Docker Mailserver
A fullstack but simple mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.). Only configuration files, no SQL database. Keep it simple and versioned. Easy to deploy and upgrade.
- Included Services
- Opening Issues and Contributing
- Requirements
- Usage
- Examples
- Environment Variables
- Release Notes
Included Services
- Postfix with SMTP or LDAP auth
- Dovecot for SASL, IMAP (or POP3), with LDAP Auth, Sieve and quotas
- Amavis
- Spamassasin supporting custom rules
- ClamAV with automatic updates
- OpenDKIM
- OpenDMARC
- Fail2ban
- Fetchmail
- Postscreen
- Postgrey
- LetsEncrypt and self-signed certificates
- Setup script to easily configure and maintain your mailserver
- Basic Sieve support using dovecot
- SASLauthd with LDAP auth
- Persistent data and state
- CI/CD
- Extension Delimiters (
you+extension@example.com
go toyou@example.com
)
Opening Issues and Contributing
Before opening an issue, read this README
carefully, use the Wiki, the Postfix/Dovecot documentation and your search engine you trust. The issue tracker is not meant to be used for unrelated questions! If you'd like to contribute, read CONTRIBUTING.md
thoroughly.
Requirements
Recommended:
- 1 Core
- 1-2GB RAM
- Swap enabled for the container
Minimum:
- 1 vCore
- 512MB RAM
Note: You'll need to deactivate some services like ClamAV to be able to run on a host with 512MB of RAM. Even with 1G RAM you may run into problems without swap, see FAQ.
Usage
Get the tools
Download the docker-compose.yml
, compose.env
, mailserver.env
and the setup.sh
files:
wget https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/setup.sh
wget https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/docker-compose.yml
wget https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/mailserver.env
wget -O .env https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/compose.env
chmod a+x ./setup.sh
Create a docker-compose environment
- Install the latest docker-compose
- Edit the files
.env
andmailserver.env
to your liking:.env
contains the configuration for Docker Composemailserver.env
contains the configuration for the mailserver container- these files supports only simple
VAR=VAL
- don't quote your values
- variable substitution is not supported (e.g.
OVERRIDE_HOSTNAME=$HOSTNAME.$DOMAINNAME
).
- Variables in
.env
are expanded in thedocker-compose.yml
file only and not in the container. The filemailserver.env
serves this case where environment variables are used in the container. - If you want to use a bare domain (host name = domain name), see FAQ
Get up and running
If you'd like to use SELinux, add -Z
to the variable SELINUX_LABEL
in .env
. If you want the volume bind mount to be shared among other containers switch -Z
to -z
docker-compose up -d mail
# without SELinux
./setup.sh email add <user@domain> [<password>]
./setup.sh alias add postmaster@<domain> <user@domain>
./setup.sh config dkim
# with SELinux
./setup.sh -Z email add <user@domain> [<password>]
./setup.sh -Z alias add postmaster@<domain> <user@domain>
./setup.sh -Z config dkim
Miscellaneous
DNS - DKIM
When keys are generated, you can configure your DNS server by just pasting the content of config/opendkim/keys/domain.tld/mail.txt
to set up DKIM.
Custom user changes & patches
If you'd like to change, patch or alter files or behavior of docker-mailserver
, you can use a script. Just place it the config/
folder that is created on startup and call it user-patches.sh
. The setup is done like this:
# 1. Either create the config/ directory yourself
# or let docker-mailserver create it on initial
# startup
/where/docker-mailserver/resides/ $ mkdir config && cd config
# 2. Create the user-patches.sh script and make it
# executable
/where/docker-mailserver/resides/config/ $ touch user-patches.sh
/where/docker-mailserver/resides/config/ $ chmod +x user-patches.sh
# 3. Edit it
/where/docker-mailserver/resides/config/ $ vi user-patches.sh
/where/docker-mailserver/resides/config/ $ cat user-patches.sh
#! /bin/bash
# ! THIS IS AN EXAMPLE !
# If you modify any supervisord configuration, make sure
# to run "supervisorctl update" afterwards.
set -euo pipefail
echo 'user-patches.sh started'
if ! grep '192.168.0.1' /etc/hosts
then
echo -e '192.168.0.1 some.domain.com' >> /etc/hosts
fi
echo 'user-patches.sh finished successfully'
And you're done. The user patches script runs right before starting daemons. That means, all the other configuration is in place, so the script can make final adjustments.
Supported Operating Systems
We are currently providing support for Linux. Windows is not supported and is known to cause problems. Similarly, macOS is not officially supported - but you may get it to work there. In the end, Linux should be your preferred operating system for this image, especially when using this mailserver in production.
Support for Multiple Domains
docker-mailserver
supports multiple domains out of the box, so you can do this:
./setup.sh email add user1@docker.example.com
./setup.sh email add user1@mail.example.de
./setup.sh email add user1@server.example.org
docker-mailserver
Updating docker-compose down
docker pull docker.io/mailserver/docker-mailserver:<VERSION TAG>
docker-compose up -d mail
You're done! And don't forget to have a look at the remaining functions of the setup.sh
script with ./setup.sh -h
.
SPF/Forwarding Problems
If you got any problems with SPF and/or forwarding mails, give SRS a try. You enable SRS by setting ENABLE_SRS=1
. See the variable description for further information.
Exposed ports
Protocol | Opt-in Encryption ¹ | Enforced Encryption | Purpose |
---|---|---|---|
SMTP | 25 | N/A | Transfer² |
ESMTP | 587 | 465³ | Submission |
POP3 | 110 | 995 | Retrieval |
IMAP4 | 143 | 993 | Retrieval |
- A connection may be secured over TLS when both ends support
STARTTLS
. On ports 110, 143 and 587,docker-mailserver
will reject a connection that cannot be secured. Port 25 is required to support insecure connections. - Receives email and filters for spam and viruses. For submitting outgoing mail you should prefer the submission ports(465, 587), which require authentication. Unless a relay host is configured, outgoing email will leave the server via port 25(thus outbound traffic must not be blocked by your provider or firewall).
- A submission port since 2018, RFC 8314. Originally a secure variant of port 25.
See the wiki for further details and best practice advice, especially regarding security concerns.
Examples
With Relevant Environmental Variables
This example provides you only with a basic example of what a minimal setup could look like. We strongly recommend that you go through the configuration file yourself and adjust everything to your needs. The default docker-compose.yml can be used for the purpose out-of-the-box, see the usage section.
version: '3.8'
services:
mail:
image: docker.io/mailserver/docker-mailserver:latest
hostname: mail # ${HOSTNAME}
domainname: domain.com # ${DOMAINNAME}
container_name: mail # ${CONTAINER_NAME}
ports:
- "25:25"
- "143:143"
- "587:587"
- "993:993"
volumes:
- maildata:/var/mail
- mailstate:/var/mail-state
- maillogs:/var/log/mail
- ./config/:/tmp/docker-mailserver/
environment:
- ENABLE_SPAMASSASSIN=1
- SPAMASSASSIN_SPAM_TO_INBOX=1
- ENABLE_CLAMAV=1
- ENABLE_FAIL2BAN=1
- ENABLE_POSTGREY=1
- ENABLE_SASLAUTHD=0
- ONE_DIR=1
- DMS_DEBUG=0
cap_add:
- NET_ADMIN
- SYS_PTRACE
restart: always
volumes:
maildata:
mailstate:
maillogs:
LDAP setup
version: '3.8'
services:
mail:
image: docker.io/mailserver/docker-mailserver:latest
hostname: mail # ${HOSTNAME}
domainname: domain.com # ${DOMAINNAME}
container_name: mail # ${CONTAINER_NAME}
ports:
- "25:25"
- "143:143"
- "587:587"
- "993:993"
volumes:
- maildata:/var/mail
- mailstate:/var/mail-state
- maillogs:/var/log/mail
- ./config/:/tmp/docker-mailserver/
environment:
- ENABLE_SPAMASSASSIN=1
- SPAMASSASSIN_SPAM_TO_INBOX=1
- ENABLE_CLAMAV=1
- ENABLE_FAIL2BAN=1
- ENABLE_POSTGREY=1
- ONE_DIR=1
- DMS_DEBUG=0
- ENABLE_LDAP=1
- LDAP_SERVER_HOST=ldap # your ldap container/IP/ServerName
- LDAP_SEARCH_BASE=ou=people,dc=localhost,dc=localdomain
- LDAP_BIND_DN=cn=admin,dc=localhost,dc=localdomain
- LDAP_BIND_PW=admin
- LDAP_QUERY_FILTER_USER=(&(mail=%s)(mailEnabled=TRUE))
- LDAP_QUERY_FILTER_GROUP=(&(mailGroupMember=%s)(mailEnabled=TRUE))
- LDAP_QUERY_FILTER_ALIAS=(|(&(mailAlias=%s)(objectClass=PostfixBookMailForward))(&(mailAlias=%s)(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE)))
- LDAP_QUERY_FILTER_DOMAIN=(|(&(mail=*@%s)(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE))(&(mailGroupMember=*@%s)(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE))(&(mailalias=*@%s)(objectClass=PostfixBookMailForward)))
- DOVECOT_PASS_FILTER=(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n))
- DOVECOT_USER_FILTER=(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n))
- ENABLE_SASLAUTHD=1
- SASLAUTHD_MECHANISMS=ldap
- SASLAUTHD_LDAP_SERVER=ldap
- SASLAUTHD_LDAP_BIND_DN=cn=admin,dc=localhost,dc=localdomain
- SASLAUTHD_LDAP_PASSWORD=admin
- SASLAUTHD_LDAP_SEARCH_BASE=ou=people,dc=localhost,dc=localdomain
- SASLAUTHD_LDAP_FILTER=(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%U))
- POSTMASTER_ADDRESS=postmaster@localhost.localdomain
- POSTFIX_MESSAGE_SIZE_LIMIT=100000000
cap_add:
- NET_ADMIN
- SYS_PTRACE
restart: always
volumes:
maildata:
mailstate:
maillogs: