From a49c42ceb4cb73c6f399ecf391a312454e15c139 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 1 Mar 2023 15:34:25 +0000 Subject: [PATCH] deploy: f3edcf9cd5f2f3f5601b2c3f09a6434fc8d477c6 --- edge/search/search_index.json | 2 +- edge/usage/index.html | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/edge/search/search_index.json b/edge/search/search_index.json index 2c4c80f2..7e849a15 100644 --- a/edge/search/search_index.json +++ b/edge/search/search_index.json @@ -1 +1 @@ -{"config":{"indexing":"full","lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"Welcome to the Documentation for docker-mailserver ! This Documentation is Versioned Make sure to select the correct version of this documentation! It should match the version of the image you are using. The default version corresponds to the :edge image tag - the most recent build, not the most recent stable release . This documentation provides you not only with the basic setup and configuration of DMS but also with advanced configuration, elaborate usage scenarios, detailed examples, hints and more. About docker-mailserver , or DMS for short, is a production-ready fullstack but simple mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.). It employs only configuration files, no SQL database. The image is focused around the slogan \"Keep it simple and versioned\". Contents Getting Started If you're completely new to mail servers or you want to read up on them, check out our Introduction page . If you're new to DMS as a mail server appliance, make sure to read the Usage chapter first. If you want to look at examples for Docker Compose, we have an Examples page . There is also a script - setup.sh - supplied with this project. It supports you in configuring and administrating your server. Information on how to get it and how to use it is available on a dedicated page . Configuration We have a dedicated configuration page . It contains most of the configuration and explanation you need to setup your mail server properly. Be aware that advanced tasks may still require reading through all parts of this documentation; it may also involve inspecting your running container for debugging purposes. After all, a mail-server is a complex arrangement of various programs. Important If you'd like to change, patch or alter files or behavior of docker-mailserver , you can use a script. Just place a script called user-patches.sh in your ./docker-data/dms/config/ folder volume (which is mounted to /tmp/docker-mailserver/ inside the container) and it will be run on container startup. See the 'Modifications via Script' page for additional documentation and an example. You might also want to check out: A list of all configuration options via ENV A list of all optional and automatically created configuration files and directories Tip Definitely check out the FAQ for more information and tips! Please do not open an issue before you have checked our documentation for answers, including the FAQ ! Tests DMS employs a variety of tests. If you want to know more about our test suite, view our testing docs . Contributing We are always happy to welcome new contributors. For guidelines and entrypoints please have a look at the Contributing section .","title":"Home"},{"location":"#welcome-to-the-documentation-for-docker-mailserver","text":"This Documentation is Versioned Make sure to select the correct version of this documentation! It should match the version of the image you are using. The default version corresponds to the :edge image tag - the most recent build, not the most recent stable release . This documentation provides you not only with the basic setup and configuration of DMS but also with advanced configuration, elaborate usage scenarios, detailed examples, hints and more.","title":"Welcome to the Documentation for docker-mailserver!"},{"location":"#about","text":"docker-mailserver , or DMS for short, is a production-ready fullstack but simple mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.). It employs only configuration files, no SQL database. The image is focused around the slogan \"Keep it simple and versioned\".","title":"About"},{"location":"#contents","text":"","title":"Contents"},{"location":"#getting-started","text":"If you're completely new to mail servers or you want to read up on them, check out our Introduction page . If you're new to DMS as a mail server appliance, make sure to read the Usage chapter first. If you want to look at examples for Docker Compose, we have an Examples page . There is also a script - setup.sh - supplied with this project. It supports you in configuring and administrating your server. Information on how to get it and how to use it is available on a dedicated page .","title":"Getting Started"},{"location":"#configuration","text":"We have a dedicated configuration page . It contains most of the configuration and explanation you need to setup your mail server properly. Be aware that advanced tasks may still require reading through all parts of this documentation; it may also involve inspecting your running container for debugging purposes. After all, a mail-server is a complex arrangement of various programs. Important If you'd like to change, patch or alter files or behavior of docker-mailserver , you can use a script. Just place a script called user-patches.sh in your ./docker-data/dms/config/ folder volume (which is mounted to /tmp/docker-mailserver/ inside the container) and it will be run on container startup. See the 'Modifications via Script' page for additional documentation and an example. You might also want to check out: A list of all configuration options via ENV A list of all optional and automatically created configuration files and directories Tip Definitely check out the FAQ for more information and tips! Please do not open an issue before you have checked our documentation for answers, including the FAQ !","title":"Configuration"},{"location":"#tests","text":"DMS employs a variety of tests. If you want to know more about our test suite, view our testing docs .","title":"Tests"},{"location":"#contributing","text":"We are always happy to welcome new contributors. For guidelines and entrypoints please have a look at the Contributing section .","title":"Contributing"},{"location":"faq/","text":"What kind of database are you using? None! No database is required. The filesystem is the database. This image is based on config files that can be persisted using bind mounts (default) or Docker volumes, and as such versioned, backed up and so forth. Where are emails stored? Mails are stored in /var/mail/${domain}/${username} . Since v9.0.0 it is possible to add custom user_attributes for each accounts to have a different mailbox configuration (See #1792 ). How are IMAP mailboxes ( aka IMAP Folders ) set up? INBOX is setup by default with the special IMAP folders Drafts , Sent , Junk and Trash . You can learn how to modify or add your own folders ( including additional special folders like Archive ) by visiting our docs page Customizing IMAP Folders for more information. How do I update DMS? Make sure to read the CHANGELOG before updating to new versions, to be prepared for possible breaking changes. Then, run the following commands: docker-compose pull docker-compose down docker-compose up -d You should see the new version number on startup, for example: [ INF ] Welcome to docker-mailserver 11.3.1 . And you're done! Don't forget to have a look at the remaining functions of the setup.sh script with ./setup.sh help . Which operating systems are supported? Linux is officially supported. Windows and macOS are not supported and users and have reported various issues running the image on these hosts. As you'll realistically be deploying to production on a Linux host, if you are on Windows or macOS and want to run the image locally first, it's advised to do so via a VM guest running Linux if you have issues running DMS on your host system. What are the system requirements? Recommended 1 vCore 2GB RAM Swap enabled for the container Minimum 1 vCore 512MB RAM You'll need to avoid running some services like ClamAV ( disabled by default ) to be able to run on a host with 512MB of RAM. Warning ClamAV can consume a lot of memory, as it reads the entire signature database into RAM. Current figure is about 850M and growing. If you get errors about ClamAV or amavis failing to allocate memory you need more RAM or more swap and of course docker must be allowed to use swap (not always the case). If you can't use swap at all you may need 3G RAM. How to alter a running DMS instance without relaunching the container? docker-mailserver aggregates multiple \"sub-services\", such as Postfix, Dovecot, Fail2ban, SpamAssassin, etc. In many cases, one may edit a sub-service's config and reload that very sub-service, without stopping and relaunching the whole mail-server. In order to do so, you'll probably want to push your config updates to your server through a Docker volume (these docs use: ./docker-data/dms/config/:/tmp/docker-mailserver/ ), then restart the sub-service to apply your changes, using supervisorctl . For instance, after editing fail2ban's config: supervisorctl restart fail2ban . See the documentation for supervisorctl . Tip To add, update or delete an email account; there is no need to restart postfix / dovecot service inside the container after using setup.sh script. For more information, see #1639 . How can I sync the container and host date/time? Share the host's /etc/localtime with the container, e.g. by using a bind mount: volumes : - /etc/localtime:/etc/localtime:ro Optionally, you can set the TZ ENV variable; e.g. TZ=Europe/Berlin . Check this list for which values are allowed. What is the file format? All files are using the Unix format with LF line endings. Please do not use CRLF . Do you support multiple domains? DMS supports multiple domains out of the box, so you can do this: ./setup.sh email add user1@example.com ./setup.sh email add user1@example.de ./setup.sh email add user1@server.example.org What about backups? Bind mounts (default) From the location of your docker-compose.yml , create a compressed archive of your docker-data/dms/config/ and docker-data/dms/mail-* folders: tar --gzip -cf \"backup- $( date +%F ) .tar.gz\" ./docker-data/dms Then to restore docker-data/dms/config/ and docker-data/dms/mail-* folders from your backup file: tar --gzip -xf backup-date.tar.gz Volumes Assuming that you use docker-compose and data volumes, you can backup the configuration, emails and logs like this: # create backup docker run --rm -it \\ -v \" ${ PWD } /docker-data/dms/config/:/tmp/docker-mailserver/\" \\ -v \" ${ PWD } /docker-data/dms-backups/:/backup/\" \\ --volumes-from mailserver \\ alpine:latest \\ tar czf \"/backup/mail- $( date +%F ) .tar.gz\" /var/mail /var/mail-state /var/log/mail /tmp/docker-mailserver # delete backups older than 30 days find \" ${ PWD } /docker-data/dms-backups/\" -type f -mtime +30 -delete What about the ./docker-data/dms/mail-state folder? When you run DMS with the ENV variable ONE_DIR=1 (default), this folder will: Provide support to persist Fail2Ban blocks, ClamAV signature updates, and the like when the container is restarted or recreated. To persist that container state properly this folder should be volume mounted to /var/mail-state/ internally . Service data is relocated to the mail-state folder for the following services: Postfix, Dovecot, Fail2Ban, Amavis, PostGrey, ClamAV, SpamAssassin. I Want to Know More About the Ports See this part of the documentation for further details and best practice advice, especially regarding security concerns . How can I configure my email client? Login is full email address ( @ ). # IMAP username : password : server : imap port : 143 or 993 with STARTTLS/SSL (recommended) imap path prefix : INBOX # SMTP smtp port : 25 or 587/465 with STARTTLS/SSL (recommended) username : password : DMS is properly configured for port 587, if possible, we recommend using port 465 for SMTP though. See this section to learn more about ports . Can I use a naked/bare domain (i.e. no hostname)? Yes, but not without some configuration changes. Normally it is assumed that DMS runs on a host with a name, so the fully qualified host name might be mail.example.com with the domain example.com . The MX records point to mail.example.com . To use a bare domain ( where the host name is example.com and the domain is also example.com ), change mydestination : From: mydestination = $myhostname, localhost.$mydomain, localhost To: mydestination = localhost.$mydomain, localhost Add the latter line to docker-data/dms/config/postfix-main.cf . If that doesn't work, make sure that OVERRIDE_HOSTNAME is blank in your mailserver.env file (see #1731 ). Without these changes there will be warnings in the logs like: warning: do not list domain example.com in BOTH mydestination and virtual_mailbox_domains Plus of course mail delivery fails. Also you need to define hostname: example.com in your docker-compose.yml and don't sepecify the domainname: at all. How can I configure a catch-all? Considering you want to redirect all incoming e-mails for the domain example.com to user1@example.com , add the following line to docker-data/dms/config/postfix-virtual.cf : @example.com user1@example.com How can I delete all the emails for a specific user? First of all, create a special alias named devnull by editing docker-data/dms/config/postfix-aliases.cf : devnull: /dev/null Considering you want to delete all the e-mails received for baduser@example.com , add the following line to docker-data/dms/config/postfix-virtual.cf : baduser@example.com devnull Important If you use a catch-all rule for the main/sub domain, you need another entry in docker-data/dms/config/postfix-virtual.cf : @mail.example.com hello@example.com baduser@example.com devnull devnull@mail.example.com devnull What kind of SSL certificates can I use? Both RSA and ECDSA certs are supported. You can provide your own cert files manually, or mount a letsencrypt generated directory ( with alternative support for Traefik's acme.json ). Check out the SSL_TYPE documentation for more details. I just moved from my old mail server to DMS, but \"it doesn't work\"? If this migration implies a DNS modification, be sure to wait for DNS propagation before opening an issue. Few examples of symptoms can be found here or here . This could be related to a modification of your MX record, or the IP mapped to mail.example.com . Additionally, validate your DNS configuration . If everything is OK regarding DNS, please provide formatted logs and config files. This will allow us to help you. If we're blind, we won't be able to do anything. Can DMS run in a Rancher environment? Yes, by adding the environment variable PERMIT_DOCKER: network . Warning Adding the Docker network's gateway to the list of trusted hosts, e.g. using the network or connected-networks option, can create an open relay , for instance if IPv6 is enabled on the host machine but not in Docker . How can I authenticate users with SMTP_ONLY=1 ? See #1247 for an example. Todo Write a How-to / Use-Case / Tutorial about authentication with SMTP_ONLY . Common Errors warning: connect to Milter service inet:localhost:8893: Connection refused # DMARC not running # = > /etc/init.d/opendmarc restart warning: connect to Milter service inet:localhost:8891: Connection refused # DKIM not running # = > /etc/init.d/opendkim restart mail amavis[1459]: (01459-01) (!)connect to /var/run/clamav/clamd.ctl failed, attempt #1: Can't connect to a UNIX socket /var/run/clamav/clamd.ctl: No such file or directory mail amavis[1459]: (01459-01) (!)ClamAV-clamd: All attempts (1) failed connecting to /var/run/clamav/clamd.ctl, retrying (2) mail amavis[1459]: (01459-01) (!)ClamAV-clamscan av-scanner FAILED: /usr/bin/clamscan KILLED, signal 9 (0009) at (eval 100) line 905. mail amavis[1459]: (01459-01) (!!)AV: ALL VIRUS SCANNERS FAILED # Clamav is not running ( not started or because you don ' t have enough memory ) # = > check requirements and/or start Clamav How to use DMS behind a proxy Using user-patches.sh , update the container file /etc/postfix/main.cf to include: proxy_interfaces = X.X.X.X (your public IP) How to adjust settings with the user-patches.sh script Suppose you want to change a number of settings that are not listed as variables or add things to the server that are not included? docker-mailserver has a built-in way to do post-install processes. If you place a script called user-patches.sh in the config directory it will be run after all configuration files are set up, but before the postfix, amavis and other daemons are started. It is common to use a local directory for config added to docker-mailsever via a volume mount in your docker-compose.yml (eg: ./docker-data/dms/config/:/tmp/docker-mailserver/ ). Add or create the script file to your config directory: cd ./docker-data/dms/config touch user-patches.sh chmod +x user-patches.sh Then fill user-patches.sh with suitable code. If you want to test it you can move into the running container, run it and see if it does what you want. For instance: # start shell in container ./setup.sh debug login # check the file cat /tmp/docker-mailserver/user-patches.sh # run the script /tmp/docker-mailserver/user-patches.sh # exit the container shell back to the host shell exit You can do a lot of things with such a script. You can find an example user-patches.sh script here: example user-patches.sh script . We also have a very similar docs page specifically about this feature! Special use-case - patching the supervisord configuration It seems worth noting, that the user-patches.sh gets executed through supervisord . If you need to patch some supervisord config (e.g. /etc/supervisor/conf.d/saslauth.conf ), the patching happens too late. An easy workaround is to make the user-patches.sh reload the supervisord config after patching it: #!/bin/bash sed -i 's/rimap -r/rimap/' /etc/supervisor/conf.d/saslauth.conf supervisorctl update How to ban custom IP addresses with Fail2ban Use the following command: ./setup.sh fail2ban ban The default bantime is 180 days. This value can be customized . What to do in case of 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. SpamAssasin How can I manage my custom SpamAssassin rules? Antispam rules are managed in docker-data/dms/config/spamassassin-rules.cf . What are acceptable SA_SPAM_SUBJECT values? For no subject set SA_SPAM_SUBJECT=undef . For a trailing white-space subject one can define the whole variable with quotes in docker-compose.yml : environment : - \"SA_SPAM_SUBJECT=[SPAM] \" Why are SpamAssassin x-headers not inserted into my subdomain.example.com subdomain emails? In the default setup, amavis only applies SpamAssassin x-headers into domains matching the template listed in the config file ( 05-domain_id in the amavis defaults). The default setup @local_domains_acl = ( \".$mydomain\" ); does not match subdomains. To match subdomains, you can override the @local_domains_acl directive in the amavis user config file 50-user with @local_domains_maps = (\".\"); to match any sort of domain template. How can I make SpamAssassin better recognize spam? Put received spams in .Junk/ imap folder using SPAMASSASSIN_SPAM_TO_INBOX=1 and MOVE_SPAM_TO_JUNK=1 and add a user cron like the following: # This assumes you're having `environment: ONE_DIR=1` in the `mailserver.env`, # with a consolidated config in `/var/mail-state` # # m h dom mon dow command # Everyday 2:00AM, learn spam from a specific user 0 2 * * * docker exec mailserver sa-learn --spam /var/mail/example.com/username/.Junk --dbpath /var/mail-state/lib-amavis/.spamassassin With docker-compose you can more easily use the internal instance of cron within docker-mailserver . This is less problematic than the simple solution shown above, because it decouples the learning from the host on which docker-mailserver is running, and avoids errors if the mail-server is not running. The following configuration works nicely: Example Create a system cron file: # in the docker-compose.yml root directory mkdir -p ./docker-data/dms/cron touch ./docker-data/dms/cron/sa-learn chown root:root ./docker-data/dms/cron/sa-learn chmod 0644 ./docker-data/dms/cron/sa-learn Edit the system cron file nano ./docker-data/dms/cron/sa-learn , and set an appropriate configuration: # This assumes you're having `environment: ONE_DIR=1` in the env-mailserver, # with a consolidated config in `/var/mail-state` # # '> /dev/null' to send error notifications from 'stderr' to 'postmaster@example.com' # # m h dom mon dow user command # # Everyday 2:00AM, learn spam from a specific user # spam: junk directory 0 2 * * * root sa-learn --spam /var/mail/example.com/username/.Junk --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # ham: archive directories 15 2 * * * root sa-learn --ham /var/mail/example.com/username/.Archive* --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # ham: inbox subdirectories 30 2 * * * root sa-learn --ham /var/mail/example.com/username/cur* --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # # Everyday 3:00AM, learn spam from all users of a domain # spam: junk directory 0 3 * * * root sa-learn --spam /var/mail/not-example.com/*/.Junk --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # ham: archive directories 15 3 * * * root sa-learn --ham /var/mail/not-example.com/*/.Archive* --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # ham: inbox subdirectories 30 3 * * * root sa-learn --ham /var/mail/not-example.com/*/cur* --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null Then with docker-compose.yml : services : mailserver : image : docker.io/mailserver/docker-mailserver:latest volumes : - ./docker-data/dms/cron/sa-learn:/etc/cron.d/sa-learn Or with Docker Swarm : version : '3.8' services : mailserver : image : docker.io/mailserver/docker-mailserver:latest # ... configs : - source : my_sa_crontab target : /etc/cron.d/sa-learn configs : my_sa_crontab : file : ./docker-data/dms/cron/sa-learn With the default settings, SpamAssassin will require 200 mails trained for spam (for example with the method explained above) and 200 mails trained for ham (using the same command as above but using --ham and providing it with some ham mails). Until you provided these 200+200 mails, SpamAssassin will not take the learned mails into account. For further reference, see the SpamAssassin Wiki . How do I have more control about what SpamAssassin is filtering? By default, SPAM and INFECTED emails are put to a quarantine which is not very straight forward to access. Several config settings are affecting this behavior: First, make sure you have the proper thresholds set: SA_TAG = -100000.0 SA_TAG2 = 3.75 SA_KILL = 100000.0 The very negative vaue in SA_TAG makes sure, that all emails have the SpamAssassin headers included. SA_TAG2 is the actual threshold to set the YES/NO flag for spam detection. SA_KILL needs to be very high, to make sure nothing is bounced at all ( SA_KILL superseeds SPAMASSASSIN_SPAM_TO_INBOX ) Make sure everything (including SPAM) is delivered to the inbox and not quarantined: SPAMASSASSIN_SPAM_TO_INBOX = 1 Use MOVE_SPAM_TO_JUNK=1 or create a sieve script which puts spam to the Junk folder: require [ \"comparator-i;ascii-numeric\" , \"relational\" , \"fileinto\" ]; if header :contains \"X-Spam-Flag\" \"YES\" { fileinto \"Junk\" ; } elsif allof ( not header :matches \"x-spam-score\" \"-*\" , header :value \"ge\" :comparator \"i;ascii-numeric\" \"x-spam-score\" \"3.75\" ) { fileinto \"Junk\" ; } Create a dedicated mailbox for emails which are infected/bad header and everything amavis is blocking by default and put its address into docker-data/dms/config/amavis.cf $clean_quarantine_to = \"amavis\\@example.com\"; $virus_quarantine_to = \"amavis\\@example.com\"; $banned_quarantine_to = \"amavis\\@example.com\"; $bad_header_quarantine_to = \"amavis\\@example.com\"; $spam_quarantine_to = \"amavis\\@example.com\";","title":"FAQ"},{"location":"faq/#what-kind-of-database-are-you-using","text":"None! No database is required. The filesystem is the database. This image is based on config files that can be persisted using bind mounts (default) or Docker volumes, and as such versioned, backed up and so forth.","title":"What kind of database are you using?"},{"location":"faq/#where-are-emails-stored","text":"Mails are stored in /var/mail/${domain}/${username} . Since v9.0.0 it is possible to add custom user_attributes for each accounts to have a different mailbox configuration (See #1792 ).","title":"Where are emails stored?"},{"location":"faq/#how-are-imap-mailboxes-aka-imap-folders-set-up","text":"INBOX is setup by default with the special IMAP folders Drafts , Sent , Junk and Trash . You can learn how to modify or add your own folders ( including additional special folders like Archive ) by visiting our docs page Customizing IMAP Folders for more information.","title":"How are IMAP mailboxes (aka IMAP Folders) set up?"},{"location":"faq/#how-do-i-update-dms","text":"Make sure to read the CHANGELOG before updating to new versions, to be prepared for possible breaking changes. Then, run the following commands: docker-compose pull docker-compose down docker-compose up -d You should see the new version number on startup, for example: [ INF ] Welcome to docker-mailserver 11.3.1 . And you're done! Don't forget to have a look at the remaining functions of the setup.sh script with ./setup.sh help .","title":"How do I update DMS?"},{"location":"faq/#which-operating-systems-are-supported","text":"Linux is officially supported. Windows and macOS are not supported and users and have reported various issues running the image on these hosts. As you'll realistically be deploying to production on a Linux host, if you are on Windows or macOS and want to run the image locally first, it's advised to do so via a VM guest running Linux if you have issues running DMS on your host system.","title":"Which operating systems are supported?"},{"location":"faq/#what-are-the-system-requirements","text":"","title":"What are the system requirements?"},{"location":"faq/#recommended","text":"1 vCore 2GB RAM Swap enabled for the container","title":"Recommended"},{"location":"faq/#minimum","text":"1 vCore 512MB RAM You'll need to avoid running some services like ClamAV ( disabled by default ) to be able to run on a host with 512MB of RAM. Warning ClamAV can consume a lot of memory, as it reads the entire signature database into RAM. Current figure is about 850M and growing. If you get errors about ClamAV or amavis failing to allocate memory you need more RAM or more swap and of course docker must be allowed to use swap (not always the case). If you can't use swap at all you may need 3G RAM.","title":"Minimum"},{"location":"faq/#how-to-alter-a-running-dms-instance-without-relaunching-the-container","text":"docker-mailserver aggregates multiple \"sub-services\", such as Postfix, Dovecot, Fail2ban, SpamAssassin, etc. In many cases, one may edit a sub-service's config and reload that very sub-service, without stopping and relaunching the whole mail-server. In order to do so, you'll probably want to push your config updates to your server through a Docker volume (these docs use: ./docker-data/dms/config/:/tmp/docker-mailserver/ ), then restart the sub-service to apply your changes, using supervisorctl . For instance, after editing fail2ban's config: supervisorctl restart fail2ban . See the documentation for supervisorctl . Tip To add, update or delete an email account; there is no need to restart postfix / dovecot service inside the container after using setup.sh script. For more information, see #1639 .","title":"How to alter a running DMS instance without relaunching the container?"},{"location":"faq/#how-can-i-sync-the-container-and-host-datetime","text":"Share the host's /etc/localtime with the container, e.g. by using a bind mount: volumes : - /etc/localtime:/etc/localtime:ro Optionally, you can set the TZ ENV variable; e.g. TZ=Europe/Berlin . Check this list for which values are allowed.","title":"How can I sync the container and host date/time?"},{"location":"faq/#what-is-the-file-format","text":"All files are using the Unix format with LF line endings. Please do not use CRLF .","title":"What is the file format?"},{"location":"faq/#do-you-support-multiple-domains","text":"DMS supports multiple domains out of the box, so you can do this: ./setup.sh email add user1@example.com ./setup.sh email add user1@example.de ./setup.sh email add user1@server.example.org","title":"Do you support multiple domains?"},{"location":"faq/#what-about-backups","text":"","title":"What about backups?"},{"location":"faq/#bind-mounts-default","text":"From the location of your docker-compose.yml , create a compressed archive of your docker-data/dms/config/ and docker-data/dms/mail-* folders: tar --gzip -cf \"backup- $( date +%F ) .tar.gz\" ./docker-data/dms Then to restore docker-data/dms/config/ and docker-data/dms/mail-* folders from your backup file: tar --gzip -xf backup-date.tar.gz","title":"Bind mounts (default)"},{"location":"faq/#volumes","text":"Assuming that you use docker-compose and data volumes, you can backup the configuration, emails and logs like this: # create backup docker run --rm -it \\ -v \" ${ PWD } /docker-data/dms/config/:/tmp/docker-mailserver/\" \\ -v \" ${ PWD } /docker-data/dms-backups/:/backup/\" \\ --volumes-from mailserver \\ alpine:latest \\ tar czf \"/backup/mail- $( date +%F ) .tar.gz\" /var/mail /var/mail-state /var/log/mail /tmp/docker-mailserver # delete backups older than 30 days find \" ${ PWD } /docker-data/dms-backups/\" -type f -mtime +30 -delete","title":"Volumes"},{"location":"faq/#what-about-the-docker-datadmsmail-state-folder","text":"When you run DMS with the ENV variable ONE_DIR=1 (default), this folder will: Provide support to persist Fail2Ban blocks, ClamAV signature updates, and the like when the container is restarted or recreated. To persist that container state properly this folder should be volume mounted to /var/mail-state/ internally . Service data is relocated to the mail-state folder for the following services: Postfix, Dovecot, Fail2Ban, Amavis, PostGrey, ClamAV, SpamAssassin.","title":"What about the ./docker-data/dms/mail-state folder?"},{"location":"faq/#i-want-to-know-more-about-the-ports","text":"See this part of the documentation for further details and best practice advice, especially regarding security concerns .","title":"I Want to Know More About the Ports"},{"location":"faq/#how-can-i-configure-my-email-client","text":"Login is full email address ( @ ). # IMAP username : password : server : imap port : 143 or 993 with STARTTLS/SSL (recommended) imap path prefix : INBOX # SMTP smtp port : 25 or 587/465 with STARTTLS/SSL (recommended) username : password : DMS is properly configured for port 587, if possible, we recommend using port 465 for SMTP though. See this section to learn more about ports .","title":"How can I configure my email client?"},{"location":"faq/#can-i-use-a-nakedbare-domain-ie-no-hostname","text":"Yes, but not without some configuration changes. Normally it is assumed that DMS runs on a host with a name, so the fully qualified host name might be mail.example.com with the domain example.com . The MX records point to mail.example.com . To use a bare domain ( where the host name is example.com and the domain is also example.com ), change mydestination : From: mydestination = $myhostname, localhost.$mydomain, localhost To: mydestination = localhost.$mydomain, localhost Add the latter line to docker-data/dms/config/postfix-main.cf . If that doesn't work, make sure that OVERRIDE_HOSTNAME is blank in your mailserver.env file (see #1731 ). Without these changes there will be warnings in the logs like: warning: do not list domain example.com in BOTH mydestination and virtual_mailbox_domains Plus of course mail delivery fails. Also you need to define hostname: example.com in your docker-compose.yml and don't sepecify the domainname: at all.","title":"Can I use a naked/bare domain (i.e. no hostname)?"},{"location":"faq/#how-can-i-configure-a-catch-all","text":"Considering you want to redirect all incoming e-mails for the domain example.com to user1@example.com , add the following line to docker-data/dms/config/postfix-virtual.cf : @example.com user1@example.com","title":"How can I configure a catch-all?"},{"location":"faq/#how-can-i-delete-all-the-emails-for-a-specific-user","text":"First of all, create a special alias named devnull by editing docker-data/dms/config/postfix-aliases.cf : devnull: /dev/null Considering you want to delete all the e-mails received for baduser@example.com , add the following line to docker-data/dms/config/postfix-virtual.cf : baduser@example.com devnull Important If you use a catch-all rule for the main/sub domain, you need another entry in docker-data/dms/config/postfix-virtual.cf : @mail.example.com hello@example.com baduser@example.com devnull devnull@mail.example.com devnull","title":"How can I delete all the emails for a specific user?"},{"location":"faq/#what-kind-of-ssl-certificates-can-i-use","text":"Both RSA and ECDSA certs are supported. You can provide your own cert files manually, or mount a letsencrypt generated directory ( with alternative support for Traefik's acme.json ). Check out the SSL_TYPE documentation for more details.","title":"What kind of SSL certificates can I use?"},{"location":"faq/#i-just-moved-from-my-old-mail-server-to-dms-but-it-doesnt-work","text":"If this migration implies a DNS modification, be sure to wait for DNS propagation before opening an issue. Few examples of symptoms can be found here or here . This could be related to a modification of your MX record, or the IP mapped to mail.example.com . Additionally, validate your DNS configuration . If everything is OK regarding DNS, please provide formatted logs and config files. This will allow us to help you. If we're blind, we won't be able to do anything.","title":"I just moved from my old mail server to DMS, but \"it doesn't work\"?"},{"location":"faq/#can-dms-run-in-a-rancher-environment","text":"Yes, by adding the environment variable PERMIT_DOCKER: network . Warning Adding the Docker network's gateway to the list of trusted hosts, e.g. using the network or connected-networks option, can create an open relay , for instance if IPv6 is enabled on the host machine but not in Docker .","title":"Can DMS run in a Rancher environment?"},{"location":"faq/#how-can-i-authenticate-users-with-smtp_only1","text":"See #1247 for an example. Todo Write a How-to / Use-Case / Tutorial about authentication with SMTP_ONLY .","title":"How can I authenticate users with SMTP_ONLY=1?"},{"location":"faq/#common-errors","text":"warning: connect to Milter service inet:localhost:8893: Connection refused # DMARC not running # = > /etc/init.d/opendmarc restart warning: connect to Milter service inet:localhost:8891: Connection refused # DKIM not running # = > /etc/init.d/opendkim restart mail amavis[1459]: (01459-01) (!)connect to /var/run/clamav/clamd.ctl failed, attempt #1: Can't connect to a UNIX socket /var/run/clamav/clamd.ctl: No such file or directory mail amavis[1459]: (01459-01) (!)ClamAV-clamd: All attempts (1) failed connecting to /var/run/clamav/clamd.ctl, retrying (2) mail amavis[1459]: (01459-01) (!)ClamAV-clamscan av-scanner FAILED: /usr/bin/clamscan KILLED, signal 9 (0009) at (eval 100) line 905. mail amavis[1459]: (01459-01) (!!)AV: ALL VIRUS SCANNERS FAILED # Clamav is not running ( not started or because you don ' t have enough memory ) # = > check requirements and/or start Clamav","title":"Common Errors"},{"location":"faq/#how-to-use-dms-behind-a-proxy","text":"Using user-patches.sh , update the container file /etc/postfix/main.cf to include: proxy_interfaces = X.X.X.X (your public IP)","title":"How to use DMS behind a proxy"},{"location":"faq/#how-to-adjust-settings-with-the-user-patchessh-script","text":"Suppose you want to change a number of settings that are not listed as variables or add things to the server that are not included? docker-mailserver has a built-in way to do post-install processes. If you place a script called user-patches.sh in the config directory it will be run after all configuration files are set up, but before the postfix, amavis and other daemons are started. It is common to use a local directory for config added to docker-mailsever via a volume mount in your docker-compose.yml (eg: ./docker-data/dms/config/:/tmp/docker-mailserver/ ). Add or create the script file to your config directory: cd ./docker-data/dms/config touch user-patches.sh chmod +x user-patches.sh Then fill user-patches.sh with suitable code. If you want to test it you can move into the running container, run it and see if it does what you want. For instance: # start shell in container ./setup.sh debug login # check the file cat /tmp/docker-mailserver/user-patches.sh # run the script /tmp/docker-mailserver/user-patches.sh # exit the container shell back to the host shell exit You can do a lot of things with such a script. You can find an example user-patches.sh script here: example user-patches.sh script . We also have a very similar docs page specifically about this feature! Special use-case - patching the supervisord configuration It seems worth noting, that the user-patches.sh gets executed through supervisord . If you need to patch some supervisord config (e.g. /etc/supervisor/conf.d/saslauth.conf ), the patching happens too late. An easy workaround is to make the user-patches.sh reload the supervisord config after patching it: #!/bin/bash sed -i 's/rimap -r/rimap/' /etc/supervisor/conf.d/saslauth.conf supervisorctl update","title":"How to adjust settings with the user-patches.sh script"},{"location":"faq/#how-to-ban-custom-ip-addresses-with-fail2ban","text":"Use the following command: ./setup.sh fail2ban ban The default bantime is 180 days. This value can be customized .","title":"How to ban custom IP addresses with Fail2ban"},{"location":"faq/#what-to-do-in-case-of-spfforwarding-problems","text":"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.","title":"What to do in case of SPF/Forwarding problems"},{"location":"faq/#spamassasin","text":"","title":"SpamAssasin"},{"location":"faq/#how-can-i-manage-my-custom-spamassassin-rules","text":"Antispam rules are managed in docker-data/dms/config/spamassassin-rules.cf .","title":"How can I manage my custom SpamAssassin rules?"},{"location":"faq/#what-are-acceptable-sa_spam_subject-values","text":"For no subject set SA_SPAM_SUBJECT=undef . For a trailing white-space subject one can define the whole variable with quotes in docker-compose.yml : environment : - \"SA_SPAM_SUBJECT=[SPAM] \"","title":"What are acceptable SA_SPAM_SUBJECT values?"},{"location":"faq/#why-are-spamassassin-x-headers-not-inserted-into-my-subdomainexamplecom-subdomain-emails","text":"In the default setup, amavis only applies SpamAssassin x-headers into domains matching the template listed in the config file ( 05-domain_id in the amavis defaults). The default setup @local_domains_acl = ( \".$mydomain\" ); does not match subdomains. To match subdomains, you can override the @local_domains_acl directive in the amavis user config file 50-user with @local_domains_maps = (\".\"); to match any sort of domain template.","title":"Why are SpamAssassin x-headers not inserted into my subdomain.example.com subdomain emails?"},{"location":"faq/#how-can-i-make-spamassassin-better-recognize-spam","text":"Put received spams in .Junk/ imap folder using SPAMASSASSIN_SPAM_TO_INBOX=1 and MOVE_SPAM_TO_JUNK=1 and add a user cron like the following: # This assumes you're having `environment: ONE_DIR=1` in the `mailserver.env`, # with a consolidated config in `/var/mail-state` # # m h dom mon dow command # Everyday 2:00AM, learn spam from a specific user 0 2 * * * docker exec mailserver sa-learn --spam /var/mail/example.com/username/.Junk --dbpath /var/mail-state/lib-amavis/.spamassassin With docker-compose you can more easily use the internal instance of cron within docker-mailserver . This is less problematic than the simple solution shown above, because it decouples the learning from the host on which docker-mailserver is running, and avoids errors if the mail-server is not running. The following configuration works nicely: Example Create a system cron file: # in the docker-compose.yml root directory mkdir -p ./docker-data/dms/cron touch ./docker-data/dms/cron/sa-learn chown root:root ./docker-data/dms/cron/sa-learn chmod 0644 ./docker-data/dms/cron/sa-learn Edit the system cron file nano ./docker-data/dms/cron/sa-learn , and set an appropriate configuration: # This assumes you're having `environment: ONE_DIR=1` in the env-mailserver, # with a consolidated config in `/var/mail-state` # # '> /dev/null' to send error notifications from 'stderr' to 'postmaster@example.com' # # m h dom mon dow user command # # Everyday 2:00AM, learn spam from a specific user # spam: junk directory 0 2 * * * root sa-learn --spam /var/mail/example.com/username/.Junk --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # ham: archive directories 15 2 * * * root sa-learn --ham /var/mail/example.com/username/.Archive* --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # ham: inbox subdirectories 30 2 * * * root sa-learn --ham /var/mail/example.com/username/cur* --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # # Everyday 3:00AM, learn spam from all users of a domain # spam: junk directory 0 3 * * * root sa-learn --spam /var/mail/not-example.com/*/.Junk --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # ham: archive directories 15 3 * * * root sa-learn --ham /var/mail/not-example.com/*/.Archive* --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # ham: inbox subdirectories 30 3 * * * root sa-learn --ham /var/mail/not-example.com/*/cur* --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null Then with docker-compose.yml : services : mailserver : image : docker.io/mailserver/docker-mailserver:latest volumes : - ./docker-data/dms/cron/sa-learn:/etc/cron.d/sa-learn Or with Docker Swarm : version : '3.8' services : mailserver : image : docker.io/mailserver/docker-mailserver:latest # ... configs : - source : my_sa_crontab target : /etc/cron.d/sa-learn configs : my_sa_crontab : file : ./docker-data/dms/cron/sa-learn With the default settings, SpamAssassin will require 200 mails trained for spam (for example with the method explained above) and 200 mails trained for ham (using the same command as above but using --ham and providing it with some ham mails). Until you provided these 200+200 mails, SpamAssassin will not take the learned mails into account. For further reference, see the SpamAssassin Wiki .","title":"How can I make SpamAssassin better recognize spam?"},{"location":"faq/#how-do-i-have-more-control-about-what-spamassassin-is-filtering","text":"By default, SPAM and INFECTED emails are put to a quarantine which is not very straight forward to access. Several config settings are affecting this behavior: First, make sure you have the proper thresholds set: SA_TAG = -100000.0 SA_TAG2 = 3.75 SA_KILL = 100000.0 The very negative vaue in SA_TAG makes sure, that all emails have the SpamAssassin headers included. SA_TAG2 is the actual threshold to set the YES/NO flag for spam detection. SA_KILL needs to be very high, to make sure nothing is bounced at all ( SA_KILL superseeds SPAMASSASSIN_SPAM_TO_INBOX ) Make sure everything (including SPAM) is delivered to the inbox and not quarantined: SPAMASSASSIN_SPAM_TO_INBOX = 1 Use MOVE_SPAM_TO_JUNK=1 or create a sieve script which puts spam to the Junk folder: require [ \"comparator-i;ascii-numeric\" , \"relational\" , \"fileinto\" ]; if header :contains \"X-Spam-Flag\" \"YES\" { fileinto \"Junk\" ; } elsif allof ( not header :matches \"x-spam-score\" \"-*\" , header :value \"ge\" :comparator \"i;ascii-numeric\" \"x-spam-score\" \"3.75\" ) { fileinto \"Junk\" ; } Create a dedicated mailbox for emails which are infected/bad header and everything amavis is blocking by default and put its address into docker-data/dms/config/amavis.cf $clean_quarantine_to = \"amavis\\@example.com\"; $virus_quarantine_to = \"amavis\\@example.com\"; $banned_quarantine_to = \"amavis\\@example.com\"; $bad_header_quarantine_to = \"amavis\\@example.com\"; $spam_quarantine_to = \"amavis\\@example.com\";","title":"How do I have more control about what SpamAssassin is filtering?"},{"location":"introduction/","text":"What is a mail-server, and how does it perform its duty? Here's an introduction to the field that covers everything you need to know to get started with docker-mailserver . Anatomy of a Mail-Server A mail-server is only a part of a client-server relationship aimed at exchanging information in the form of emails . Exchanging emails requires using specific means (programs and protocols). docker-mailserver provides you with the server portion, whereas the client can be anything from a terminal via text-based software (eg. Mutt ) to a fully-fledged desktop application (eg. Mozilla Thunderbird , Microsoft Outlook \u2026), to a web interface, etc. Unlike the client-side where usually a single program is used to perform retrieval and viewing of emails, the server-side is composed of many specialized components. The mail-server is capable of accepting, forwarding, delivering, storing and overall exchanging messages, but each one of those tasks is actually handled by a specific piece of software. All of these \"agents\" must be integrated with one another for the exchange to take place. docker-mailserver has made informed choices about those components and their (default) configuration. It offers a comprehensive platform to run a fully featured mail-server in no time! Components The following components are required to create a complete delivery chain : MUA: a Mail User Agent is basically any client/program capable of sending emails to a mail-server; while also capable of fetching emails from a mail-server for presenting them to the end users. MTA: a Mail Transfer Agent is the so-called \"mail-server\" as seen from the MUA's perspective. It's a piece of software dedicated to accepting submitted emails, then forwarding them-where exactly will depend on an email's final destination. If the receiving MTA is responsible for the FQDN the email is sent to, then an MTA is to forward that email to an MDA (see below). Otherwise, it is to transfer (ie. forward, relay) to another MTA, \"closer\" to the email's final destination. MDA: a Mail Delivery Agent is responsible for accepting emails from an MTA and dropping them into their recipients' mailboxes, whichever the form. Here's a schematic view of mail delivery: Sending an email: MUA ----> MTA ----> (MTA relays) ----> MDA Fetching an email: MUA <--------------------------------- MDA There may be other moving parts or sub-divisions (for instance, at several points along the chain, specialized programs may be analyzing, filtering, bouncing, editing\u2026 the exchanged emails). In a nutshell, docker-mailserver provides you with the following components: A MTA: Postfix A MDA: Dovecot A bunch of additional programs to improve security and emails processing Here's where docker-mailserver 's toochain fits within the delivery chain: docker-mailserver is here: \u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513 Sending an email: MUA ---> MTA ---> (MTA relays) ---> \u252b MTA \u256e \u2503 Fetching an email: MUA <------------------------------ \u252b MDA \u256f \u2503 \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251b Example Let's say Alice owns a Gmail account, alice@gmail.com ; and Bob owns an account on a docker-mailserver 's instance, bob@dms.io . Make sure not to conflate these two very different scenarios: A) Alice sends an email to bob@dms.io => the email is first submitted to MTA smtp.gmail.com , then relayed to MTA smtp.dms.io where it is then delivered into Bob's mailbox. B) Bob sends an email to alice@gmail.com => the email is first submitted to MTA smtp.dms.io , then relayed to MTA smtp.gmail.com and eventually delivered into Alice's mailbox. In scenario A the email leaves Gmail's premises, that email's initial submission is not handled by your docker-mailserver instance(MTA); it merely receives the email after it has been relayed by Gmail's MTA. In scenario B , the docker-mailserver instance(MTA) handles the submission, prior to relaying. The main takeaway is that when a third-party sends an email to a docker-mailserver instance(MTA) (or any MTA for that matter), it does not establish a direct connection with that MTA. Email submission first goes through the sender's MTA, then some relaying between at least two MTAs is required to deliver the email. That will prove very important when it comes to security management. One important thing to note is that MTA and MDA programs may actually handle multiple tasks (which is the case with docker-mailserver 's Postfix and Dovecot). For instance, Postfix is both an SMTP server (accepting emails) and a relaying MTA (transferring, ie. sending emails to other MTA/MDA); Dovecot is both an MDA (delivering emails in mailboxes) and an IMAP server (allowing MUAs to fetch emails from the mail-server ). On top of that, Postfix may rely on Dovecot's authentication capabilities. The exact relationship between all the components and their respective (sometimes shared) responsibilities is beyond the scope of this document. Please explore this wiki & the web to get more insights about docker-mailserver 's toolchain. About Security & Ports In the previous section, different components were outlined. Each one of those is responsible for a specific task, it has a specific purpose. Three main purposes exist when it comes to exchanging emails: Submission : for a MUA (client), the act of sending actual email data over the network, toward an MTA (server). Transfer (aka. Relay ): for an MTA, the act of sending actual email data over the network, toward another MTA (server) closer to the final destination (where an MTA will forward data to an MDA). Retrieval : for a MUA (client), the act of fetching actual email data over the network, from an MDA. Postfix handles Submission (and might handle Relay), whereas Dovecot handles Retrieval. They both need to be accessible by MUAs in order to act as servers, therefore they expose public endpoints on specific TCP ports (see. Understanding the ports for more details). Those endpoints may be secured, using an encryption scheme and TLS certificates. When it comes to the specifics of email exchange, we have to look at protocols and ports enabled to support all the identified purposes. There are several valid options and they've been evolving overtime. Here's docker-mailserver 's default configuration: Purpose Protocol TCP port / encryption Transfer/Relay SMTP 25 (unencrypted) Submission ESMTP 587 (encrypted using STARTTLS) Retrieval IMAP4 143 (encrypted using STARTTLS) + 993 (TLS) Retrieval POP3 Not activated \u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Submission \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Transfer/Relay \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2510 MUA ----- STARTTLS ---> \u2524(587) MTA \u256e (25)\u251c <-- cleartext ---> \u250a Third-party MTA \u250a ---- cleartext ---> \u2524(25) \u2502 | \u2514\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2518 |\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504| MUA <---- STARTTLS ---- \u2524(143) MDA \u256f | <-- enforced TLS -- \u2524(993) | \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Retrieval \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251b If you're new to email infrastructure, both that table and the schema may be confusing. Read on to expand your understanding and learn about docker-mailserver 's configuration, including how you can customize it. Submission - SMTP For a MUA to send an email to an MTA, it needs to establish a connection with that server, then push data packets over a network that both the MUA (client) and the MTA (server) are connected to. The server implements the SMTP protocol, which makes it capable of handling Submission . In the case of docker-mailserver , the MTA (SMTP server) is Postfix. The MUA (client) may vary, yet its Submission request is performed as TCP packets sent over the public internet. This exchange of information may be secured in order to counter eavesdropping. Two kinds of Submission Let's say I own an account on a docker-mailserver instance, me@dms.io . There are two very different use-cases for Submission: I want to send an email to someone Someone wants to send you an email In the first scenario, I will be submitting my email directly to my docker-mailserver instance/MTA (Postfix), which will then relay the email to its recipient's MTA for final delivery. In this case, Submission is first handled by establishing a direct connection to my own MTA-so at least for this portion of the delivery chain, I'll be able to ensure security/confidentiality. Not so much for what comes next, ie. relaying between MTAs and final delivery. In the second scenario, a third-party email account owner will be first submitting an email to some third-party MTA. I have no control over this initial portion of the delivery chain, nor do I have control over the relaying that comes next. My MTA will merely accept a relayed email coming \"out of the blue\". My MTA will thus have to support two kinds of Submission: Outward Submission (self-owned email is submitted directly to the MTA, then is relayed \"outside\") Inward Submission (third-party email has been submitted & relayed, then is accepted \"inside\" by the MTA) \u250f\u2501\u2501\u2501\u2501 Outward Submission \u2501\u2501\u2501\u2501\u2513 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2510 Me ---------------> \u2524 \u251c -----------------> \u250a \u250a \u2502 My MTA \u2502 \u250a Third-party MTA \u250a \u2502 \u251c <----------------- \u250a \u250a \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2518 \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Inward Submission \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251b Outward Submission The best practice as of 2020 when it comes to securing Outward Submission is to use Implicit TLS connection via ESMTP on port 465 (see RFC 8314 ). Let's break it down. Implicit TLS means the server enforces the client into using an encrypted TCP connection, using TLS . With this kind of connection, the MUA has to establish a TLS-encrypted connection from the get go (TLS is implied, hence the name \"Implicit\"). Any client attempting to either submit email in cleartext (unencrypted, not secure), or requesting a cleartext connection to be upgraded to a TLS-encrypted one using STARTTLS , is to be denied. Implicit TLS is sometimes called Enforced TLS for that reason. ESMTP is SMTP + extensions. It's the version of the SMTP protocol that a mail-server commonly communicates with today. For the purpose of this documentation, ESMTP and SMTP are synonymous. Port 465 is the reserved TCP port for Implicit TLS Submission (since 2018). There is actually a boisterous history to that ports usage, but let's keep it simple. Warning This Submission setup is sometimes referred to as SMTPS . Long story short: this is incorrect and should be avoided. Although a very satisfactory setup, Implicit TLS on port 465 is somewhat \"cutting edge\". There exists another well established mail Submission setup that must be supported as well, SMTP+STARTTLS on port 587. It uses Explicit TLS: the client starts with a cleartext connection, then the server informs a TLS-encrypted \"upgraded\" connection may be established, and the client may eventually decide to establish it prior to the Submission. Basically it's an opportunistic, opt-in TLS upgrade of the connection between the client and the server, at the client's discretion, using a mechanism known as STARTTLS that both ends need to implement. In many implementations, the mail-server doesn't enforce TLS encryption, for backwards compatibility. Clients are thus free to deny the TLS-upgrade proposal (or misled by a hacker about STARTTLS not being available), and the server accepts unencrypted (cleartext) mail exchange, which poses a confidentiality threat and, to some extent, spam issues. RFC 8314 (section 3.3) recommends for a mail-server to support both Implicit and Explicit TLS for Submission, and to enforce TLS-encryption on ports 587 (Explicit TLS) and 465 (Implicit TLS). That's exactly docker-mailserver 's default configuration: abiding by RFC 8314, it enforces a strict ( encrypt ) STARTTLS policy , where a denied TLS upgrade terminates the connection thus (hopefully but at the client's discretion) preventing unencrypted (cleartext) Submission. docker-mailserver 's default configuration enables and requires Explicit TLS (STARTTLS) on port 587 for Outward Submission. It does not enable Implicit TLS Outward Submission on port 465 by default. One may enable it through simple custom configuration, either as a replacement or (better!) supplementary mean of secure Submission. It does not support old MUAs (clients) not supporting TLS encryption on ports 587/465 (those should perform Submission on port 25, more details below). One may relax that constraint through advanced custom configuration, for backwards compatibility. A final Outward Submission setup exists and is akin SMTP+STARTTLS on port 587, but on port 25. That port has historically been reserved specifically for unencrypted (cleartext) mail exchange though, making STARTTLS a bit wrong to use. As is expected by RFC 5321 , docker-mailserver uses port 25 for unencrypted Submission in order to support older clients, but most importantly for unencrypted Transfer/Relay between MTAs. docker-mailserver 's default configuration also enables unencrypted (cleartext) on port 25 for Outward Submission. It does not enable Explicit TLS (STARTTLS) on port 25 by default. One may enable it through advanced custom configuration, either as a replacement (bad!) or as a supplementary mean of secure Outward Submission. One may also secure Outward Submission using advanced encryption scheme, such as DANE/DNSSEC and/or MTA-STS. Inward Submission Granted it's still very difficult enforcing encryption between MTAs (Transfer/Relay) without risking dropping emails (when relayed by MTAs not supporting TLS-encryption), Inward Submission is to be handled in cleartext on port 25 by default. docker-mailserver 's default configuration enables unencrypted (cleartext) on port 25 for Inward Submission. It does not enable Explicit TLS (STARTTLS) on port 25 by default. One may enable it through advanced custom configuration, either as a replacement (bad!) or as a supplementary mean of secure Inward Submission. One may also secure Inward Submission using advanced encryption scheme, such as DANE/DNSSEC and/or MTA-STS. Overall, docker-mailserver 's default configuration for SMTP looks like this: \u250f\u2501\u2501\u2501\u2501 Outward Submission \u2501\u2501\u2501\u2501\u2513 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2510 Me -- cleartext --> \u2524(25) (25)\u251c --- cleartext ---> \u250a \u250a Me -- STARTTLS ---> \u2524(587) My MTA \u2502 \u250a Third-party MTA \u250a \u2502 (25)\u251c <---cleartext ---- \u250a \u250a \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2518 \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Inward Submission \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251b Retrieval - IMAP A MUA willing to fetch an email from a mail-server will most likely communicate with its IMAP server. As with SMTP described earlier, communication will take place in the form of data packets exchanged over a network that both the client and the server are connected to. The IMAP protocol makes the server capable of handling Retrieval . In the case of docker-mailserver , the IMAP server is Dovecot. The MUA (client) may vary, yet its Retrieval request is performed as TCP packets sent over the public internet. This exchange of information may be secured in order to counter eavesdropping. Again, as with SMTP described earlier, the IMAP protocol may be secured with either Implicit TLS (aka. IMAPS / IMAP4S) or Explicit TLS (using STARTTLS). The best practice as of 2020 is to enforce IMAPS on port 993, rather than IMAP+STARTTLS on port 143 (see RFC 8314 ); yet the latter is usually provided for backwards compatibility. docker-mailserver 's default configuration enables both Implicit and Explicit TLS for Retrievial, on ports 993 and 143 respectively. Retrieval - POP3 Similarly to IMAP, the older POP3 protocol may be secured with either Implicit or Explicit TLS. The best practice as of 2020 would be POP3S on port 995, rather than POP3 +STARTTLS on port 110 (see RFC 8314 ). docker-mailserver 's default configuration disables POP3 altogether. One should expect MUAs to use TLS-encrypted IMAP for Retrieval. How does docker-mailserver help with setting everything up? As a batteries included Docker image, docker-mailserver provides you with all the required components and a default configuration, to run a decent and secure mail-server. One may then customize all aspects of its internal components. Simple customization is supported through docker-compose configuration and the env-mailserver configuration file. Advanced customization is supported through providing \"monkey-patching\" configuration files and/or deriving your own image from docker-mailserver 's upstream, for a complete control over how things run. On the subject of security, one might consider docker-mailserver 's default configuration to not be 100% secure: it enables unencrypted traffic on port 25 it enables Explicit TLS (STARTTLS) on port 587, instead of Implicit TLS on port 465 We believe docker-mailserver 's default configuration to be a good middle ground: it goes slightly beyond \"old\" (1999) RFC 2487 ; and with developer friendly configuration settings, it makes it pretty easy to abide by the \"newest\" (2018) RFC 8314 . Eventually, it is up to you deciding exactly what kind of transportation/encryption to use and/or enforce, and to customize your instance accordingly (with looser or stricter security). Be also aware that protocols and ports on your server can only go so far with security; third-party MTAs might relay your emails on insecure connections, man-in-the-middle attacks might still prove effective, etc. Advanced counter-measure such as DANE, MTA-STS and/or full body encryption (eg. PGP) should be considered as well for increased confidentiality, but ideally without compromising backwards compatibility so as to not block emails. The README is the best starting point in configuring and running your mail-server. You may then explore this wiki to cover additional topics, including but not limited to, security.","title":"Introduction"},{"location":"introduction/#anatomy-of-a-mail-server","text":"A mail-server is only a part of a client-server relationship aimed at exchanging information in the form of emails . Exchanging emails requires using specific means (programs and protocols). docker-mailserver provides you with the server portion, whereas the client can be anything from a terminal via text-based software (eg. Mutt ) to a fully-fledged desktop application (eg. Mozilla Thunderbird , Microsoft Outlook \u2026), to a web interface, etc. Unlike the client-side where usually a single program is used to perform retrieval and viewing of emails, the server-side is composed of many specialized components. The mail-server is capable of accepting, forwarding, delivering, storing and overall exchanging messages, but each one of those tasks is actually handled by a specific piece of software. All of these \"agents\" must be integrated with one another for the exchange to take place. docker-mailserver has made informed choices about those components and their (default) configuration. It offers a comprehensive platform to run a fully featured mail-server in no time!","title":"Anatomy of a Mail-Server"},{"location":"introduction/#components","text":"The following components are required to create a complete delivery chain : MUA: a Mail User Agent is basically any client/program capable of sending emails to a mail-server; while also capable of fetching emails from a mail-server for presenting them to the end users. MTA: a Mail Transfer Agent is the so-called \"mail-server\" as seen from the MUA's perspective. It's a piece of software dedicated to accepting submitted emails, then forwarding them-where exactly will depend on an email's final destination. If the receiving MTA is responsible for the FQDN the email is sent to, then an MTA is to forward that email to an MDA (see below). Otherwise, it is to transfer (ie. forward, relay) to another MTA, \"closer\" to the email's final destination. MDA: a Mail Delivery Agent is responsible for accepting emails from an MTA and dropping them into their recipients' mailboxes, whichever the form. Here's a schematic view of mail delivery: Sending an email: MUA ----> MTA ----> (MTA relays) ----> MDA Fetching an email: MUA <--------------------------------- MDA There may be other moving parts or sub-divisions (for instance, at several points along the chain, specialized programs may be analyzing, filtering, bouncing, editing\u2026 the exchanged emails). In a nutshell, docker-mailserver provides you with the following components: A MTA: Postfix A MDA: Dovecot A bunch of additional programs to improve security and emails processing Here's where docker-mailserver 's toochain fits within the delivery chain: docker-mailserver is here: \u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513 Sending an email: MUA ---> MTA ---> (MTA relays) ---> \u252b MTA \u256e \u2503 Fetching an email: MUA <------------------------------ \u252b MDA \u256f \u2503 \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251b Example Let's say Alice owns a Gmail account, alice@gmail.com ; and Bob owns an account on a docker-mailserver 's instance, bob@dms.io . Make sure not to conflate these two very different scenarios: A) Alice sends an email to bob@dms.io => the email is first submitted to MTA smtp.gmail.com , then relayed to MTA smtp.dms.io where it is then delivered into Bob's mailbox. B) Bob sends an email to alice@gmail.com => the email is first submitted to MTA smtp.dms.io , then relayed to MTA smtp.gmail.com and eventually delivered into Alice's mailbox. In scenario A the email leaves Gmail's premises, that email's initial submission is not handled by your docker-mailserver instance(MTA); it merely receives the email after it has been relayed by Gmail's MTA. In scenario B , the docker-mailserver instance(MTA) handles the submission, prior to relaying. The main takeaway is that when a third-party sends an email to a docker-mailserver instance(MTA) (or any MTA for that matter), it does not establish a direct connection with that MTA. Email submission first goes through the sender's MTA, then some relaying between at least two MTAs is required to deliver the email. That will prove very important when it comes to security management. One important thing to note is that MTA and MDA programs may actually handle multiple tasks (which is the case with docker-mailserver 's Postfix and Dovecot). For instance, Postfix is both an SMTP server (accepting emails) and a relaying MTA (transferring, ie. sending emails to other MTA/MDA); Dovecot is both an MDA (delivering emails in mailboxes) and an IMAP server (allowing MUAs to fetch emails from the mail-server ). On top of that, Postfix may rely on Dovecot's authentication capabilities. The exact relationship between all the components and their respective (sometimes shared) responsibilities is beyond the scope of this document. Please explore this wiki & the web to get more insights about docker-mailserver 's toolchain.","title":"Components"},{"location":"introduction/#about-security-ports","text":"In the previous section, different components were outlined. Each one of those is responsible for a specific task, it has a specific purpose. Three main purposes exist when it comes to exchanging emails: Submission : for a MUA (client), the act of sending actual email data over the network, toward an MTA (server). Transfer (aka. Relay ): for an MTA, the act of sending actual email data over the network, toward another MTA (server) closer to the final destination (where an MTA will forward data to an MDA). Retrieval : for a MUA (client), the act of fetching actual email data over the network, from an MDA. Postfix handles Submission (and might handle Relay), whereas Dovecot handles Retrieval. They both need to be accessible by MUAs in order to act as servers, therefore they expose public endpoints on specific TCP ports (see. Understanding the ports for more details). Those endpoints may be secured, using an encryption scheme and TLS certificates. When it comes to the specifics of email exchange, we have to look at protocols and ports enabled to support all the identified purposes. There are several valid options and they've been evolving overtime. Here's docker-mailserver 's default configuration: Purpose Protocol TCP port / encryption Transfer/Relay SMTP 25 (unencrypted) Submission ESMTP 587 (encrypted using STARTTLS) Retrieval IMAP4 143 (encrypted using STARTTLS) + 993 (TLS) Retrieval POP3 Not activated \u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Submission \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Transfer/Relay \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2510 MUA ----- STARTTLS ---> \u2524(587) MTA \u256e (25)\u251c <-- cleartext ---> \u250a Third-party MTA \u250a ---- cleartext ---> \u2524(25) \u2502 | \u2514\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2518 |\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504| MUA <---- STARTTLS ---- \u2524(143) MDA \u256f | <-- enforced TLS -- \u2524(993) | \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Retrieval \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251b If you're new to email infrastructure, both that table and the schema may be confusing. Read on to expand your understanding and learn about docker-mailserver 's configuration, including how you can customize it.","title":"About Security & Ports"},{"location":"introduction/#submission-smtp","text":"For a MUA to send an email to an MTA, it needs to establish a connection with that server, then push data packets over a network that both the MUA (client) and the MTA (server) are connected to. The server implements the SMTP protocol, which makes it capable of handling Submission . In the case of docker-mailserver , the MTA (SMTP server) is Postfix. The MUA (client) may vary, yet its Submission request is performed as TCP packets sent over the public internet. This exchange of information may be secured in order to counter eavesdropping.","title":"Submission - SMTP"},{"location":"introduction/#two-kinds-of-submission","text":"Let's say I own an account on a docker-mailserver instance, me@dms.io . There are two very different use-cases for Submission: I want to send an email to someone Someone wants to send you an email In the first scenario, I will be submitting my email directly to my docker-mailserver instance/MTA (Postfix), which will then relay the email to its recipient's MTA for final delivery. In this case, Submission is first handled by establishing a direct connection to my own MTA-so at least for this portion of the delivery chain, I'll be able to ensure security/confidentiality. Not so much for what comes next, ie. relaying between MTAs and final delivery. In the second scenario, a third-party email account owner will be first submitting an email to some third-party MTA. I have no control over this initial portion of the delivery chain, nor do I have control over the relaying that comes next. My MTA will merely accept a relayed email coming \"out of the blue\". My MTA will thus have to support two kinds of Submission: Outward Submission (self-owned email is submitted directly to the MTA, then is relayed \"outside\") Inward Submission (third-party email has been submitted & relayed, then is accepted \"inside\" by the MTA) \u250f\u2501\u2501\u2501\u2501 Outward Submission \u2501\u2501\u2501\u2501\u2513 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2510 Me ---------------> \u2524 \u251c -----------------> \u250a \u250a \u2502 My MTA \u2502 \u250a Third-party MTA \u250a \u2502 \u251c <----------------- \u250a \u250a \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2518 \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Inward Submission \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251b","title":"Two kinds of Submission"},{"location":"introduction/#outward-submission","text":"The best practice as of 2020 when it comes to securing Outward Submission is to use Implicit TLS connection via ESMTP on port 465 (see RFC 8314 ). Let's break it down. Implicit TLS means the server enforces the client into using an encrypted TCP connection, using TLS . With this kind of connection, the MUA has to establish a TLS-encrypted connection from the get go (TLS is implied, hence the name \"Implicit\"). Any client attempting to either submit email in cleartext (unencrypted, not secure), or requesting a cleartext connection to be upgraded to a TLS-encrypted one using STARTTLS , is to be denied. Implicit TLS is sometimes called Enforced TLS for that reason. ESMTP is SMTP + extensions. It's the version of the SMTP protocol that a mail-server commonly communicates with today. For the purpose of this documentation, ESMTP and SMTP are synonymous. Port 465 is the reserved TCP port for Implicit TLS Submission (since 2018). There is actually a boisterous history to that ports usage, but let's keep it simple. Warning This Submission setup is sometimes referred to as SMTPS . Long story short: this is incorrect and should be avoided. Although a very satisfactory setup, Implicit TLS on port 465 is somewhat \"cutting edge\". There exists another well established mail Submission setup that must be supported as well, SMTP+STARTTLS on port 587. It uses Explicit TLS: the client starts with a cleartext connection, then the server informs a TLS-encrypted \"upgraded\" connection may be established, and the client may eventually decide to establish it prior to the Submission. Basically it's an opportunistic, opt-in TLS upgrade of the connection between the client and the server, at the client's discretion, using a mechanism known as STARTTLS that both ends need to implement. In many implementations, the mail-server doesn't enforce TLS encryption, for backwards compatibility. Clients are thus free to deny the TLS-upgrade proposal (or misled by a hacker about STARTTLS not being available), and the server accepts unencrypted (cleartext) mail exchange, which poses a confidentiality threat and, to some extent, spam issues. RFC 8314 (section 3.3) recommends for a mail-server to support both Implicit and Explicit TLS for Submission, and to enforce TLS-encryption on ports 587 (Explicit TLS) and 465 (Implicit TLS). That's exactly docker-mailserver 's default configuration: abiding by RFC 8314, it enforces a strict ( encrypt ) STARTTLS policy , where a denied TLS upgrade terminates the connection thus (hopefully but at the client's discretion) preventing unencrypted (cleartext) Submission. docker-mailserver 's default configuration enables and requires Explicit TLS (STARTTLS) on port 587 for Outward Submission. It does not enable Implicit TLS Outward Submission on port 465 by default. One may enable it through simple custom configuration, either as a replacement or (better!) supplementary mean of secure Submission. It does not support old MUAs (clients) not supporting TLS encryption on ports 587/465 (those should perform Submission on port 25, more details below). One may relax that constraint through advanced custom configuration, for backwards compatibility. A final Outward Submission setup exists and is akin SMTP+STARTTLS on port 587, but on port 25. That port has historically been reserved specifically for unencrypted (cleartext) mail exchange though, making STARTTLS a bit wrong to use. As is expected by RFC 5321 , docker-mailserver uses port 25 for unencrypted Submission in order to support older clients, but most importantly for unencrypted Transfer/Relay between MTAs. docker-mailserver 's default configuration also enables unencrypted (cleartext) on port 25 for Outward Submission. It does not enable Explicit TLS (STARTTLS) on port 25 by default. One may enable it through advanced custom configuration, either as a replacement (bad!) or as a supplementary mean of secure Outward Submission. One may also secure Outward Submission using advanced encryption scheme, such as DANE/DNSSEC and/or MTA-STS.","title":"Outward Submission"},{"location":"introduction/#inward-submission","text":"Granted it's still very difficult enforcing encryption between MTAs (Transfer/Relay) without risking dropping emails (when relayed by MTAs not supporting TLS-encryption), Inward Submission is to be handled in cleartext on port 25 by default. docker-mailserver 's default configuration enables unencrypted (cleartext) on port 25 for Inward Submission. It does not enable Explicit TLS (STARTTLS) on port 25 by default. One may enable it through advanced custom configuration, either as a replacement (bad!) or as a supplementary mean of secure Inward Submission. One may also secure Inward Submission using advanced encryption scheme, such as DANE/DNSSEC and/or MTA-STS. Overall, docker-mailserver 's default configuration for SMTP looks like this: \u250f\u2501\u2501\u2501\u2501 Outward Submission \u2501\u2501\u2501\u2501\u2513 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2510 Me -- cleartext --> \u2524(25) (25)\u251c --- cleartext ---> \u250a \u250a Me -- STARTTLS ---> \u2524(587) My MTA \u2502 \u250a Third-party MTA \u250a \u2502 (25)\u251c <---cleartext ---- \u250a \u250a \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2518 \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Inward Submission \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251b","title":"Inward Submission"},{"location":"introduction/#retrieval-imap","text":"A MUA willing to fetch an email from a mail-server will most likely communicate with its IMAP server. As with SMTP described earlier, communication will take place in the form of data packets exchanged over a network that both the client and the server are connected to. The IMAP protocol makes the server capable of handling Retrieval . In the case of docker-mailserver , the IMAP server is Dovecot. The MUA (client) may vary, yet its Retrieval request is performed as TCP packets sent over the public internet. This exchange of information may be secured in order to counter eavesdropping. Again, as with SMTP described earlier, the IMAP protocol may be secured with either Implicit TLS (aka. IMAPS / IMAP4S) or Explicit TLS (using STARTTLS). The best practice as of 2020 is to enforce IMAPS on port 993, rather than IMAP+STARTTLS on port 143 (see RFC 8314 ); yet the latter is usually provided for backwards compatibility. docker-mailserver 's default configuration enables both Implicit and Explicit TLS for Retrievial, on ports 993 and 143 respectively.","title":"Retrieval - IMAP"},{"location":"introduction/#retrieval-pop3","text":"Similarly to IMAP, the older POP3 protocol may be secured with either Implicit or Explicit TLS. The best practice as of 2020 would be POP3S on port 995, rather than POP3 +STARTTLS on port 110 (see RFC 8314 ). docker-mailserver 's default configuration disables POP3 altogether. One should expect MUAs to use TLS-encrypted IMAP for Retrieval.","title":"Retrieval - POP3"},{"location":"introduction/#how-does-docker-mailserver-help-with-setting-everything-up","text":"As a batteries included Docker image, docker-mailserver provides you with all the required components and a default configuration, to run a decent and secure mail-server. One may then customize all aspects of its internal components. Simple customization is supported through docker-compose configuration and the env-mailserver configuration file. Advanced customization is supported through providing \"monkey-patching\" configuration files and/or deriving your own image from docker-mailserver 's upstream, for a complete control over how things run. On the subject of security, one might consider docker-mailserver 's default configuration to not be 100% secure: it enables unencrypted traffic on port 25 it enables Explicit TLS (STARTTLS) on port 587, instead of Implicit TLS on port 465 We believe docker-mailserver 's default configuration to be a good middle ground: it goes slightly beyond \"old\" (1999) RFC 2487 ; and with developer friendly configuration settings, it makes it pretty easy to abide by the \"newest\" (2018) RFC 8314 . Eventually, it is up to you deciding exactly what kind of transportation/encryption to use and/or enforce, and to customize your instance accordingly (with looser or stricter security). Be also aware that protocols and ports on your server can only go so far with security; third-party MTAs might relay your emails on insecure connections, man-in-the-middle attacks might still prove effective, etc. Advanced counter-measure such as DANE, MTA-STS and/or full body encryption (eg. PGP) should be considered as well for increased confidentiality, but ideally without compromising backwards compatibility so as to not block emails. The README is the best starting point in configuring and running your mail-server. You may then explore this wiki to cover additional topics, including but not limited to, security.","title":"How does docker-mailserver help with setting everything up?"},{"location":"usage/","text":"This pages explains how to get started with DMS, basically explaining how you can use it. The procedure uses Docker Compose as a reference. In our examples, /docker-data/dms/config/ on the host is mounted to /tmp/docker-mailserver/ inside the container. Available Images / Tags - Tagging Convention CI/CD will automatically build, test and push new images to container registries. Currently, the following registries are supported: DockerHub ( docker.io/mailserver/docker-mailserver ) GitHub Container Registry ( ghcr.io/docker-mailserver/docker-mailserver ) All workflows are using the tagging convention listed below. It is subsequently applied to all images. Event Image Tags push on master edge push a tag ( v1.2.3 ) 1.2.3 , 1.2 , 1 , latest Get the Tools setup.sh Not Required Anymore Since DMS v10.2.0 , setup.sh functionality is included within the container image. The external convenience script is no longer required if you prefer using docker exec setup instead. Issue the following commands to acquire the necessary files: DMS_GITHUB_URL = 'https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master' wget \" ${ DMS_GITHUB_URL } /docker-compose.yml\" wget \" ${ DMS_GITHUB_URL } /mailserver.env\" # Optional wget \" ${ DMS_GITHUB_URL } /setup.sh\" chmod a+x ./setup.sh Create a docker-compose Environment Install the latest Docker Compose Edit docker-compose.yml to your liking substitute mail.example.com according to your FQDN if you want to use SELinux for the ./docker-data/dms/config/:/tmp/docker-mailserver/ mount, append -z or -Z Configure the mailserver container to your liking by editing mailserver.env ( Documentation ), but keep in mind this .env file: only basic VAR=VAL is supported ( do not quote your values) variable substitution is not supported (e.g. OVERRIDE_HOSTNAME=$HOSTNAME.$DOMAINNAME ) Podman Support If you're using podman, make sure to read the related documentation Get up and running First Things First Using the Correct Commands For Stopping and Starting DMS Use docker compose up / down , not docker compose start / stop . Otherwise, the container is not properly destroyed and you may experience problems during startup because of inconsistent state. You are able to get a full overview of how the configuration works by either running: ./setup.sh help which includes the options of setup.sh . docker run --rm docker.io/mailserver/docker-mailserver:latest setup help which provides you with all the information on configuration provided \"inside\" the container itself. Usage of setup.sh when no DMS Container Is Running If no DMS container is running, any ./setup.sh command will check online for the :latest image tag (the current stable release), performing a docker pull ... if necessary followed by running the command in a temporary container: $ ./setup.sh help Image 'docker.io/mailserver/docker-mailserver:latest' not found. Pulling ... SETUP(1) NAME setup - 'docker-mailserver' Administration & Configuration script ... $ docker run --rm docker.io/mailserver/docker-mailserver:latest setup help SETUP(1) NAME setup - 'docker-mailserver' Administration & Configuration script ... Starting for the first time On first start, you will need to add at least one email account (unless you're using LDAP). You have two minutes to do so, otherwise DMS will shutdown and restart. You can add accounts with the following two methods: Use setup.sh : ./setup.sh email add Run the command directly in the container: docker exec -ti setup email add You can then proceed by creating the postmaster alias and by creating DKIM keys. docker-compose up -d mailserver # you may add some more users # for SELinux, use -Z ./setup.sh [ -Z ] email add [ ] # and configure aliases, DKIM and more ./setup.sh [ -Z ] alias add postmaster@ Further Miscellaneous Steps DNS - DKIM You can (and you should) generate DKIM keys by running ./setup.sh [ -Z ] config dkim If you want to see detailed usage information, run ./setup.sh config dkim help In case you're using LDAP, the setup looks a bit different as you do not add user accounts directly. Postfix doesn't know your domain(s) and you need to provide it when configuring DKIM: ./setup.sh config dkim domain '[,]' 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 . See the documentation for more details. Custom User Changes & Patches If you'd like to change, patch or alter files or behavior of docker-mailserver , you can use a script. See this part of our documentation for a detailed explanation.","title":"Usage"},{"location":"usage/#available-images-tags-tagging-convention","text":"CI/CD will automatically build, test and push new images to container registries. Currently, the following registries are supported: DockerHub ( docker.io/mailserver/docker-mailserver ) GitHub Container Registry ( ghcr.io/docker-mailserver/docker-mailserver ) All workflows are using the tagging convention listed below. It is subsequently applied to all images. Event Image Tags push on master edge push a tag ( v1.2.3 ) 1.2.3 , 1.2 , 1 , latest","title":"Available Images / Tags - Tagging Convention"},{"location":"usage/#get-the-tools","text":"setup.sh Not Required Anymore Since DMS v10.2.0 , setup.sh functionality is included within the container image. The external convenience script is no longer required if you prefer using docker exec setup instead. Issue the following commands to acquire the necessary files: DMS_GITHUB_URL = 'https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master' wget \" ${ DMS_GITHUB_URL } /docker-compose.yml\" wget \" ${ DMS_GITHUB_URL } /mailserver.env\" # Optional wget \" ${ DMS_GITHUB_URL } /setup.sh\" chmod a+x ./setup.sh","title":"Get the Tools"},{"location":"usage/#create-a-docker-compose-environment","text":"Install the latest Docker Compose Edit docker-compose.yml to your liking substitute mail.example.com according to your FQDN if you want to use SELinux for the ./docker-data/dms/config/:/tmp/docker-mailserver/ mount, append -z or -Z Configure the mailserver container to your liking by editing mailserver.env ( Documentation ), but keep in mind this .env file: only basic VAR=VAL is supported ( do not quote your values) variable substitution is not supported (e.g. OVERRIDE_HOSTNAME=$HOSTNAME.$DOMAINNAME ) Podman Support If you're using podman, make sure to read the related documentation","title":"Create a docker-compose Environment"},{"location":"usage/#get-up-and-running","text":"","title":"Get up and running"},{"location":"usage/#first-things-first","text":"Using the Correct Commands For Stopping and Starting DMS Use docker compose up / down , not docker compose start / stop . Otherwise, the container is not properly destroyed and you may experience problems during startup because of inconsistent state. You are able to get a full overview of how the configuration works by either running: ./setup.sh help which includes the options of setup.sh . docker run --rm docker.io/mailserver/docker-mailserver:latest setup help which provides you with all the information on configuration provided \"inside\" the container itself. Usage of setup.sh when no DMS Container Is Running If no DMS container is running, any ./setup.sh command will check online for the :latest image tag (the current stable release), performing a docker pull ... if necessary followed by running the command in a temporary container: $ ./setup.sh help Image 'docker.io/mailserver/docker-mailserver:latest' not found. Pulling ... SETUP(1) NAME setup - 'docker-mailserver' Administration & Configuration script ... $ docker run --rm docker.io/mailserver/docker-mailserver:latest setup help SETUP(1) NAME setup - 'docker-mailserver' Administration & Configuration script ...","title":"First Things First"},{"location":"usage/#starting-for-the-first-time","text":"On first start, you will need to add at least one email account (unless you're using LDAP). You have two minutes to do so, otherwise DMS will shutdown and restart. You can add accounts with the following two methods: Use setup.sh : ./setup.sh email add Run the command directly in the container: docker exec -ti setup email add You can then proceed by creating the postmaster alias and by creating DKIM keys. docker-compose up -d mailserver # you may add some more users # for SELinux, use -Z ./setup.sh [ -Z ] email add [ ] # and configure aliases, DKIM and more ./setup.sh [ -Z ] alias add postmaster@ ","title":"Starting for the first time"},{"location":"usage/#further-miscellaneous-steps","text":"","title":"Further Miscellaneous Steps"},{"location":"usage/#dns-dkim","text":"You can (and you should) generate DKIM keys by running ./setup.sh [ -Z ] config dkim If you want to see detailed usage information, run ./setup.sh config dkim help In case you're using LDAP, the setup looks a bit different as you do not add user accounts directly. Postfix doesn't know your domain(s) and you need to provide it when configuring DKIM: ./setup.sh config dkim domain '[,]' 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 . See the documentation for more details.","title":"DNS - DKIM"},{"location":"usage/#custom-user-changes-patches","text":"If you'd like to change, patch or alter files or behavior of docker-mailserver , you can use a script. See this part of our documentation for a detailed explanation.","title":"Custom User Changes & Patches"},{"location":"config/environment/","text":"Info Values in bold are the default values. If an option doesn't work as documented here, check if you are running the latest image. The current master branch corresponds to the image mailserver/docker-mailserver:edge . General OVERRIDE_HOSTNAME empty => uses the hostname command to get canonical hostname for docker-mailserver to use. => Specify a fully-qualified domainname to serve mail for. This is used for many of the config features so if you can't set your hostname ( eg: you're in a container platform that doesn't let you ) specify it via this environment variable. It will take priority over docker run options: --hostname and --domainname , or docker-compose.yml config equivalents: hostname: and domainname: . DMS_DEBUG This environment variable was removed in v11.0.0 ! Use LOG_LEVEL instead. LOG_LEVEL Set the log level for DMS. This is mostly relevant for container startup scripts and change detection event feedback. Valid values (in order of increasing verbosity) are: error , warn , info , debug and trace . The default log level is info . SUPERVISOR_LOGLEVEL Here you can adjust the log-level for Supervisor . Possible values are critical => Only show critical messages error => Only show erroneous output warn => Show warnings info => Normal informational output debug => Also show debug messages The log-level will show everything in its class and above. ONE_DIR 0 => state in default directories. 1 => consolidate all states into a single directory ( /var/mail-state ) to allow persistence using docker volumes. See the related FAQ entry for more information. ACCOUNT_PROVISIONER Configures the provisioning source of user accounts (including aliases) for user queries and authentication by services managed by DMS ( Postfix and Dovecot ). User provisioning via OIDC is planned for the future, see this tracking issue . empty => use FILE LDAP => use LDAP authentication OIDC => use OIDC authentication ( not yet implemented ) FILE => use local files (this is used as the default) A second container for the ldap service is necessary (e.g. docker-openldap ) PERMIT_DOCKER Set different options for mynetworks option (can be overwrite in postfix-main.cf) WARNING : Adding the docker network's gateway to the list of trusted hosts, e.g. using the network or connected-networks option, can create an open relay , for instance if IPv6 is enabled on the host machine but not in Docker. none => Explicitly force authentication container => Container IP address only. host => Add docker host (ipv4 only). network => Add the docker default bridge network (172.16.0.0/12); WARNING : docker-compose might use others (e.g. 192.168.0.0/16) use PERMIT_DOCKER=connected-networks in this case. connected-networks => Add all connected docker networks (ipv4 only). Note: you probably want to set POSTFIX_INET_PROTOCOLS=ipv4 to make it work fine with Docker. TZ Set the timezone. If this variable is unset, the container runtime will try to detect the time using /etc/localtime , which you can alternatively mount into the container. The value of this variable must follow the pattern AREA/ZONE , i.e. of you want to use Germany's time zone, use Europe/Berlin . You can lookup all available timezones here . ENABLE_RSPAMD Enable or disable Rspamd. Current State Rspamd-support is under active development. Be aware that breaking changes can happen at any time. To get more information, see the detailed documentation page for Rspamd . 0 => disabled 1 => enabled ENABLE_REDIS Explicit control over running a Redis instance within the container. By default, this value will match what is set for ENABLE_RSPAMD . The purpose of this setting is to opt-out of starting an internal Redis instance when enabling Rspamd, replacing it with your own external instance. Configuring rspamd for an external Redis instance You will need to provide configuration at /etc/rspamd/local.d/redis.conf similar to: servers = \"redis.example.test:6379\"; expand_keys = true; 0 => Disabled 1 => Enabled ENABLE_AMAVIS Amavis content filter (used for ClamAV & SpamAssassin) 0 => Amavis is disabled 1 => Amavis is enabled AMAVIS_LOGLEVEL This page provides information on Amavis' logging statistics. -1/-2/-3 => Only show errors 0 => Show warnings 1/2 => Show default informational output 3/4/5 => log debug information (very verbose) ENABLE_DNSBL This enables DNS block lists in Postscreen . If you want to know which lists we are using, have a look at the default main.cf for Postfix we provide and search for postscreen_dnsbl_sites . A Warning On DNS Block Lists Make sure your DNS queries are properly resolved, i.e. you will most likely not want to use a public DNS resolver as these queries do not return meaningful results. We try our best to only evaluate proper return codes - this is not a guarantee that all codes are handled fine though. Note that emails will be rejected if they don't pass the block list checks! 0 => DNS block lists are disabled 1 => DNS block lists are enabled ENABLE_OPENDKIM Enables the OpenDKIM service. 1 => Enabled 0 => Disabled ENABLE_OPENDMARC Enables the OpenDMARC service. 1 => Enabled 0 => Disabled ENABLE_POP3 empty => POP3 service disabled 1 => Enables POP3 service ENABLE_CLAMAV 0 => ClamAV is disabled 1 => ClamAV is enabled ENABLE_FAIL2BAN 0 => fail2ban service disabled 1 => Enables fail2ban service If you enable Fail2Ban, don't forget to add the following lines to your docker-compose.yml : cap_add: - NET_ADMIN Otherwise, nftables won't be able to ban IPs. FAIL2BAN_BLOCKTYPE drop => drop packet (send NO reply) reject => reject packet (send ICMP unreachable) FAIL2BAN_BLOCKTYPE=drop SMTP_ONLY empty => all daemons start 1 => only launch postfix smtp SSL_TYPE In the majority of cases, you want letsencrypt or manual . self-signed can be used for testing SSL until you provide a valid certificate, note that third-parties cannot trust self-signed certificates, do not use this type in production. custom is a temporary workaround that is not officially supported. empty => SSL disabled. letsencrypt => Support for using certificates with Let's Encrypt provisioners. (Docs: Let's Encrypt Setup ) manual => Provide your own certificate via separate key and cert files. (Docs: Bring Your Own Certificates ) Requires: SSL_CERT_PATH and SSL_KEY_PATH ENV vars to be set to the location of the files within the container. Optional: SSL_ALT_CERT_PATH and SSL_ALT_KEY_PATH allow providing a 2nd certificate as a fallback for dual (aka hybrid) certificate support. Useful for ECDSA with an RSA fallback. Presently only manual mode supports this feature . custom => Provide your own certificate as a single file containing both the private key and full certificate chain. (Docs: None ) self-signed => Provide your own self-signed certificate files. Expects a self-signed CA cert for verification. Use only for local testing of your setup . (Docs: Self-Signed Certificates ) Please read the SSL page in the documentation for more information. TLS_LEVEL empty => modern modern => Enables TLSv1.2 and modern ciphers only. (default) intermediate => Enables TLSv1, TLSv1.1 and TLSv1.2 and broad compatibility ciphers. SPOOF_PROTECTION Configures the handling of creating mails with forged sender addresses. empty => Mail address spoofing allowed. Any logged in user may create email messages with a forged sender address. See also Wikipedia (not recommended, but default for backwards compatibility reasons) 1 => (recommended) Mail spoofing denied. Each user may only send with his own or his alias addresses. Addresses with extension delimiters are not able to send messages. ENABLE_SRS Enables the Sender Rewriting Scheme. SRS is needed if docker-mailserver acts as forwarder. See postsrsd for further explanation. 0 => Disabled 1 => Enabled NETWORK_INTERFACE In case your network interface differs from eth0 , e.g. when you are using HostNetworking in Kubernetes, you can set this to whatever interface you want. This interface will then be used. empty => eth0 VIRUSMAILS_DELETE_DELAY Set how many days a virusmail will stay on the server before being deleted empty => 7 days POSTFIX_DAGENT Configure Postfix virtual_transport to deliver mail to a different LMTP client ( default is a unix socket to dovecot ). Provide any valid URI. Examples: empty => lmtp:unix:/var/run/dovecot/lmtp (default, configured in Postfix main.cf ) lmtp:unix:private/dovecot-lmtp (use socket) lmtps:inet:: (secure lmtp with starttls) lmtp::2003 (use kopano as mailstore) POSTFIX_MAILBOX_SIZE_LIMIT Set the mailbox size limit for all users. If set to zero, the size will be unlimited (default). empty => 0 (no limit) ENABLE_QUOTAS 1 => Dovecot quota is enabled 0 => Dovecot quota is disabled See mailbox quota . POSTFIX_MESSAGE_SIZE_LIMIT Set the message size limit for all users. If set to zero, the size will be unlimited (not recommended!) empty => 10240000 (~10 MB) CLAMAV_MESSAGE_SIZE_LIMIT Mails larger than this limit won't be scanned. ClamAV must be enabled (ENABLE_CLAMAV=1) for this. empty => 25M (25 MB) ENABLE_MANAGESIEVE empty => Managesieve service disabled 1 => Enables Managesieve on port 4190 POSTMASTER_ADDRESS empty => postmaster@example.com => Specify the postmaster address ENABLE_UPDATE_CHECK Check for updates on container start and then once a day. If an update is available, a mail is send to POSTMASTER_ADDRESS. 0 => Update check disabled 1 => Update check enabled UPDATE_CHECK_INTERVAL Customize the update check interval. Number + Suffix. Suffix must be 's' for seconds, 'm' for minutes, 'h' for hours or 'd' for days. 1d => Check for updates once a day POSTSCREEN_ACTION enforce => Allow other tests to complete. Reject attempts to deliver mail with a 550 SMTP reply, and log the helo/sender/recipient information. Repeat this test the next time the client connects. drop => Drop the connection immediately with a 521 SMTP reply. Repeat this test the next time the client connects. ignore => Ignore the failure of this test. Allow other tests to complete. Repeat this test the next time the client connects. This option is useful for testing and collecting statistics without blocking mail. DOVECOT_MAILBOX_FORMAT maildir => uses very common Maildir format, one file contains one message sdbox => (experimental) uses Dovecot high-performance mailbox format, one file contains one message mdbox ==> (experimental) uses Dovecot high-performance mailbox format, multiple messages per file and multiple files per box This option has been added in November 2019. Using other format than Maildir is considered as experimental in docker-mailserver and should only be used for testing purpose. For more details, please refer to Dovecot Documentation . POSTFIX_INET_PROTOCOLS all => Listen on all interfaces. ipv4 => Listen only on IPv4 interfaces. Most likely you want this behind Docker. ipv6 => Listen only on IPv6 interfaces. Note: More details at http://www.postfix.org/postconf.5.html#inet_protocols DOVECOT_INET_PROTOCOLS all => Listen on all interfaces ipv4 => Listen only on IPv4 interfaces. Most likely you want this behind Docker. ipv6 => Listen only on IPv6 interfaces. Note: More information at https://dovecot.org/doc/dovecot-example.conf Reports PFLOGSUMM_TRIGGER Enables regular Postfix log summary (\"pflogsumm\") mail reports. not set => No report daily_cron => Daily report for the previous day logrotate => Full report based on the mail log when it is rotated This is a new option. The old REPORT options are still supported for backwards compatibility. If this is not set and reports are enabled with the old options, logrotate will be used. PFLOGSUMM_RECIPIENT Recipient address for Postfix log summary reports. not set => Use POSTMASTER_ADDRESS => Specify the recipient address(es) PFLOGSUMM_SENDER Sender address ( FROM ) for pflogsumm reports (if Postfix log summary reports are enabled). not set => Use REPORT_SENDER => Specify the sender address LOGWATCH_INTERVAL Interval for logwatch report. none => No report is generated daily => Send a daily report weekly => Send a report every week LOGWATCH_RECIPIENT Recipient address for logwatch reports if they are enabled. not set => Use REPORT_RECIPIENT or POSTMASTER_ADDRESS => Specify the recipient address(es) LOGWATCH_SENDER Sender address ( FROM ) for logwatch reports if logwatch reports are enabled. not set => Use REPORT_SENDER => Specify the sender address REPORT_RECIPIENT Defines who receives reports (if they are enabled). empty => Use POSTMASTER_ADDRESS => Specify the recipient address REPORT_SENDER Defines who sends reports (if they are enabled). empty => mailserver-report@ => Specify the sender address LOGROTATE_INTERVAL Changes the interval in which log files are rotated. weekly => Rotate log files weekly daily => Rotate log files daily monthly => Rotate log files monthly Note LOGROTATE_INTERVAL only manages logrotate within the container for services we manage internally. The entire log output for the container is still available via docker logs mailserver (or your respective container name). If you want to configure external log rotation for that container output as well, : Docker Logging Drivers . By default, the logs are lost when the container is destroyed (eg: re-creating via docker-compose down && docker-compose up -d ). To keep the logs, mount a volume (to /var/log/mail/ ). Note This variable can also determine the interval for Postfix's log summary reports, see PFLOGSUMM_TRIGGER . SpamAssassin ENABLE_SPAMASSASSIN 0 => SpamAssassin is disabled 1 => SpamAssassin is enabled SPAMASSASSIN_SPAM_TO_INBOX 0 => Spam messages will be bounced ( rejected ) without any notification ( dangerous ). 1 => Spam messages will be delivered to the inbox and tagged as spam using SA_SPAM_SUBJECT . ENABLE_SPAMASSASSIN_KAM KAM is a 3rd party SpamAssassin ruleset, provided by the McGrail Foundation. If SpamAssassin is enabled, KAM can be used in addition to the default ruleset. 0 => KAM disabled 1 => KAM enabled MOVE_SPAM_TO_JUNK Spam messages can be moved in the Junk folder. Note: this setting needs SPAMASSASSIN_SPAM_TO_INBOX=1 0 => Spam messages will be delivered in the mailbox. 1 => Spam messages will be delivered in the Junk folder. SA_TAG 2.0 => add spam info headers if at, or above that level Note: this SpamAssassin setting needs ENABLE_SPAMASSASSIN=1 SA_TAG2 6.31 => add 'spam detected' headers at that level Note: this SpamAssassin setting needs ENABLE_SPAMASSASSIN=1 SA_KILL 6.31 => triggers spam evasive actions This SpamAssassin setting needs ENABLE_SPAMASSASSIN=1 By default, docker-mailserver is configured to quarantine spam emails. If emails are quarantined, they are compressed and stored in a location dependent on the ONE_DIR setting above. To inhibit this behaviour and deliver spam emails, set this to a very high value e.g. 100.0 . If ONE_DIR=1 (default) the location is /var/mail-state/lib-amavis/virusmails/ , or if ONE_DIR=0 : /var/lib/amavis/virusmails/ . These paths are inside the docker container. SA_SPAM_SUBJECT ***SPAM*** => add tag to subject if spam detected Note: this SpamAssassin setting needs ENABLE_SPAMASSASSIN=1 . Add the SpamAssassin score to the subject line by inserting the keyword _SCORE_: ***SPAM(_SCORE_)*** . SA_SHORTCIRCUIT_BAYES_SPAM 1 => will activate SpamAssassin short circuiting for bayes spam detection. This will uncomment the respective line in /etc/spamassasin/local.cf Note: activate this only if you are confident in your bayes database for identifying spam. SA_SHORTCIRCUIT_BAYES_HAM 1 => will activate SpamAssassin short circuiting for bayes ham detection This will uncomment the respective line in /etc/spamassasin/local.cf Note: activate this only if you are confident in your bayes database for identifying ham. Fetchmail ENABLE_FETCHMAIL 0 => fetchmail disabled 1 => fetchmail enabled FETCHMAIL_POLL 300 => fetchmail The number of seconds for the interval FETCHMAIL_PARALLEL 0 => fetchmail runs with a single config file /etc/fetchmailrc 1 => /etc/fetchmailrc is split per poll entry. For every poll entry a separate fetchmail instance is started to allow having multiple imap idle configurations defined. Note: The defaults of your fetchmailrc file need to be at the top of the file. Otherwise it won't be added correctly to all separate fetchmail instances. LDAP ENABLE_LDAP Deprecated. See ACCOUNT_PROVISIONER . LDAP_START_TLS empty => no yes => LDAP over TLS enabled for Postfix LDAP_SERVER_HOST empty => mail.example.com => Specify the dns-name/ip-address where the ldap-server is listening, or an URI like ldaps://mail.example.com NOTE: If you going to use docker-mailserver in combination with docker-compose.yml you can set the service name here LDAP_SEARCH_BASE empty => ou=people,dc=domain,dc=com => e.g. LDAP_SEARCH_BASE=dc=mydomain,dc=local LDAP_BIND_DN empty => cn=admin,dc=domain,dc=com => take a look at examples of SASL_LDAP_BIND_DN LDAP_BIND_PW empty => admin => Specify the password to bind against ldap LDAP_QUERY_FILTER_USER e.g. (&(mail=%s)(mailEnabled=TRUE)) => Specify how ldap should be asked for users LDAP_QUERY_FILTER_GROUP e.g. (&(mailGroupMember=%s)(mailEnabled=TRUE)) => Specify how ldap should be asked for groups LDAP_QUERY_FILTER_ALIAS e.g. (&(mailAlias=%s)(mailEnabled=TRUE)) => Specify how ldap should be asked for aliases LDAP_QUERY_FILTER_DOMAIN e.g. (&(|(mail=*@%s)(mailalias=*@%s)(mailGroupMember=*@%s))(mailEnabled=TRUE)) => Specify how ldap should be asked for domains LDAP_QUERY_FILTER_SENDERS empty => use user/alias/group maps directly, equivalent to (|($LDAP_QUERY_FILTER_USER)($LDAP_QUERY_FILTER_ALIAS)($LDAP_QUERY_FILTER_GROUP)) => Override how ldap should be asked if a sender address is allowed for a user DOVECOT_TLS empty => no yes => LDAP over TLS enabled for Dovecot Dovecot The following variables overwrite the default values for /etc/dovecot/dovecot-ldap.conf.ext . DOVECOT_BASE empty => same as LDAP_SEARCH_BASE => Tell Dovecot to search only below this base entry. (e.g. ou=people,dc=domain,dc=com ) DOVECOT_DEFAULT_PASS_SCHEME empty => SSHA => Select one crypt scheme for password hashing from this list of password schemes . DOVECOT_DN empty => same as LDAP_BIND_DN => Bind dn for LDAP connection. (e.g. cn=admin,dc=domain,dc=com ) DOVECOT_DNPASS empty => same as LDAP_BIND_PW => Password for LDAP dn sepecifified in DOVECOT_DN . DOVECOT_URIS empty => same as LDAP_SERVER_HOST => Specify a space separated list of LDAP uris. Note: If the protocol is missing, ldap:// will be used. Note: This deprecates DOVECOT_HOSTS (as it didn't allow to use LDAPS), which is currently still supported for backwards compatibility. DOVECOT_LDAP_VERSION empty => 3 2 => LDAP version 2 is used 3 => LDAP version 3 is used DOVECOT_AUTH_BIND empty => no yes => Enable LDAP authentication binds DOVECOT_USER_FILTER e.g. (&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n)) DOVECOT_USER_ATTRS e.g. homeDirectory=home,qmailUID=uid,qmailGID=gid,mailMessageStore=mail => Specify the directory to dovecot attribute mapping that fits your directory structure. Note: This is necessary for directories that do not use the Postfix Book Schema. Note: The left-hand value is the directory attribute, the right hand value is the dovecot variable. More details on the Dovecot Wiki DOVECOT_PASS_FILTER e.g. (&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n)) empty => same as DOVECOT_USER_FILTER DOVECOT_PASS_ATTRS e.g. uid=user,userPassword=password => Specify the directory to dovecot variable mapping that fits your directory structure. Note: This is necessary for directories that do not use the Postfix Book Schema. Note: The left-hand value is the directory attribute, the right hand value is the dovecot variable. More details on the Dovecot Wiki Postgrey ENABLE_POSTGREY 0 => postgrey is disabled 1 => postgrey is enabled POSTGREY_DELAY 300 => greylist for N seconds Note: This postgrey setting needs ENABLE_POSTGREY=1 POSTGREY_MAX_AGE 35 => delete entries older than N days since the last time that they have been seen Note: This postgrey setting needs ENABLE_POSTGREY=1 POSTGREY_AUTO_WHITELIST_CLIENTS 5 => whitelist host after N successful deliveries (N=0 to disable whitelisting) Note: This postgrey setting needs ENABLE_POSTGREY=1 POSTGREY_TEXT Delayed by Postgrey => response when a mail is greylisted Note: This postgrey setting needs ENABLE_POSTGREY=1 SASL Auth ENABLE_SASLAUTHD 0 => saslauthd is disabled 1 => saslauthd is enabled SASLAUTHD_MECHANISMS empty => pam ldap => authenticate against ldap server shadow => authenticate against local user db mysql => authenticate against mysql db rimap => authenticate against imap server NOTE: can be a list of mechanisms like pam ldap shadow SASLAUTHD_MECH_OPTIONS empty => None e.g. with SASLAUTHD_MECHANISMS rimap you need to specify the ip-address/servername of the imap server ==> xxx.xxx.xxx.xxx SASLAUTHD_LDAP_SERVER empty => same as LDAP_SERVER_HOST Note: since version 10.0.0, you can specify a protocol here (like ldaps://); this deprecates SASLAUTHD_LDAP_SSL. SASLAUTHD_LDAP_START_TLS empty => no yes => Enable ldap_start_tls option SASLAUTHD_LDAP_TLS_CHECK_PEER empty => no yes => Enable ldap_tls_check_peer option SASLAUTHD_LDAP_TLS_CACERT_DIR Path to directory with CA (Certificate Authority) certificates. empty => Nothing is added to the configuration Any value => Fills the ldap_tls_cacert_dir option SASLAUTHD_LDAP_TLS_CACERT_FILE File containing CA (Certificate Authority) certificate(s). empty => Nothing is added to the configuration Any value => Fills the ldap_tls_cacert_file option SASLAUTHD_LDAP_BIND_DN empty => same as LDAP_BIND_DN specify an object with privileges to search the directory tree e.g. active directory: SASLAUTHD_LDAP_BIND_DN=cn=Administrator,cn=Users,dc=mydomain,dc=net e.g. openldap: SASLAUTHD_LDAP_BIND_DN=cn=admin,dc=mydomain,dc=net SASLAUTHD_LDAP_PASSWORD empty => same as LDAP_BIND_PW SASLAUTHD_LDAP_SEARCH_BASE empty => same as LDAP_SEARCH_BASE specify the search base SASLAUTHD_LDAP_FILTER empty => default filter (&(uniqueIdentifier=%u)(mailEnabled=TRUE)) e.g. for active directory: (&(sAMAccountName=%U)(objectClass=person)) e.g. for openldap: (&(uid=%U)(objectClass=person)) SASLAUTHD_LDAP_PASSWORD_ATTR Specify what password attribute to use for password verification. empty => Nothing is added to the configuration but the documentation says it is userPassword by default. Any value => Fills the ldap_password_attr option SASLAUTHD_LDAP_AUTH_METHOD empty => bind will be used as a default value fastbind => The fastbind method is used custom => The custom method uses userPassword attribute to verify the password SASLAUTHD_LDAP_MECH Specify the authentication mechanism for SASL bind. empty => Nothing is added to the configuration Any value => Fills the ldap_mech option SRS (Sender Rewriting Scheme) SRS_SENDER_CLASSES An email has an \"envelope\" sender (indicating the sending server) and a \"header\" sender (indicating who sent it). More strict SPF policies may require you to replace both instead of just the envelope sender. More info . envelope_sender => Rewrite only envelope sender address header_sender => Rewrite only header sender (not recommended) envelope_sender,header_sender => Rewrite both senders SRS_EXCLUDE_DOMAINS empty => Envelope sender will be rewritten for all domains provide comma separated list of domains to exclude from rewriting SRS_SECRET empty => generated when the container is started for the first time provide a secret to use in base64 you may specify multiple keys, comma separated. the first one is used for signing and the remaining will be used for verification. this is how you rotate and expire keys if you have a cluster/swarm make sure the same keys are on all nodes example command to generate a key: dd if=/dev/urandom bs=24 count=1 2>/dev/null | base64 SRS_DOMAINNAME empty => Derived from OVERRIDE_HOSTNAME , $DOMAINNAME (internal), or the container's hostname Set this if auto-detection fails, isn't what you want, or you wish to have a separate container handle DSNs Default Relay Host DEFAULT_RELAY_HOST empty => don't set default relayhost setting in main.cf default host and port to relay all mail through. Format: [example.com]:587 (don't forget the brackets if you need this to be compatible with $RELAY_USER and $RELAY_PASSWORD , explained below). Multi-domain Relay Hosts RELAY_HOST empty => don't configure relay host default host to relay mail through RELAY_PORT empty => 25 default port to relay mail through RELAY_USER empty => no default default relay username (if no specific entry exists in postfix-sasl-password.cf) RELAY_PASSWORD empty => no default password for default relay user","title":"Environment Variables"},{"location":"config/environment/#general","text":"","title":"General"},{"location":"config/environment/#override_hostname","text":"empty => uses the hostname command to get canonical hostname for docker-mailserver to use. => Specify a fully-qualified domainname to serve mail for. This is used for many of the config features so if you can't set your hostname ( eg: you're in a container platform that doesn't let you ) specify it via this environment variable. It will take priority over docker run options: --hostname and --domainname , or docker-compose.yml config equivalents: hostname: and domainname: .","title":"OVERRIDE_HOSTNAME"},{"location":"config/environment/#dms_debug","text":"This environment variable was removed in v11.0.0 ! Use LOG_LEVEL instead.","title":"DMS_DEBUG"},{"location":"config/environment/#log_level","text":"Set the log level for DMS. This is mostly relevant for container startup scripts and change detection event feedback. Valid values (in order of increasing verbosity) are: error , warn , info , debug and trace . The default log level is info .","title":"LOG_LEVEL"},{"location":"config/environment/#supervisor_loglevel","text":"Here you can adjust the log-level for Supervisor . Possible values are critical => Only show critical messages error => Only show erroneous output warn => Show warnings info => Normal informational output debug => Also show debug messages The log-level will show everything in its class and above.","title":"SUPERVISOR_LOGLEVEL"},{"location":"config/environment/#one_dir","text":"0 => state in default directories. 1 => consolidate all states into a single directory ( /var/mail-state ) to allow persistence using docker volumes. See the related FAQ entry for more information.","title":"ONE_DIR"},{"location":"config/environment/#account_provisioner","text":"Configures the provisioning source of user accounts (including aliases) for user queries and authentication by services managed by DMS ( Postfix and Dovecot ). User provisioning via OIDC is planned for the future, see this tracking issue . empty => use FILE LDAP => use LDAP authentication OIDC => use OIDC authentication ( not yet implemented ) FILE => use local files (this is used as the default) A second container for the ldap service is necessary (e.g. docker-openldap )","title":"ACCOUNT_PROVISIONER"},{"location":"config/environment/#permit_docker","text":"Set different options for mynetworks option (can be overwrite in postfix-main.cf) WARNING : Adding the docker network's gateway to the list of trusted hosts, e.g. using the network or connected-networks option, can create an open relay , for instance if IPv6 is enabled on the host machine but not in Docker. none => Explicitly force authentication container => Container IP address only. host => Add docker host (ipv4 only). network => Add the docker default bridge network (172.16.0.0/12); WARNING : docker-compose might use others (e.g. 192.168.0.0/16) use PERMIT_DOCKER=connected-networks in this case. connected-networks => Add all connected docker networks (ipv4 only). Note: you probably want to set POSTFIX_INET_PROTOCOLS=ipv4 to make it work fine with Docker.","title":"PERMIT_DOCKER"},{"location":"config/environment/#tz","text":"Set the timezone. If this variable is unset, the container runtime will try to detect the time using /etc/localtime , which you can alternatively mount into the container. The value of this variable must follow the pattern AREA/ZONE , i.e. of you want to use Germany's time zone, use Europe/Berlin . You can lookup all available timezones here .","title":"TZ"},{"location":"config/environment/#enable_rspamd","text":"Enable or disable Rspamd. Current State Rspamd-support is under active development. Be aware that breaking changes can happen at any time. To get more information, see the detailed documentation page for Rspamd . 0 => disabled 1 => enabled","title":"ENABLE_RSPAMD"},{"location":"config/environment/#enable_redis","text":"Explicit control over running a Redis instance within the container. By default, this value will match what is set for ENABLE_RSPAMD . The purpose of this setting is to opt-out of starting an internal Redis instance when enabling Rspamd, replacing it with your own external instance. Configuring rspamd for an external Redis instance You will need to provide configuration at /etc/rspamd/local.d/redis.conf similar to: servers = \"redis.example.test:6379\"; expand_keys = true; 0 => Disabled 1 => Enabled","title":"ENABLE_REDIS"},{"location":"config/environment/#enable_amavis","text":"Amavis content filter (used for ClamAV & SpamAssassin) 0 => Amavis is disabled 1 => Amavis is enabled","title":"ENABLE_AMAVIS"},{"location":"config/environment/#amavis_loglevel","text":"This page provides information on Amavis' logging statistics. -1/-2/-3 => Only show errors 0 => Show warnings 1/2 => Show default informational output 3/4/5 => log debug information (very verbose)","title":"AMAVIS_LOGLEVEL"},{"location":"config/environment/#enable_dnsbl","text":"This enables DNS block lists in Postscreen . If you want to know which lists we are using, have a look at the default main.cf for Postfix we provide and search for postscreen_dnsbl_sites . A Warning On DNS Block Lists Make sure your DNS queries are properly resolved, i.e. you will most likely not want to use a public DNS resolver as these queries do not return meaningful results. We try our best to only evaluate proper return codes - this is not a guarantee that all codes are handled fine though. Note that emails will be rejected if they don't pass the block list checks! 0 => DNS block lists are disabled 1 => DNS block lists are enabled","title":"ENABLE_DNSBL"},{"location":"config/environment/#enable_opendkim","text":"Enables the OpenDKIM service. 1 => Enabled 0 => Disabled","title":"ENABLE_OPENDKIM"},{"location":"config/environment/#enable_opendmarc","text":"Enables the OpenDMARC service. 1 => Enabled 0 => Disabled","title":"ENABLE_OPENDMARC"},{"location":"config/environment/#enable_pop3","text":"empty => POP3 service disabled 1 => Enables POP3 service","title":"ENABLE_POP3"},{"location":"config/environment/#enable_clamav","text":"0 => ClamAV is disabled 1 => ClamAV is enabled","title":"ENABLE_CLAMAV"},{"location":"config/environment/#enable_fail2ban","text":"0 => fail2ban service disabled 1 => Enables fail2ban service If you enable Fail2Ban, don't forget to add the following lines to your docker-compose.yml : cap_add: - NET_ADMIN Otherwise, nftables won't be able to ban IPs.","title":"ENABLE_FAIL2BAN"},{"location":"config/environment/#fail2ban_blocktype","text":"drop => drop packet (send NO reply) reject => reject packet (send ICMP unreachable) FAIL2BAN_BLOCKTYPE=drop","title":"FAIL2BAN_BLOCKTYPE"},{"location":"config/environment/#smtp_only","text":"empty => all daemons start 1 => only launch postfix smtp","title":"SMTP_ONLY"},{"location":"config/environment/#ssl_type","text":"In the majority of cases, you want letsencrypt or manual . self-signed can be used for testing SSL until you provide a valid certificate, note that third-parties cannot trust self-signed certificates, do not use this type in production. custom is a temporary workaround that is not officially supported. empty => SSL disabled. letsencrypt => Support for using certificates with Let's Encrypt provisioners. (Docs: Let's Encrypt Setup ) manual => Provide your own certificate via separate key and cert files. (Docs: Bring Your Own Certificates ) Requires: SSL_CERT_PATH and SSL_KEY_PATH ENV vars to be set to the location of the files within the container. Optional: SSL_ALT_CERT_PATH and SSL_ALT_KEY_PATH allow providing a 2nd certificate as a fallback for dual (aka hybrid) certificate support. Useful for ECDSA with an RSA fallback. Presently only manual mode supports this feature . custom => Provide your own certificate as a single file containing both the private key and full certificate chain. (Docs: None ) self-signed => Provide your own self-signed certificate files. Expects a self-signed CA cert for verification. Use only for local testing of your setup . (Docs: Self-Signed Certificates ) Please read the SSL page in the documentation for more information.","title":"SSL_TYPE"},{"location":"config/environment/#tls_level","text":"empty => modern modern => Enables TLSv1.2 and modern ciphers only. (default) intermediate => Enables TLSv1, TLSv1.1 and TLSv1.2 and broad compatibility ciphers.","title":"TLS_LEVEL"},{"location":"config/environment/#spoof_protection","text":"Configures the handling of creating mails with forged sender addresses. empty => Mail address spoofing allowed. Any logged in user may create email messages with a forged sender address. See also Wikipedia (not recommended, but default for backwards compatibility reasons) 1 => (recommended) Mail spoofing denied. Each user may only send with his own or his alias addresses. Addresses with extension delimiters are not able to send messages.","title":"SPOOF_PROTECTION"},{"location":"config/environment/#enable_srs","text":"Enables the Sender Rewriting Scheme. SRS is needed if docker-mailserver acts as forwarder. See postsrsd for further explanation. 0 => Disabled 1 => Enabled","title":"ENABLE_SRS"},{"location":"config/environment/#network_interface","text":"In case your network interface differs from eth0 , e.g. when you are using HostNetworking in Kubernetes, you can set this to whatever interface you want. This interface will then be used. empty => eth0","title":"NETWORK_INTERFACE"},{"location":"config/environment/#virusmails_delete_delay","text":"Set how many days a virusmail will stay on the server before being deleted empty => 7 days","title":"VIRUSMAILS_DELETE_DELAY"},{"location":"config/environment/#postfix_dagent","text":"Configure Postfix virtual_transport to deliver mail to a different LMTP client ( default is a unix socket to dovecot ). Provide any valid URI. Examples: empty => lmtp:unix:/var/run/dovecot/lmtp (default, configured in Postfix main.cf ) lmtp:unix:private/dovecot-lmtp (use socket) lmtps:inet:: (secure lmtp with starttls) lmtp::2003 (use kopano as mailstore)","title":"POSTFIX_DAGENT"},{"location":"config/environment/#postfix_mailbox_size_limit","text":"Set the mailbox size limit for all users. If set to zero, the size will be unlimited (default). empty => 0 (no limit)","title":"POSTFIX_MAILBOX_SIZE_LIMIT"},{"location":"config/environment/#enable_quotas","text":"1 => Dovecot quota is enabled 0 => Dovecot quota is disabled See mailbox quota .","title":"ENABLE_QUOTAS"},{"location":"config/environment/#postfix_message_size_limit","text":"Set the message size limit for all users. If set to zero, the size will be unlimited (not recommended!) empty => 10240000 (~10 MB)","title":"POSTFIX_MESSAGE_SIZE_LIMIT"},{"location":"config/environment/#clamav_message_size_limit","text":"Mails larger than this limit won't be scanned. ClamAV must be enabled (ENABLE_CLAMAV=1) for this. empty => 25M (25 MB)","title":"CLAMAV_MESSAGE_SIZE_LIMIT"},{"location":"config/environment/#enable_managesieve","text":"empty => Managesieve service disabled 1 => Enables Managesieve on port 4190","title":"ENABLE_MANAGESIEVE"},{"location":"config/environment/#postmaster_address","text":"empty => postmaster@example.com => Specify the postmaster address","title":"POSTMASTER_ADDRESS"},{"location":"config/environment/#enable_update_check","text":"Check for updates on container start and then once a day. If an update is available, a mail is send to POSTMASTER_ADDRESS. 0 => Update check disabled 1 => Update check enabled","title":"ENABLE_UPDATE_CHECK"},{"location":"config/environment/#update_check_interval","text":"Customize the update check interval. Number + Suffix. Suffix must be 's' for seconds, 'm' for minutes, 'h' for hours or 'd' for days. 1d => Check for updates once a day","title":"UPDATE_CHECK_INTERVAL"},{"location":"config/environment/#postscreen_action","text":"enforce => Allow other tests to complete. Reject attempts to deliver mail with a 550 SMTP reply, and log the helo/sender/recipient information. Repeat this test the next time the client connects. drop => Drop the connection immediately with a 521 SMTP reply. Repeat this test the next time the client connects. ignore => Ignore the failure of this test. Allow other tests to complete. Repeat this test the next time the client connects. This option is useful for testing and collecting statistics without blocking mail.","title":"POSTSCREEN_ACTION"},{"location":"config/environment/#dovecot_mailbox_format","text":"maildir => uses very common Maildir format, one file contains one message sdbox => (experimental) uses Dovecot high-performance mailbox format, one file contains one message mdbox ==> (experimental) uses Dovecot high-performance mailbox format, multiple messages per file and multiple files per box This option has been added in November 2019. Using other format than Maildir is considered as experimental in docker-mailserver and should only be used for testing purpose. For more details, please refer to Dovecot Documentation .","title":"DOVECOT_MAILBOX_FORMAT"},{"location":"config/environment/#postfix_inet_protocols","text":"all => Listen on all interfaces. ipv4 => Listen only on IPv4 interfaces. Most likely you want this behind Docker. ipv6 => Listen only on IPv6 interfaces. Note: More details at http://www.postfix.org/postconf.5.html#inet_protocols","title":"POSTFIX_INET_PROTOCOLS"},{"location":"config/environment/#dovecot_inet_protocols","text":"all => Listen on all interfaces ipv4 => Listen only on IPv4 interfaces. Most likely you want this behind Docker. ipv6 => Listen only on IPv6 interfaces. Note: More information at https://dovecot.org/doc/dovecot-example.conf","title":"DOVECOT_INET_PROTOCOLS"},{"location":"config/environment/#reports","text":"","title":"Reports"},{"location":"config/environment/#pflogsumm_trigger","text":"Enables regular Postfix log summary (\"pflogsumm\") mail reports. not set => No report daily_cron => Daily report for the previous day logrotate => Full report based on the mail log when it is rotated This is a new option. The old REPORT options are still supported for backwards compatibility. If this is not set and reports are enabled with the old options, logrotate will be used.","title":"PFLOGSUMM_TRIGGER"},{"location":"config/environment/#pflogsumm_recipient","text":"Recipient address for Postfix log summary reports. not set => Use POSTMASTER_ADDRESS => Specify the recipient address(es)","title":"PFLOGSUMM_RECIPIENT"},{"location":"config/environment/#pflogsumm_sender","text":"Sender address ( FROM ) for pflogsumm reports (if Postfix log summary reports are enabled). not set => Use REPORT_SENDER => Specify the sender address","title":"PFLOGSUMM_SENDER"},{"location":"config/environment/#logwatch_interval","text":"Interval for logwatch report. none => No report is generated daily => Send a daily report weekly => Send a report every week","title":"LOGWATCH_INTERVAL"},{"location":"config/environment/#logwatch_recipient","text":"Recipient address for logwatch reports if they are enabled. not set => Use REPORT_RECIPIENT or POSTMASTER_ADDRESS => Specify the recipient address(es)","title":"LOGWATCH_RECIPIENT"},{"location":"config/environment/#logwatch_sender","text":"Sender address ( FROM ) for logwatch reports if logwatch reports are enabled. not set => Use REPORT_SENDER => Specify the sender address","title":"LOGWATCH_SENDER"},{"location":"config/environment/#report_recipient","text":"Defines who receives reports (if they are enabled). empty => Use POSTMASTER_ADDRESS => Specify the recipient address","title":"REPORT_RECIPIENT"},{"location":"config/environment/#report_sender","text":"Defines who sends reports (if they are enabled). empty => mailserver-report@ => Specify the sender address","title":"REPORT_SENDER"},{"location":"config/environment/#logrotate_interval","text":"Changes the interval in which log files are rotated. weekly => Rotate log files weekly daily => Rotate log files daily monthly => Rotate log files monthly Note LOGROTATE_INTERVAL only manages logrotate within the container for services we manage internally. The entire log output for the container is still available via docker logs mailserver (or your respective container name). If you want to configure external log rotation for that container output as well, : Docker Logging Drivers . By default, the logs are lost when the container is destroyed (eg: re-creating via docker-compose down && docker-compose up -d ). To keep the logs, mount a volume (to /var/log/mail/ ). Note This variable can also determine the interval for Postfix's log summary reports, see PFLOGSUMM_TRIGGER .","title":"LOGROTATE_INTERVAL"},{"location":"config/environment/#spamassassin","text":"","title":"SpamAssassin"},{"location":"config/environment/#enable_spamassassin","text":"0 => SpamAssassin is disabled 1 => SpamAssassin is enabled","title":"ENABLE_SPAMASSASSIN"},{"location":"config/environment/#spamassassin_spam_to_inbox","text":"0 => Spam messages will be bounced ( rejected ) without any notification ( dangerous ). 1 => Spam messages will be delivered to the inbox and tagged as spam using SA_SPAM_SUBJECT .","title":"SPAMASSASSIN_SPAM_TO_INBOX"},{"location":"config/environment/#enable_spamassassin_kam","text":"KAM is a 3rd party SpamAssassin ruleset, provided by the McGrail Foundation. If SpamAssassin is enabled, KAM can be used in addition to the default ruleset. 0 => KAM disabled 1 => KAM enabled","title":"ENABLE_SPAMASSASSIN_KAM"},{"location":"config/environment/#move_spam_to_junk","text":"Spam messages can be moved in the Junk folder. Note: this setting needs SPAMASSASSIN_SPAM_TO_INBOX=1 0 => Spam messages will be delivered in the mailbox. 1 => Spam messages will be delivered in the Junk folder.","title":"MOVE_SPAM_TO_JUNK"},{"location":"config/environment/#sa_tag","text":"2.0 => add spam info headers if at, or above that level Note: this SpamAssassin setting needs ENABLE_SPAMASSASSIN=1","title":"SA_TAG"},{"location":"config/environment/#sa_tag2","text":"6.31 => add 'spam detected' headers at that level Note: this SpamAssassin setting needs ENABLE_SPAMASSASSIN=1","title":"SA_TAG2"},{"location":"config/environment/#sa_kill","text":"6.31 => triggers spam evasive actions This SpamAssassin setting needs ENABLE_SPAMASSASSIN=1 By default, docker-mailserver is configured to quarantine spam emails. If emails are quarantined, they are compressed and stored in a location dependent on the ONE_DIR setting above. To inhibit this behaviour and deliver spam emails, set this to a very high value e.g. 100.0 . If ONE_DIR=1 (default) the location is /var/mail-state/lib-amavis/virusmails/ , or if ONE_DIR=0 : /var/lib/amavis/virusmails/ . These paths are inside the docker container.","title":"SA_KILL"},{"location":"config/environment/#sa_spam_subject","text":"***SPAM*** => add tag to subject if spam detected Note: this SpamAssassin setting needs ENABLE_SPAMASSASSIN=1 . Add the SpamAssassin score to the subject line by inserting the keyword _SCORE_: ***SPAM(_SCORE_)*** .","title":"SA_SPAM_SUBJECT"},{"location":"config/environment/#sa_shortcircuit_bayes_spam","text":"1 => will activate SpamAssassin short circuiting for bayes spam detection. This will uncomment the respective line in /etc/spamassasin/local.cf Note: activate this only if you are confident in your bayes database for identifying spam.","title":"SA_SHORTCIRCUIT_BAYES_SPAM"},{"location":"config/environment/#sa_shortcircuit_bayes_ham","text":"1 => will activate SpamAssassin short circuiting for bayes ham detection This will uncomment the respective line in /etc/spamassasin/local.cf Note: activate this only if you are confident in your bayes database for identifying ham.","title":"SA_SHORTCIRCUIT_BAYES_HAM"},{"location":"config/environment/#fetchmail","text":"","title":"Fetchmail"},{"location":"config/environment/#enable_fetchmail","text":"0 => fetchmail disabled 1 => fetchmail enabled","title":"ENABLE_FETCHMAIL"},{"location":"config/environment/#fetchmail_poll","text":"300 => fetchmail The number of seconds for the interval","title":"FETCHMAIL_POLL"},{"location":"config/environment/#fetchmail_parallel","text":"0 => fetchmail runs with a single config file /etc/fetchmailrc 1 => /etc/fetchmailrc is split per poll entry. For every poll entry a separate fetchmail instance is started to allow having multiple imap idle configurations defined. Note: The defaults of your fetchmailrc file need to be at the top of the file. Otherwise it won't be added correctly to all separate fetchmail instances.","title":"FETCHMAIL_PARALLEL"},{"location":"config/environment/#ldap","text":"","title":"LDAP"},{"location":"config/environment/#enable_ldap","text":"Deprecated. See ACCOUNT_PROVISIONER .","title":"ENABLE_LDAP"},{"location":"config/environment/#ldap_start_tls","text":"empty => no yes => LDAP over TLS enabled for Postfix","title":"LDAP_START_TLS"},{"location":"config/environment/#ldap_server_host","text":"empty => mail.example.com => Specify the dns-name/ip-address where the ldap-server is listening, or an URI like ldaps://mail.example.com NOTE: If you going to use docker-mailserver in combination with docker-compose.yml you can set the service name here","title":"LDAP_SERVER_HOST"},{"location":"config/environment/#ldap_search_base","text":"empty => ou=people,dc=domain,dc=com => e.g. LDAP_SEARCH_BASE=dc=mydomain,dc=local","title":"LDAP_SEARCH_BASE"},{"location":"config/environment/#ldap_bind_dn","text":"empty => cn=admin,dc=domain,dc=com => take a look at examples of SASL_LDAP_BIND_DN","title":"LDAP_BIND_DN"},{"location":"config/environment/#ldap_bind_pw","text":"empty => admin => Specify the password to bind against ldap","title":"LDAP_BIND_PW"},{"location":"config/environment/#ldap_query_filter_user","text":"e.g. (&(mail=%s)(mailEnabled=TRUE)) => Specify how ldap should be asked for users","title":"LDAP_QUERY_FILTER_USER"},{"location":"config/environment/#ldap_query_filter_group","text":"e.g. (&(mailGroupMember=%s)(mailEnabled=TRUE)) => Specify how ldap should be asked for groups","title":"LDAP_QUERY_FILTER_GROUP"},{"location":"config/environment/#ldap_query_filter_alias","text":"e.g. (&(mailAlias=%s)(mailEnabled=TRUE)) => Specify how ldap should be asked for aliases","title":"LDAP_QUERY_FILTER_ALIAS"},{"location":"config/environment/#ldap_query_filter_domain","text":"e.g. (&(|(mail=*@%s)(mailalias=*@%s)(mailGroupMember=*@%s))(mailEnabled=TRUE)) => Specify how ldap should be asked for domains","title":"LDAP_QUERY_FILTER_DOMAIN"},{"location":"config/environment/#ldap_query_filter_senders","text":"empty => use user/alias/group maps directly, equivalent to (|($LDAP_QUERY_FILTER_USER)($LDAP_QUERY_FILTER_ALIAS)($LDAP_QUERY_FILTER_GROUP)) => Override how ldap should be asked if a sender address is allowed for a user","title":"LDAP_QUERY_FILTER_SENDERS"},{"location":"config/environment/#dovecot_tls","text":"empty => no yes => LDAP over TLS enabled for Dovecot","title":"DOVECOT_TLS"},{"location":"config/environment/#dovecot","text":"The following variables overwrite the default values for /etc/dovecot/dovecot-ldap.conf.ext .","title":"Dovecot"},{"location":"config/environment/#dovecot_base","text":"empty => same as LDAP_SEARCH_BASE => Tell Dovecot to search only below this base entry. (e.g. ou=people,dc=domain,dc=com )","title":"DOVECOT_BASE"},{"location":"config/environment/#dovecot_default_pass_scheme","text":"empty => SSHA => Select one crypt scheme for password hashing from this list of password schemes .","title":"DOVECOT_DEFAULT_PASS_SCHEME"},{"location":"config/environment/#dovecot_dn","text":"empty => same as LDAP_BIND_DN => Bind dn for LDAP connection. (e.g. cn=admin,dc=domain,dc=com )","title":"DOVECOT_DN"},{"location":"config/environment/#dovecot_dnpass","text":"empty => same as LDAP_BIND_PW => Password for LDAP dn sepecifified in DOVECOT_DN .","title":"DOVECOT_DNPASS"},{"location":"config/environment/#dovecot_uris","text":"empty => same as LDAP_SERVER_HOST => Specify a space separated list of LDAP uris. Note: If the protocol is missing, ldap:// will be used. Note: This deprecates DOVECOT_HOSTS (as it didn't allow to use LDAPS), which is currently still supported for backwards compatibility.","title":"DOVECOT_URIS"},{"location":"config/environment/#dovecot_ldap_version","text":"empty => 3 2 => LDAP version 2 is used 3 => LDAP version 3 is used","title":"DOVECOT_LDAP_VERSION"},{"location":"config/environment/#dovecot_auth_bind","text":"empty => no yes => Enable LDAP authentication binds","title":"DOVECOT_AUTH_BIND"},{"location":"config/environment/#dovecot_user_filter","text":"e.g. (&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n))","title":"DOVECOT_USER_FILTER"},{"location":"config/environment/#dovecot_user_attrs","text":"e.g. homeDirectory=home,qmailUID=uid,qmailGID=gid,mailMessageStore=mail => Specify the directory to dovecot attribute mapping that fits your directory structure. Note: This is necessary for directories that do not use the Postfix Book Schema. Note: The left-hand value is the directory attribute, the right hand value is the dovecot variable. More details on the Dovecot Wiki","title":"DOVECOT_USER_ATTRS"},{"location":"config/environment/#dovecot_pass_filter","text":"e.g. (&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n)) empty => same as DOVECOT_USER_FILTER","title":"DOVECOT_PASS_FILTER"},{"location":"config/environment/#dovecot_pass_attrs","text":"e.g. uid=user,userPassword=password => Specify the directory to dovecot variable mapping that fits your directory structure. Note: This is necessary for directories that do not use the Postfix Book Schema. Note: The left-hand value is the directory attribute, the right hand value is the dovecot variable. More details on the Dovecot Wiki","title":"DOVECOT_PASS_ATTRS"},{"location":"config/environment/#postgrey","text":"","title":"Postgrey"},{"location":"config/environment/#enable_postgrey","text":"0 => postgrey is disabled 1 => postgrey is enabled","title":"ENABLE_POSTGREY"},{"location":"config/environment/#postgrey_delay","text":"300 => greylist for N seconds Note: This postgrey setting needs ENABLE_POSTGREY=1","title":"POSTGREY_DELAY"},{"location":"config/environment/#postgrey_max_age","text":"35 => delete entries older than N days since the last time that they have been seen Note: This postgrey setting needs ENABLE_POSTGREY=1","title":"POSTGREY_MAX_AGE"},{"location":"config/environment/#postgrey_auto_whitelist_clients","text":"5 => whitelist host after N successful deliveries (N=0 to disable whitelisting) Note: This postgrey setting needs ENABLE_POSTGREY=1","title":"POSTGREY_AUTO_WHITELIST_CLIENTS"},{"location":"config/environment/#postgrey_text","text":"Delayed by Postgrey => response when a mail is greylisted Note: This postgrey setting needs ENABLE_POSTGREY=1","title":"POSTGREY_TEXT"},{"location":"config/environment/#sasl-auth","text":"","title":"SASL Auth"},{"location":"config/environment/#enable_saslauthd","text":"0 => saslauthd is disabled 1 => saslauthd is enabled","title":"ENABLE_SASLAUTHD"},{"location":"config/environment/#saslauthd_mechanisms","text":"empty => pam ldap => authenticate against ldap server shadow => authenticate against local user db mysql => authenticate against mysql db rimap => authenticate against imap server NOTE: can be a list of mechanisms like pam ldap shadow","title":"SASLAUTHD_MECHANISMS"},{"location":"config/environment/#saslauthd_mech_options","text":"empty => None e.g. with SASLAUTHD_MECHANISMS rimap you need to specify the ip-address/servername of the imap server ==> xxx.xxx.xxx.xxx","title":"SASLAUTHD_MECH_OPTIONS"},{"location":"config/environment/#saslauthd_ldap_server","text":"empty => same as LDAP_SERVER_HOST Note: since version 10.0.0, you can specify a protocol here (like ldaps://); this deprecates SASLAUTHD_LDAP_SSL.","title":"SASLAUTHD_LDAP_SERVER"},{"location":"config/environment/#saslauthd_ldap_start_tls","text":"empty => no yes => Enable ldap_start_tls option","title":"SASLAUTHD_LDAP_START_TLS"},{"location":"config/environment/#saslauthd_ldap_tls_check_peer","text":"empty => no yes => Enable ldap_tls_check_peer option","title":"SASLAUTHD_LDAP_TLS_CHECK_PEER"},{"location":"config/environment/#saslauthd_ldap_tls_cacert_dir","text":"Path to directory with CA (Certificate Authority) certificates. empty => Nothing is added to the configuration Any value => Fills the ldap_tls_cacert_dir option","title":"SASLAUTHD_LDAP_TLS_CACERT_DIR"},{"location":"config/environment/#saslauthd_ldap_tls_cacert_file","text":"File containing CA (Certificate Authority) certificate(s). empty => Nothing is added to the configuration Any value => Fills the ldap_tls_cacert_file option","title":"SASLAUTHD_LDAP_TLS_CACERT_FILE"},{"location":"config/environment/#saslauthd_ldap_bind_dn","text":"empty => same as LDAP_BIND_DN specify an object with privileges to search the directory tree e.g. active directory: SASLAUTHD_LDAP_BIND_DN=cn=Administrator,cn=Users,dc=mydomain,dc=net e.g. openldap: SASLAUTHD_LDAP_BIND_DN=cn=admin,dc=mydomain,dc=net","title":"SASLAUTHD_LDAP_BIND_DN"},{"location":"config/environment/#saslauthd_ldap_password","text":"empty => same as LDAP_BIND_PW","title":"SASLAUTHD_LDAP_PASSWORD"},{"location":"config/environment/#saslauthd_ldap_search_base","text":"empty => same as LDAP_SEARCH_BASE specify the search base","title":"SASLAUTHD_LDAP_SEARCH_BASE"},{"location":"config/environment/#saslauthd_ldap_filter","text":"empty => default filter (&(uniqueIdentifier=%u)(mailEnabled=TRUE)) e.g. for active directory: (&(sAMAccountName=%U)(objectClass=person)) e.g. for openldap: (&(uid=%U)(objectClass=person))","title":"SASLAUTHD_LDAP_FILTER"},{"location":"config/environment/#saslauthd_ldap_password_attr","text":"Specify what password attribute to use for password verification. empty => Nothing is added to the configuration but the documentation says it is userPassword by default. Any value => Fills the ldap_password_attr option","title":"SASLAUTHD_LDAP_PASSWORD_ATTR"},{"location":"config/environment/#saslauthd_ldap_auth_method","text":"empty => bind will be used as a default value fastbind => The fastbind method is used custom => The custom method uses userPassword attribute to verify the password","title":"SASLAUTHD_LDAP_AUTH_METHOD"},{"location":"config/environment/#saslauthd_ldap_mech","text":"Specify the authentication mechanism for SASL bind. empty => Nothing is added to the configuration Any value => Fills the ldap_mech option","title":"SASLAUTHD_LDAP_MECH"},{"location":"config/environment/#srs-sender-rewriting-scheme","text":"","title":"SRS (Sender Rewriting Scheme)"},{"location":"config/environment/#srs_sender_classes","text":"An email has an \"envelope\" sender (indicating the sending server) and a \"header\" sender (indicating who sent it). More strict SPF policies may require you to replace both instead of just the envelope sender. More info . envelope_sender => Rewrite only envelope sender address header_sender => Rewrite only header sender (not recommended) envelope_sender,header_sender => Rewrite both senders","title":"SRS_SENDER_CLASSES"},{"location":"config/environment/#srs_exclude_domains","text":"empty => Envelope sender will be rewritten for all domains provide comma separated list of domains to exclude from rewriting","title":"SRS_EXCLUDE_DOMAINS"},{"location":"config/environment/#srs_secret","text":"empty => generated when the container is started for the first time provide a secret to use in base64 you may specify multiple keys, comma separated. the first one is used for signing and the remaining will be used for verification. this is how you rotate and expire keys if you have a cluster/swarm make sure the same keys are on all nodes example command to generate a key: dd if=/dev/urandom bs=24 count=1 2>/dev/null | base64","title":"SRS_SECRET"},{"location":"config/environment/#srs_domainname","text":"empty => Derived from OVERRIDE_HOSTNAME , $DOMAINNAME (internal), or the container's hostname Set this if auto-detection fails, isn't what you want, or you wish to have a separate container handle DSNs","title":"SRS_DOMAINNAME"},{"location":"config/environment/#default-relay-host","text":"","title":"Default Relay Host"},{"location":"config/environment/#default_relay_host","text":"empty => don't set default relayhost setting in main.cf default host and port to relay all mail through. Format: [example.com]:587 (don't forget the brackets if you need this to be compatible with $RELAY_USER and $RELAY_PASSWORD , explained below).","title":"DEFAULT_RELAY_HOST"},{"location":"config/environment/#multi-domain-relay-hosts","text":"","title":"Multi-domain Relay Hosts"},{"location":"config/environment/#relay_host","text":"empty => don't configure relay host default host to relay mail through","title":"RELAY_HOST"},{"location":"config/environment/#relay_port","text":"empty => 25 default port to relay mail through","title":"RELAY_PORT"},{"location":"config/environment/#relay_user","text":"empty => no default default relay username (if no specific entry exists in postfix-sasl-password.cf)","title":"RELAY_USER"},{"location":"config/environment/#relay_password","text":"empty => no default password for default relay user","title":"RELAY_PASSWORD"},{"location":"config/pop3/","text":"If you want to use POP3(S), you have to add the ports 110 and/or 995 (TLS secured) and the environment variable ENABLE_POP3 to your docker-compose.yml : mailserver : ports : - \"25:25\" # SMTP (explicit TLS => STARTTLS) - \"143:143\" # IMAP4 (explicit TLS => STARTTLS) - \"465:465\" # ESMTP (implicit TLS) - \"587:587\" # ESMTP (explicit TLS => STARTTLS) - \"993:993\" # IMAP4 (implicit TLS) - \"110:110\" # POP3 - \"995:995\" # POP3 (with TLS) environment : - ENABLE_POP3=1","title":"Mail Delivery with POP3"},{"location":"config/setup.sh/","text":"setup.sh is an administration script that helps with the most common tasks, including initial configuration. It is intended to be run from the host machine, not from inside your running container. The latest version of the script is included in the docker-mailserver repository. You may retrieve it at any time by running this command in your console: wget https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/setup.sh chmod a+x ./setup.sh setup.sh for docker-mailserver version v10.1.x and below If you're using docker-mailserver version v10.1.x or below, you will need to get setup.sh with a specific version. Substitute with the tagged release version that you're using: wget https://raw.githubusercontent.com/docker-mailserver/docker-mailserver//setup.sh . Usage Run ./setup.sh help and you'll get all you have ever wanted some usage information: SETUP(1) NAME setup.sh - docker-mailserver administration script SYNOPSIS ./setup.sh [ OPTIONS... ] COMMAND [ help | ARGUMENTS... ] COMMAND := { email | alias | quota | config | relay | debug } SUBCOMMAND DESCRIPTION This is the main administration script that you use for all your interactions with 'docker-mailserver'. Setup, configuration and much more is done with this script. Please note that the script executes most of the commands inside the container itself. If the image was not found, this script will pull the ':latest' tag of 'mailserver/docker-mailserver'. This tag refers to the latest release, see the tagging convention in the README under https://github.com/docker-mailserver/docker-mailserver/blob/master/README.md You will be able to see detailed information about the script you're invoking and its arguments by appending help after your command. Currently, this does not work with all scripts. [SUB]COMMANDS COMMAND email := ./setup.sh email add [] ./setup.sh email update [] ./setup.sh email del [ OPTIONS... ] [ ... ] ./setup.sh email restrict [] ./setup.sh email list COMMAND alias := ./setup.sh alias add ./setup.sh alias del ./setup.sh alias list COMMAND quota := ./setup.sh quota set [] ./setup.sh quota del COMMAND config := ./setup.sh config dkim [ ARGUMENTS... ] COMMAND relay := ./setup.sh relay add-auth [] ./setup.sh relay add-domain [] ./setup.sh relay exclude-domain COMMAND fail2ban = ./setup.sh fail2ban ./setup.sh fail2ban ban ./setup.sh fail2ban unban COMMAND debug := ./setup.sh debug fetchmail ./setup.sh debug login ./setup.sh debug show-mail-logs EXAMPLES ./setup.sh email add test@example.com [password] Add the email account test@example.com. You will be prompted to input a password afterwards if no password was supplied. When supplying `[password]`, it should be in plaintext. ./setup.sh config dkim keysize 2048 domain 'example.com,not-example.com' Creates keys of length 2048 but in an LDAP setup where domains are not known to Postfix by default, so you need to provide them yourself in a comma-separated list. ./setup.sh config dkim help This will provide you with a detailed explanation on how to use the config dkim command, showing what arguments can be passed and what they do. OPTIONS Config path, container or image adjustments -i IMAGE_NAME Provides the name of the 'docker-mailserver' image. The default value is 'docker.io/mailserver/docker-mailserver:latest' -c CONTAINER_NAME Provides the name of the running container. -p PATH Provides the config folder path to the temporary container (does not work if a 'docker-mailserver' container already exists). SELinux -z Allows container access to the bind mount content that is shared among multiple containers on a SELinux-enabled host. -Z Allows container access to the bind mount content that is private and unshared with other containers on a SELinux-enabled host. EXIT STATUS Exit status is 0 if the command was successful. If there was an unexpected error, an error message is shown describing the error. In case of an error, the script will exit with exit status 1.","title":"Your Best Friend setup.sh"},{"location":"config/setup.sh/#usage","text":"Run ./setup.sh help and you'll get all you have ever wanted some usage information: SETUP(1) NAME setup.sh - docker-mailserver administration script SYNOPSIS ./setup.sh [ OPTIONS... ] COMMAND [ help | ARGUMENTS... ] COMMAND := { email | alias | quota | config | relay | debug } SUBCOMMAND DESCRIPTION This is the main administration script that you use for all your interactions with 'docker-mailserver'. Setup, configuration and much more is done with this script. Please note that the script executes most of the commands inside the container itself. If the image was not found, this script will pull the ':latest' tag of 'mailserver/docker-mailserver'. This tag refers to the latest release, see the tagging convention in the README under https://github.com/docker-mailserver/docker-mailserver/blob/master/README.md You will be able to see detailed information about the script you're invoking and its arguments by appending help after your command. Currently, this does not work with all scripts. [SUB]COMMANDS COMMAND email := ./setup.sh email add [] ./setup.sh email update [] ./setup.sh email del [ OPTIONS... ] [ ... ] ./setup.sh email restrict [] ./setup.sh email list COMMAND alias := ./setup.sh alias add ./setup.sh alias del ./setup.sh alias list COMMAND quota := ./setup.sh quota set [] ./setup.sh quota del COMMAND config := ./setup.sh config dkim [ ARGUMENTS... ] COMMAND relay := ./setup.sh relay add-auth [] ./setup.sh relay add-domain [] ./setup.sh relay exclude-domain COMMAND fail2ban = ./setup.sh fail2ban ./setup.sh fail2ban ban ./setup.sh fail2ban unban COMMAND debug := ./setup.sh debug fetchmail ./setup.sh debug login ./setup.sh debug show-mail-logs EXAMPLES ./setup.sh email add test@example.com [password] Add the email account test@example.com. You will be prompted to input a password afterwards if no password was supplied. When supplying `[password]`, it should be in plaintext. ./setup.sh config dkim keysize 2048 domain 'example.com,not-example.com' Creates keys of length 2048 but in an LDAP setup where domains are not known to Postfix by default, so you need to provide them yourself in a comma-separated list. ./setup.sh config dkim help This will provide you with a detailed explanation on how to use the config dkim command, showing what arguments can be passed and what they do. OPTIONS Config path, container or image adjustments -i IMAGE_NAME Provides the name of the 'docker-mailserver' image. The default value is 'docker.io/mailserver/docker-mailserver:latest' -c CONTAINER_NAME Provides the name of the running container. -p PATH Provides the config folder path to the temporary container (does not work if a 'docker-mailserver' container already exists). SELinux -z Allows container access to the bind mount content that is shared among multiple containers on a SELinux-enabled host. -Z Allows container access to the bind mount content that is private and unshared with other containers on a SELinux-enabled host. EXIT STATUS Exit status is 0 if the command was successful. If there was an unexpected error, an error message is shown describing the error. In case of an error, the script will exit with exit status 1.","title":"Usage"},{"location":"config/advanced/auth-ldap/","text":"Introduction Getting started with ldap and docker-mailserver we need to take 3 parts in account: postfix for incoming & outgoing email dovecot for accessing mailboxes saslauthd for SMTP authentication (this can also be delegated to dovecot) Variables to Control Provisioning by the Container Have a look at the ENV page for information on the default values. LDAP_QUERY_FILTER_* Those variables contain the LDAP lookup filters for postfix, using %s as the placeholder for the domain or email address in question. This means that... ...for incoming email, the domain must return an entry for the DOMAIN filter (see virtual_alias_domains ). ...for incoming email, the inboxes which receive the email are chosen by the USER , ALIAS and GROUP filters. The USER filter specifies personal mailboxes, for which only one should exist per address, for example (mail=%s) (also see virtual_mailbox_maps ) The ALIAS filter specifies aliases for mailboxes, using virtual_alias_maps , for example (mailAlias=%s) The GROUP filter specifies the personal mailboxes in a group (for emails that multiple people shall receive), using virtual_alias_maps , for example (mailGroupMember=%s) . Technically, there is no difference between ALIAS and GROUP , but ideally you should use ALIAS for personal aliases for a singular person (like ceo@example.org ) and GROUP for multiple people (like hr@example.org ). ...for outgoing email, the sender address is put through the SENDERS filter, and only if the authenticated user is one of the returned entries, the email can be sent. This only applies if SPOOF_PROTECTION=1 . If the SENDERS filter is missing, the USER , ALIAS and GROUP filters will be used in in a disjunction (OR). To for example allow users from the admin group to spoof any sender email address, and to force everyone else to only use their personal mailbox address for outgoing email, you can use something like this: (|(memberOf=cn=admin,*)(mail=%s)) Example A really simple LDAP_QUERY_FILTER configuration, using only the user filter and allowing only admin@* to spoof any sender addresses. - ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER - LDAP_START_TLS=yes - ACCOUNT_PROVISIONER=LDAP - LDAP_SERVER_HOST=ldap.example.org - LDAP_SEARCH_BASE=dc=example,dc=org\" - LDAP_BIND_DN=cn=admin,dc=example,dc=org - LDAP_BIND_PW=mypassword - SPOOF_PROTECTION=1 - LDAP_QUERY_FILTER_DOMAIN=(mail=*@%s) - LDAP_QUERY_FILTER_USER=(mail=%s) - LDAP_QUERY_FILTER_ALIAS=(|) # doesn't match anything - LDAP_QUERY_FILTER_GROUP=(|) # doesn't match anything - LDAP_QUERY_FILTER_SENDERS=(|(mail=%s)(mail=admin@*)) DOVECOT_*_FILTER & DOVECOT_*_ATTRS These variables specify the LDAP filters that dovecot uses to determine if a user can log in to their IMAP account, and which mailbox is responsible to receive email for a specific postfix user. This is split into the following two lookups, both using %u as the placeholder for the full login name ( see dovecot documentation for a full list of placeholders ). Usually you only need to set DOVECOT_USER_FILTER , in which case it will be used for both filters. DOVECOT_USER_FILTER is used to get the account details (uid, gid, home directory, quota, ...) of a user. DOVECOT_PASS_FILTER is used to get the password information of the user, and is in pretty much all cases identical to DOVECOT_USER_FILTER (which is the default behaviour if left away). If your directory doesn't have the postfix-book schema installed, then you must change the internal attribute handling for dovecot. For this you have to change the pass_attr and the user_attr mapping, as shown in the example below: - DOVECOT_PASS_ATTRS==user,=password - DOVECOT_USER_ATTRS==home,=mail,=uid,=gid Note For DOVECOT_*_ATTRS , you can replace ldapAttr=dovecotAttr with =dovecotAttr=%{ldap:ldapAttr} for more flexibility, like for example =home=/var/mail/%{ldap:uid} or just =uid=5000 . A list of dovecot attributes can be found in the dovecot documentation . Defaults - DOVECOT_USER_ATTRS=mailHomeDirectory=home,mailUidNumber=uid,mailGidNumber=gid,mailStorageDirectory=mail - DOVECOT_PASS_ATTRS=uniqueIdentifier=user,userPassword=password - DOVECOT_USER_FILTER=(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n)) Example Setup for a directory that has the qmail-schema installed and uses uid : - DOVECOT_PASS_ATTRS=uid=user,userPassword=password - DOVECOT_USER_ATTRS=homeDirectory=home,qmailUID=uid,qmailGID=gid,mailMessageStore=mail - DOVECOT_USER_FILTER=(&(objectClass=qmailUser)(uid=%u)(accountStatus=active)) The LDAP server configuration for dovecot will be taken mostly from postfix, other options can be found in the environment section in the docs . DOVECOT_AUTH_BIND Set this to yes to enable authentication binds ( more details in the dovecot documentation ). Currently, only DN lookup is supported without further changes to the configuration files, so this is only useful when you want to bind as a readonly user without the permission to read passwords. SASLAUTHD_LDAP_FILTER This filter is used for saslauthd , which is called by postfix when someone is authenticating through SMTP (assuming that SASLAUTHD_MECHANISMS=ldap is being used). Note that you'll need to set up the LDAP server for saslauthd separately from postfix. The filter variables are explained in detail in the LDAP_SASLAUTHD file , but unfortunately, this method doesn't really support domains right now - that means that %U is the only token that makes sense in this variable. When to use this and how to avoid it Using a separate filter for SMTP authentication allows you to for example allow noreply@example.org to send email, but not log in to IMAP or receive email: (&(mail=%U@example.org)(|(memberOf=cn=email,*)(mail=noreply@example.org))) If you don't want to use a separate filter for SMTP authentication, you can set SASLAUTHD_MECHANISMS=rimap and SASLAUTHD_MECH_OPTIONS=127.0.0.1 to authenticate against dovecot instead - this means that the DOVECOT_USER_FILTER and DOVECOT_PASS_FILTER will be used for SMTP authentication as well. Configure LDAP with saslauthd - ENABLE_SASLAUTHD=1 - SASLAUTHD_MECHANISMS=ldap - SASLAUTHD_LDAP_FILTER=(mail=%U@example.org) Secure Connection with LDAPS or StartTLS To enable LDAPS, all you need to do is to add the protocol to LDAP_SERVER_HOST , for example ldaps://example.org:636 . To enable LDAP over StartTLS (on port 389), you need to set the following environment variables instead (the protocol must not be ldaps:// in this case!): - LDAP_START_TLS=yes - DOVECOT_TLS=yes - SASLAUTHD_LDAP_START_TLS=yes Active Directory Configurations (Tested with Samba4 AD Implementation) In addition to LDAP explanation above, when Docker Mailserver is intended to be used with Active Directory (or the equivalent implementations like Samba4 AD DC) the following points should be taken into consideration: Samba4 Active Directory requires a secure connection to the domain controller (DC), either via SSL/TLS (LDAPS) or via StartTLS. The username equivalent in Active Directory is: sAMAccountName . proxyAddresses can be used to store email aliases of single users. The convention is to prefix the email aliases with smtp: (e.g: smtp:some.name@example.com ). Active Directory is used typically not only as LDAP Directory storage, but also as a domain controller , i.e., it will do many things including authenticating users. Mixing Linux and Windows clients requires the usage of RFC2307 attributes , namely uidNumber , gidNumber instead of the typical uid . Assigning different owner to email folders can also be done in this approach, nevertheless there is a bug at the moment in Docker Mailserver that overwrites all permissions when starting the container. Either a manual fix is necessary now, or a temporary workaround to use a hard-coded ldap:uidNumber that equals to 5000 until this issue is fixed. To deliver the emails to different members of Active Directory Security Group or Distribution Group (similar to mailing lists), use a user-patches.sh script to modify ldap-groups.cf so that it includes leaf_result_attribute = mail and special_result_attribute = member . This can be achieved simply by: The configuration shown to get the Group to work is from here and here . # user-patches.sh ... grep -q '^leaf_result_attribute = mail$' /etc/postfix/ldap-groups.cf || echo \"leaf_result_attribute = mail\" >> /etc/postfix/ldap-groups.cf grep -q '^special_result_attribute = member$' /etc/postfix/ldap-groups.cf || echo \"special_result_attribute = member\" >> /etc/postfix/ldap-groups.cf ... In /etc/ldap/ldap.conf , if the TLS_REQCERT is demand / hard (default), the CA certificate used to verify the LDAP server certificate must be recognized as a trusted CA. This can be done by volume mounting the ca.crt file and updating the trust store via a user-patches.sh script: # user-patches.sh ... cp /MOUNTED_FOLDER/ca.crt /usr/local/share/ca-certificates/ update-ca-certificates ... The changes on the configurations necessary to work with Active Directory ( only changes are listed, the rest of the LDAP configuration can be taken from the other examples shown in this documentation): # If StartTLS is the chosen method to establish a secure connection with Active Directory. - LDAP_START_TLS=yes - SASLAUTHD_LDAP_START_TLS=yes - DOVECOT_TLS=yes - LDAP_QUERY_FILTER_USER=(&(objectclass=person)(mail=%s)) - LDAP_QUERY_FILTER_ALIAS=(&(objectclass=person)(proxyAddresses=smtp:%s)) # Filters Active Directory groups (mail lists). Additional changes on ldap-groups.cf are also required as shown above. - LDAP_QUERY_FILTER_GROUP=(&(objectClass=group)(mail=%s)) - LDAP_QUERY_FILTER_DOMAIN=(mail=*@%s) # Allows only Domain admins to send any sender email address, otherwise the sender address must match the LDAP attribute `mail`. - SPOOF_PROTECTION=1 - LDAP_QUERY_FILTER_SENDERS=(|(mail=%s)(proxyAddresses=smtp:%s)(memberOf=cn=Domain Admins,cn=Users,dc=*)) - DOVECOT_USER_FILTER=(&(objectclass=person)(sAMAccountName=%n)) # At the moment to be able to use %{ldap:uidNumber}, a manual bug fix as described above must be used. Otherwise %{ldap:uidNumber} %{ldap:uidNumber} must be replaced by the hard-coded value 5000. - DOVECOT_USER_ATTRS==uid=%{ldap:uidNumber},=gid=5000,=home=/var/mail/%Ln,=mail=maildir:~/Maildir - DOVECOT_PASS_ATTRS=sAMAccountName=user,userPassword=password - SASLAUTHD_LDAP_FILTER=(&(sAMAccountName=%U)(objectClass=person)) LDAP Setup Examples Basic Setup services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail.example.com ports : - \"25:25\" - \"143:143\" - \"587:587\" - \"993:993\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - /etc/localtime:/etc/localtime:ro environment : - ENABLE_SPAMASSASSIN=1 - ENABLE_CLAMAV=1 - ENABLE_FAIL2BAN=1 - ENABLE_POSTGREY=1 # >>> Postfix LDAP Integration - ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER - ACCOUNT_PROVISIONER=LDAP - LDAP_SERVER_HOST=ldap.example.org - LDAP_BIND_DN=cn=admin,ou=users,dc=example,dc=org - LDAP_BIND_PW=mypassword - LDAP_SEARCH_BASE=dc=example,dc=org - LDAP_QUERY_FILTER_DOMAIN=(|(mail=*@%s)(mailAlias=*@%s)(mailGroupMember=*@%s)) - LDAP_QUERY_FILTER_USER=(&(objectClass=inetOrgPerson)(mail=%s)) - LDAP_QUERY_FILTER_ALIAS=(&(objectClass=inetOrgPerson)(mailAlias=%s)) - LDAP_QUERY_FILTER_GROUP=(&(objectClass=inetOrgPerson)(mailGroupMember=%s)) - LDAP_QUERY_FILTER_SENDERS=(&(objectClass=inetOrgPerson)(|(mail=%s)(mailAlias=%s)(mailGroupMember=%s))) - SPOOF_PROTECTION=1 # <<< Postfix LDAP Integration # >>> Dovecot LDAP Integration - DOVECOT_USER_FILTER=(&(objectClass=inetOrgPerson)(mail=%u)) - DOVECOT_PASS_ATTRS=uid=user,userPassword=password - DOVECOT_USER_ATTRS==home=/var/mail/%{ldap:uid},=mail=maildir:~/Maildir,uidNumber=uid,gidNumber=gid # <<< Dovecot LDAP Integration # >>> SASL LDAP Authentication - ENABLE_SASLAUTHD=1 - SASLAUTHD_MECHANISMS=ldap - SASLAUTHD_LDAP_FILTER=(&(mail=%U@example.org)(objectClass=inetOrgPerson)) # <<< SASL LDAP Authentication - SSL_TYPE=letsencrypt - PERMIT_DOCKER=host cap_add : - NET_ADMIN Kopano / Zarafa services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail.example.com ports : - \"25:25\" - \"143:143\" - \"587:587\" - \"993:993\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ environment : # We are not using dovecot here - SMTP_ONLY=1 - ENABLE_SPAMASSASSIN=1 - ENABLE_CLAMAV=1 - ENABLE_FAIL2BAN=1 - ENABLE_POSTGREY=1 - SASLAUTHD_PASSWD= # >>> SASL Authentication - ENABLE_SASLAUTHD=1 - SASLAUTHD_LDAP_FILTER=(&(sAMAccountName=%U)(objectClass=person)) - SASLAUTHD_MECHANISMS=ldap # <<< SASL Authentication # >>> Postfix Ldap Integration - ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER - ACCOUNT_PROVISIONER=LDAP - LDAP_SERVER_HOST= - LDAP_SEARCH_BASE=dc=mydomain,dc=loc - LDAP_BIND_DN=cn=Administrator,cn=Users,dc=mydomain,dc=loc - LDAP_BIND_PW=mypassword - LDAP_QUERY_FILTER_USER=(&(objectClass=user)(mail=%s)) - LDAP_QUERY_FILTER_GROUP=(&(objectclass=group)(mail=%s)) - LDAP_QUERY_FILTER_ALIAS=(&(objectClass=user)(otherMailbox=%s)) - LDAP_QUERY_FILTER_DOMAIN=(&(|(mail=*@%s)(mailalias=*@%s)(mailGroupMember=*@%s))(mailEnabled=TRUE)) # <<< Postfix Ldap Integration # >>> Kopano Integration - POSTFIX_DAGENT=lmtp:kopano:2003 # <<< Kopano Integration - SSL_TYPE=letsencrypt - PERMIT_DOCKER=host cap_add : - NET_ADMIN","title":"LDAP Authentication"},{"location":"config/advanced/auth-ldap/#introduction","text":"Getting started with ldap and docker-mailserver we need to take 3 parts in account: postfix for incoming & outgoing email dovecot for accessing mailboxes saslauthd for SMTP authentication (this can also be delegated to dovecot)","title":"Introduction"},{"location":"config/advanced/auth-ldap/#variables-to-control-provisioning-by-the-container","text":"Have a look at the ENV page for information on the default values.","title":"Variables to Control Provisioning by the Container"},{"location":"config/advanced/auth-ldap/#ldap_query_filter_","text":"Those variables contain the LDAP lookup filters for postfix, using %s as the placeholder for the domain or email address in question. This means that... ...for incoming email, the domain must return an entry for the DOMAIN filter (see virtual_alias_domains ). ...for incoming email, the inboxes which receive the email are chosen by the USER , ALIAS and GROUP filters. The USER filter specifies personal mailboxes, for which only one should exist per address, for example (mail=%s) (also see virtual_mailbox_maps ) The ALIAS filter specifies aliases for mailboxes, using virtual_alias_maps , for example (mailAlias=%s) The GROUP filter specifies the personal mailboxes in a group (for emails that multiple people shall receive), using virtual_alias_maps , for example (mailGroupMember=%s) . Technically, there is no difference between ALIAS and GROUP , but ideally you should use ALIAS for personal aliases for a singular person (like ceo@example.org ) and GROUP for multiple people (like hr@example.org ). ...for outgoing email, the sender address is put through the SENDERS filter, and only if the authenticated user is one of the returned entries, the email can be sent. This only applies if SPOOF_PROTECTION=1 . If the SENDERS filter is missing, the USER , ALIAS and GROUP filters will be used in in a disjunction (OR). To for example allow users from the admin group to spoof any sender email address, and to force everyone else to only use their personal mailbox address for outgoing email, you can use something like this: (|(memberOf=cn=admin,*)(mail=%s)) Example A really simple LDAP_QUERY_FILTER configuration, using only the user filter and allowing only admin@* to spoof any sender addresses. - ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER - LDAP_START_TLS=yes - ACCOUNT_PROVISIONER=LDAP - LDAP_SERVER_HOST=ldap.example.org - LDAP_SEARCH_BASE=dc=example,dc=org\" - LDAP_BIND_DN=cn=admin,dc=example,dc=org - LDAP_BIND_PW=mypassword - SPOOF_PROTECTION=1 - LDAP_QUERY_FILTER_DOMAIN=(mail=*@%s) - LDAP_QUERY_FILTER_USER=(mail=%s) - LDAP_QUERY_FILTER_ALIAS=(|) # doesn't match anything - LDAP_QUERY_FILTER_GROUP=(|) # doesn't match anything - LDAP_QUERY_FILTER_SENDERS=(|(mail=%s)(mail=admin@*))","title":"LDAP_QUERY_FILTER_*"},{"location":"config/advanced/auth-ldap/#dovecot__filter-dovecot__attrs","text":"These variables specify the LDAP filters that dovecot uses to determine if a user can log in to their IMAP account, and which mailbox is responsible to receive email for a specific postfix user. This is split into the following two lookups, both using %u as the placeholder for the full login name ( see dovecot documentation for a full list of placeholders ). Usually you only need to set DOVECOT_USER_FILTER , in which case it will be used for both filters. DOVECOT_USER_FILTER is used to get the account details (uid, gid, home directory, quota, ...) of a user. DOVECOT_PASS_FILTER is used to get the password information of the user, and is in pretty much all cases identical to DOVECOT_USER_FILTER (which is the default behaviour if left away). If your directory doesn't have the postfix-book schema installed, then you must change the internal attribute handling for dovecot. For this you have to change the pass_attr and the user_attr mapping, as shown in the example below: - DOVECOT_PASS_ATTRS==user,=password - DOVECOT_USER_ATTRS==home,=mail,=uid,=gid Note For DOVECOT_*_ATTRS , you can replace ldapAttr=dovecotAttr with =dovecotAttr=%{ldap:ldapAttr} for more flexibility, like for example =home=/var/mail/%{ldap:uid} or just =uid=5000 . A list of dovecot attributes can be found in the dovecot documentation . Defaults - DOVECOT_USER_ATTRS=mailHomeDirectory=home,mailUidNumber=uid,mailGidNumber=gid,mailStorageDirectory=mail - DOVECOT_PASS_ATTRS=uniqueIdentifier=user,userPassword=password - DOVECOT_USER_FILTER=(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n)) Example Setup for a directory that has the qmail-schema installed and uses uid : - DOVECOT_PASS_ATTRS=uid=user,userPassword=password - DOVECOT_USER_ATTRS=homeDirectory=home,qmailUID=uid,qmailGID=gid,mailMessageStore=mail - DOVECOT_USER_FILTER=(&(objectClass=qmailUser)(uid=%u)(accountStatus=active)) The LDAP server configuration for dovecot will be taken mostly from postfix, other options can be found in the environment section in the docs .","title":"DOVECOT_*_FILTER & DOVECOT_*_ATTRS"},{"location":"config/advanced/auth-ldap/#dovecot_auth_bind","text":"Set this to yes to enable authentication binds ( more details in the dovecot documentation ). Currently, only DN lookup is supported without further changes to the configuration files, so this is only useful when you want to bind as a readonly user without the permission to read passwords.","title":"DOVECOT_AUTH_BIND"},{"location":"config/advanced/auth-ldap/#saslauthd_ldap_filter","text":"This filter is used for saslauthd , which is called by postfix when someone is authenticating through SMTP (assuming that SASLAUTHD_MECHANISMS=ldap is being used). Note that you'll need to set up the LDAP server for saslauthd separately from postfix. The filter variables are explained in detail in the LDAP_SASLAUTHD file , but unfortunately, this method doesn't really support domains right now - that means that %U is the only token that makes sense in this variable. When to use this and how to avoid it Using a separate filter for SMTP authentication allows you to for example allow noreply@example.org to send email, but not log in to IMAP or receive email: (&(mail=%U@example.org)(|(memberOf=cn=email,*)(mail=noreply@example.org))) If you don't want to use a separate filter for SMTP authentication, you can set SASLAUTHD_MECHANISMS=rimap and SASLAUTHD_MECH_OPTIONS=127.0.0.1 to authenticate against dovecot instead - this means that the DOVECOT_USER_FILTER and DOVECOT_PASS_FILTER will be used for SMTP authentication as well. Configure LDAP with saslauthd - ENABLE_SASLAUTHD=1 - SASLAUTHD_MECHANISMS=ldap - SASLAUTHD_LDAP_FILTER=(mail=%U@example.org)","title":"SASLAUTHD_LDAP_FILTER"},{"location":"config/advanced/auth-ldap/#secure-connection-with-ldaps-or-starttls","text":"To enable LDAPS, all you need to do is to add the protocol to LDAP_SERVER_HOST , for example ldaps://example.org:636 . To enable LDAP over StartTLS (on port 389), you need to set the following environment variables instead (the protocol must not be ldaps:// in this case!): - LDAP_START_TLS=yes - DOVECOT_TLS=yes - SASLAUTHD_LDAP_START_TLS=yes","title":"Secure Connection with LDAPS or StartTLS"},{"location":"config/advanced/auth-ldap/#active-directory-configurations-tested-with-samba4-ad-implementation","text":"In addition to LDAP explanation above, when Docker Mailserver is intended to be used with Active Directory (or the equivalent implementations like Samba4 AD DC) the following points should be taken into consideration: Samba4 Active Directory requires a secure connection to the domain controller (DC), either via SSL/TLS (LDAPS) or via StartTLS. The username equivalent in Active Directory is: sAMAccountName . proxyAddresses can be used to store email aliases of single users. The convention is to prefix the email aliases with smtp: (e.g: smtp:some.name@example.com ). Active Directory is used typically not only as LDAP Directory storage, but also as a domain controller , i.e., it will do many things including authenticating users. Mixing Linux and Windows clients requires the usage of RFC2307 attributes , namely uidNumber , gidNumber instead of the typical uid . Assigning different owner to email folders can also be done in this approach, nevertheless there is a bug at the moment in Docker Mailserver that overwrites all permissions when starting the container. Either a manual fix is necessary now, or a temporary workaround to use a hard-coded ldap:uidNumber that equals to 5000 until this issue is fixed. To deliver the emails to different members of Active Directory Security Group or Distribution Group (similar to mailing lists), use a user-patches.sh script to modify ldap-groups.cf so that it includes leaf_result_attribute = mail and special_result_attribute = member . This can be achieved simply by: The configuration shown to get the Group to work is from here and here . # user-patches.sh ... grep -q '^leaf_result_attribute = mail$' /etc/postfix/ldap-groups.cf || echo \"leaf_result_attribute = mail\" >> /etc/postfix/ldap-groups.cf grep -q '^special_result_attribute = member$' /etc/postfix/ldap-groups.cf || echo \"special_result_attribute = member\" >> /etc/postfix/ldap-groups.cf ... In /etc/ldap/ldap.conf , if the TLS_REQCERT is demand / hard (default), the CA certificate used to verify the LDAP server certificate must be recognized as a trusted CA. This can be done by volume mounting the ca.crt file and updating the trust store via a user-patches.sh script: # user-patches.sh ... cp /MOUNTED_FOLDER/ca.crt /usr/local/share/ca-certificates/ update-ca-certificates ... The changes on the configurations necessary to work with Active Directory ( only changes are listed, the rest of the LDAP configuration can be taken from the other examples shown in this documentation): # If StartTLS is the chosen method to establish a secure connection with Active Directory. - LDAP_START_TLS=yes - SASLAUTHD_LDAP_START_TLS=yes - DOVECOT_TLS=yes - LDAP_QUERY_FILTER_USER=(&(objectclass=person)(mail=%s)) - LDAP_QUERY_FILTER_ALIAS=(&(objectclass=person)(proxyAddresses=smtp:%s)) # Filters Active Directory groups (mail lists). Additional changes on ldap-groups.cf are also required as shown above. - LDAP_QUERY_FILTER_GROUP=(&(objectClass=group)(mail=%s)) - LDAP_QUERY_FILTER_DOMAIN=(mail=*@%s) # Allows only Domain admins to send any sender email address, otherwise the sender address must match the LDAP attribute `mail`. - SPOOF_PROTECTION=1 - LDAP_QUERY_FILTER_SENDERS=(|(mail=%s)(proxyAddresses=smtp:%s)(memberOf=cn=Domain Admins,cn=Users,dc=*)) - DOVECOT_USER_FILTER=(&(objectclass=person)(sAMAccountName=%n)) # At the moment to be able to use %{ldap:uidNumber}, a manual bug fix as described above must be used. Otherwise %{ldap:uidNumber} %{ldap:uidNumber} must be replaced by the hard-coded value 5000. - DOVECOT_USER_ATTRS==uid=%{ldap:uidNumber},=gid=5000,=home=/var/mail/%Ln,=mail=maildir:~/Maildir - DOVECOT_PASS_ATTRS=sAMAccountName=user,userPassword=password - SASLAUTHD_LDAP_FILTER=(&(sAMAccountName=%U)(objectClass=person))","title":"Active Directory Configurations (Tested with Samba4 AD Implementation)"},{"location":"config/advanced/auth-ldap/#ldap-setup-examples","text":"Basic Setup services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail.example.com ports : - \"25:25\" - \"143:143\" - \"587:587\" - \"993:993\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - /etc/localtime:/etc/localtime:ro environment : - ENABLE_SPAMASSASSIN=1 - ENABLE_CLAMAV=1 - ENABLE_FAIL2BAN=1 - ENABLE_POSTGREY=1 # >>> Postfix LDAP Integration - ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER - ACCOUNT_PROVISIONER=LDAP - LDAP_SERVER_HOST=ldap.example.org - LDAP_BIND_DN=cn=admin,ou=users,dc=example,dc=org - LDAP_BIND_PW=mypassword - LDAP_SEARCH_BASE=dc=example,dc=org - LDAP_QUERY_FILTER_DOMAIN=(|(mail=*@%s)(mailAlias=*@%s)(mailGroupMember=*@%s)) - LDAP_QUERY_FILTER_USER=(&(objectClass=inetOrgPerson)(mail=%s)) - LDAP_QUERY_FILTER_ALIAS=(&(objectClass=inetOrgPerson)(mailAlias=%s)) - LDAP_QUERY_FILTER_GROUP=(&(objectClass=inetOrgPerson)(mailGroupMember=%s)) - LDAP_QUERY_FILTER_SENDERS=(&(objectClass=inetOrgPerson)(|(mail=%s)(mailAlias=%s)(mailGroupMember=%s))) - SPOOF_PROTECTION=1 # <<< Postfix LDAP Integration # >>> Dovecot LDAP Integration - DOVECOT_USER_FILTER=(&(objectClass=inetOrgPerson)(mail=%u)) - DOVECOT_PASS_ATTRS=uid=user,userPassword=password - DOVECOT_USER_ATTRS==home=/var/mail/%{ldap:uid},=mail=maildir:~/Maildir,uidNumber=uid,gidNumber=gid # <<< Dovecot LDAP Integration # >>> SASL LDAP Authentication - ENABLE_SASLAUTHD=1 - SASLAUTHD_MECHANISMS=ldap - SASLAUTHD_LDAP_FILTER=(&(mail=%U@example.org)(objectClass=inetOrgPerson)) # <<< SASL LDAP Authentication - SSL_TYPE=letsencrypt - PERMIT_DOCKER=host cap_add : - NET_ADMIN Kopano / Zarafa services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail.example.com ports : - \"25:25\" - \"143:143\" - \"587:587\" - \"993:993\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ environment : # We are not using dovecot here - SMTP_ONLY=1 - ENABLE_SPAMASSASSIN=1 - ENABLE_CLAMAV=1 - ENABLE_FAIL2BAN=1 - ENABLE_POSTGREY=1 - SASLAUTHD_PASSWD= # >>> SASL Authentication - ENABLE_SASLAUTHD=1 - SASLAUTHD_LDAP_FILTER=(&(sAMAccountName=%U)(objectClass=person)) - SASLAUTHD_MECHANISMS=ldap # <<< SASL Authentication # >>> Postfix Ldap Integration - ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER - ACCOUNT_PROVISIONER=LDAP - LDAP_SERVER_HOST= - LDAP_SEARCH_BASE=dc=mydomain,dc=loc - LDAP_BIND_DN=cn=Administrator,cn=Users,dc=mydomain,dc=loc - LDAP_BIND_PW=mypassword - LDAP_QUERY_FILTER_USER=(&(objectClass=user)(mail=%s)) - LDAP_QUERY_FILTER_GROUP=(&(objectclass=group)(mail=%s)) - LDAP_QUERY_FILTER_ALIAS=(&(objectClass=user)(otherMailbox=%s)) - LDAP_QUERY_FILTER_DOMAIN=(&(|(mail=*@%s)(mailalias=*@%s)(mailGroupMember=*@%s))(mailEnabled=TRUE)) # <<< Postfix Ldap Integration # >>> Kopano Integration - POSTFIX_DAGENT=lmtp:kopano:2003 # <<< Kopano Integration - SSL_TYPE=letsencrypt - PERMIT_DOCKER=host cap_add : - NET_ADMIN","title":"LDAP Setup Examples"},{"location":"config/advanced/dovecot-master-accounts/","text":"Introduction A dovecot master account is able to login as any configured user. This is useful for administrative tasks like hot backups. Configuration It is possible to create, update, delete and list dovecot master accounts using setup.sh . See setup.sh help for usage. This feature is presently not supported with LDAP . Logging in Once a master account is configured, it is possible to connect to any users mailbox using this account. Log in over POP3/IMAP using the following credential scheme: Username: * Password: ","title":"Dovecot Master Accounts"},{"location":"config/advanced/dovecot-master-accounts/#introduction","text":"A dovecot master account is able to login as any configured user. This is useful for administrative tasks like hot backups.","title":"Introduction"},{"location":"config/advanced/dovecot-master-accounts/#configuration","text":"It is possible to create, update, delete and list dovecot master accounts using setup.sh . See setup.sh help for usage. This feature is presently not supported with LDAP .","title":"Configuration"},{"location":"config/advanced/dovecot-master-accounts/#logging-in","text":"Once a master account is configured, it is possible to connect to any users mailbox using this account. Log in over POP3/IMAP using the following credential scheme: Username: * Password: ","title":"Logging in"},{"location":"config/advanced/full-text-search/","text":"Overview Full-text search allows all messages to be indexed, so that mail clients can quickly and efficiently search messages by their full text content. Dovecot supports a variety of community supported FTS indexing backends . docker-mailserver comes pre-installed with two plugins that can be enabled with a dovecot config file. Please be aware that indexing consumes memory and takes up additional disk space. Xapian The dovecot-fts-xapian plugin makes use of Xapian . Xapian enables embedding an FTS engine without the need for additional backends. The indexes will be stored as a subfolder named xapian-indexes inside your local mail-data folder ( /var/mail internally ). With the default settings, 10GB of email data may generate around 4GB of indexed data. While indexing is memory intensive, you can configure the plugin to limit the amount of memory consumed by the index workers. With Xapian being small and fast, this plugin is a good choice for low memory environments (2GB) as compared to Solr. Setup To configure fts-xapian as a dovecot plugin, create a file at docker-data/dms/config/dovecot/fts-xapian-plugin.conf and place the following in it: mail_plugins = $mail_plugins fts fts_xapian plugin { fts = xapian fts_xapian = partial=3 full=20 verbose=0 fts_autoindex = yes fts_enforced = yes # disable indexing of folders # fts_autoindex_exclude = \\Trash # Index attachements # fts_decoder = decode2text } service indexer-worker { # limit size of indexer-worker RAM usage, ex: 512MB, 1GB, 2GB vsz_limit = 1GB } # service decode2text { # executable = script /usr/libexec/dovecot/decode2text.sh # user = dovecot # unix_listener decode2text { # mode = 0666 # } # } adjust the settings to tune for your desired memory limits, exclude folders and enable searching text inside of attachments Update docker-compose.yml to load the previously created dovecot plugin config file: version : '3.8' services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail domainname : example.com env_file : mailserver.env ports : - \"25:25\" # SMTP (explicit TLS => STARTTLS) - \"143:143\" # IMAP4 (explicit TLS => STARTTLS) - \"465:465\" # ESMTP (implicit TLS) - \"587:587\" # ESMTP (explicit TLS => STARTTLS) - \"993:993\" # IMAP4 (implicit TLS) volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - ./docker-data/dms/config/dovecot/fts-xapian-plugin.conf:/etc/dovecot/conf.d/10-plugin.conf:ro - /etc/localtime:/etc/localtime:ro restart : always stop_grace_period : 1m cap_add : - NET_ADMIN Recreate containers: docker-compose down docker-compose up -d Initialize indexing on all users for all mail: docker-compose exec mailserver doveadm index -A -q \\* Run the following command in a daily cron job: docker-compose exec mailserver doveadm fts optimize -A Or like the Spamassassin example shows, you can instead use cron from within docker-mailserver to avoid potential errors if the mail-server is not running: Example Create a system cron file: # in the docker-compose.yml root directory mkdir -p ./docker-data/dms/cron # if you didn't have this folder before touch ./docker-data/dms/cron/fts_xapian chown root:root ./docker-data/dms/cron/fts_xapian chmod 0644 ./docker-data/dms/cron/fts_xapian Edit the system cron file nano ./docker-data/dms/cron/fts_xapian , and set an appropriate configuration: # Adding `MAILTO=\"\"` prevents cron emailing notifications of the task outcome each run MAILTO = \"\" # # m h dom mon dow user command # # Everyday 4:00AM, optimize index files 0 4 * * * root doveadm fts optimize -A Then with docker-compose.yml : services : mailserver : image : docker.io/mailserver/docker-mailserver:latest volumes : - ./docker-data/dms/cron/fts_xapian:/etc/cron.d/fts_xapian Solr The dovecot-solr Plugin is used in conjunction with Apache Solr running in a separate container. This is quite straightforward to setup using the following instructions. Solr is a mature and fast indexing backend that runs on the JVM. The indexes are relatively compact compared to the size of your total email. However, Solr also requires a fair bit of RAM. While Solr is highly tuneable , it may require a bit of testing to get it right. Setup docker-compose.yml : solr : image : lmmdock/dovecot-solr:latest volumes : - ./docker-data/dms/config/dovecot/solr-dovecot:/opt/solr/server/solr/dovecot restart : always mailserver : depends_on : - solr image : docker.io/mailserver/docker-mailserver:latest ... volumes : ... - ./docker-data/dms/config/dovecot/10-plugin.conf:/etc/dovecot/conf.d/10-plugin.conf:ro ... ./docker-data/dms/config/dovecot/10-plugin.conf : mail_plugins = $mail_plugins fts fts_solr plugin { fts = solr fts_autoindex = yes fts_solr = url=http://solr:8983/solr/dovecot/ } Recreate containers: docker-compose down ; docker-compose up -d Flag all user mailbox FTS indexes as invalid, so they are rescanned on demand when they are next searched: docker-compose exec mailserver doveadm fts rescan -A Further Discussion See #905","title":"Full-Text Search"},{"location":"config/advanced/full-text-search/#overview","text":"Full-text search allows all messages to be indexed, so that mail clients can quickly and efficiently search messages by their full text content. Dovecot supports a variety of community supported FTS indexing backends . docker-mailserver comes pre-installed with two plugins that can be enabled with a dovecot config file. Please be aware that indexing consumes memory and takes up additional disk space.","title":"Overview"},{"location":"config/advanced/full-text-search/#xapian","text":"The dovecot-fts-xapian plugin makes use of Xapian . Xapian enables embedding an FTS engine without the need for additional backends. The indexes will be stored as a subfolder named xapian-indexes inside your local mail-data folder ( /var/mail internally ). With the default settings, 10GB of email data may generate around 4GB of indexed data. While indexing is memory intensive, you can configure the plugin to limit the amount of memory consumed by the index workers. With Xapian being small and fast, this plugin is a good choice for low memory environments (2GB) as compared to Solr.","title":"Xapian"},{"location":"config/advanced/full-text-search/#setup","text":"To configure fts-xapian as a dovecot plugin, create a file at docker-data/dms/config/dovecot/fts-xapian-plugin.conf and place the following in it: mail_plugins = $mail_plugins fts fts_xapian plugin { fts = xapian fts_xapian = partial=3 full=20 verbose=0 fts_autoindex = yes fts_enforced = yes # disable indexing of folders # fts_autoindex_exclude = \\Trash # Index attachements # fts_decoder = decode2text } service indexer-worker { # limit size of indexer-worker RAM usage, ex: 512MB, 1GB, 2GB vsz_limit = 1GB } # service decode2text { # executable = script /usr/libexec/dovecot/decode2text.sh # user = dovecot # unix_listener decode2text { # mode = 0666 # } # } adjust the settings to tune for your desired memory limits, exclude folders and enable searching text inside of attachments Update docker-compose.yml to load the previously created dovecot plugin config file: version : '3.8' services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail domainname : example.com env_file : mailserver.env ports : - \"25:25\" # SMTP (explicit TLS => STARTTLS) - \"143:143\" # IMAP4 (explicit TLS => STARTTLS) - \"465:465\" # ESMTP (implicit TLS) - \"587:587\" # ESMTP (explicit TLS => STARTTLS) - \"993:993\" # IMAP4 (implicit TLS) volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - ./docker-data/dms/config/dovecot/fts-xapian-plugin.conf:/etc/dovecot/conf.d/10-plugin.conf:ro - /etc/localtime:/etc/localtime:ro restart : always stop_grace_period : 1m cap_add : - NET_ADMIN Recreate containers: docker-compose down docker-compose up -d Initialize indexing on all users for all mail: docker-compose exec mailserver doveadm index -A -q \\* Run the following command in a daily cron job: docker-compose exec mailserver doveadm fts optimize -A Or like the Spamassassin example shows, you can instead use cron from within docker-mailserver to avoid potential errors if the mail-server is not running: Example Create a system cron file: # in the docker-compose.yml root directory mkdir -p ./docker-data/dms/cron # if you didn't have this folder before touch ./docker-data/dms/cron/fts_xapian chown root:root ./docker-data/dms/cron/fts_xapian chmod 0644 ./docker-data/dms/cron/fts_xapian Edit the system cron file nano ./docker-data/dms/cron/fts_xapian , and set an appropriate configuration: # Adding `MAILTO=\"\"` prevents cron emailing notifications of the task outcome each run MAILTO = \"\" # # m h dom mon dow user command # # Everyday 4:00AM, optimize index files 0 4 * * * root doveadm fts optimize -A Then with docker-compose.yml : services : mailserver : image : docker.io/mailserver/docker-mailserver:latest volumes : - ./docker-data/dms/cron/fts_xapian:/etc/cron.d/fts_xapian","title":"Setup"},{"location":"config/advanced/full-text-search/#solr","text":"The dovecot-solr Plugin is used in conjunction with Apache Solr running in a separate container. This is quite straightforward to setup using the following instructions. Solr is a mature and fast indexing backend that runs on the JVM. The indexes are relatively compact compared to the size of your total email. However, Solr also requires a fair bit of RAM. While Solr is highly tuneable , it may require a bit of testing to get it right.","title":"Solr"},{"location":"config/advanced/full-text-search/#setup_1","text":"docker-compose.yml : solr : image : lmmdock/dovecot-solr:latest volumes : - ./docker-data/dms/config/dovecot/solr-dovecot:/opt/solr/server/solr/dovecot restart : always mailserver : depends_on : - solr image : docker.io/mailserver/docker-mailserver:latest ... volumes : ... - ./docker-data/dms/config/dovecot/10-plugin.conf:/etc/dovecot/conf.d/10-plugin.conf:ro ... ./docker-data/dms/config/dovecot/10-plugin.conf : mail_plugins = $mail_plugins fts fts_solr plugin { fts = solr fts_autoindex = yes fts_solr = url=http://solr:8983/solr/dovecot/ } Recreate containers: docker-compose down ; docker-compose up -d Flag all user mailbox FTS indexes as invalid, so they are rescanned on demand when they are next searched: docker-compose exec mailserver doveadm fts rescan -A","title":"Setup"},{"location":"config/advanced/full-text-search/#further-discussion","text":"See #905","title":"Further Discussion"},{"location":"config/advanced/ipv6/","text":"Background If your container host supports IPv6, then docker-mailserver will automatically accept IPv6 connections by way of the docker host's IPv6. However, incoming mail will fail SPF checks because they will appear to come from the IPv4 gateway that docker is using to proxy the IPv6 connection ( 172.20.0.1 is the gateway). This can be solved by supporting IPv6 connections all the way to the docker-mailserver container. Setup steps +++ b/serv/docker-compose.yml @@ -1,4 +1,4 @@ -version: '2' +version: '2.1' @@ -32,6 +32,16 @@ services: + ipv6nat: + image: robbertkl/ipv6nat + restart: always + network_mode: \"host\" + cap_add: + - NET_ADMIN + - SYS_MODULE + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - /lib/modules:/lib/modules:ro @@ -306,4 +316,13 @@ networks: + default: + driver: bridge + enable_ipv6: true + ipam: + driver: default + config: + - subnet: fd00:0123:4567::/48 + gateway: fd00:0123:4567::1 Further Discussion See #1438","title":"IPv6"},{"location":"config/advanced/ipv6/#background","text":"If your container host supports IPv6, then docker-mailserver will automatically accept IPv6 connections by way of the docker host's IPv6. However, incoming mail will fail SPF checks because they will appear to come from the IPv4 gateway that docker is using to proxy the IPv6 connection ( 172.20.0.1 is the gateway). This can be solved by supporting IPv6 connections all the way to the docker-mailserver container.","title":"Background"},{"location":"config/advanced/ipv6/#setup-steps","text":"+++ b/serv/docker-compose.yml @@ -1,4 +1,4 @@ -version: '2' +version: '2.1' @@ -32,6 +32,16 @@ services: + ipv6nat: + image: robbertkl/ipv6nat + restart: always + network_mode: \"host\" + cap_add: + - NET_ADMIN + - SYS_MODULE + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - /lib/modules:/lib/modules:ro @@ -306,4 +316,13 @@ networks: + default: + driver: bridge + enable_ipv6: true + ipam: + driver: default + config: + - subnet: fd00:0123:4567::/48 + gateway: fd00:0123:4567::1","title":"Setup steps"},{"location":"config/advanced/ipv6/#further-discussion","text":"See #1438","title":"Further Discussion"},{"location":"config/advanced/kubernetes/","text":"Introduction This article describes how to deploy docker-mailserver to Kubernetes. Please note that there is also a Helm chart available. Requirements We assume basic knowledge about Kubernetes from the reader. Moreover, we assume the reader to have a basic understanding of mail servers. Ideally, the reader has deployed docker-mailserver before in an easier setup with Docker (Compose). About Support for Kubernetes Please note that Kubernetes is not officially supported and we do not build images specifically designed for it. When opening an issue, please remember that only Docker & Docker Compose are officially supported. This content is entirely community-supported. If you find errors, please open an issue and provide a PR. Manifests Configuration We want to provide the basic configuration in the form of environment variables with a ConfigMap . Note that this is just an example configuration; tune the ConfigMap to your needs. --- apiVersion : v1 kind : ConfigMap metadata : name : mailserver.environment immutable : false data : TLS_LEVEL : modern POSTSCREEN_ACTION : drop OVERRIDE_HOSTNAME : mail.example.com FAIL2BAN_BLOCKTYPE : drop POSTMASTER_ADDRESS : postmaster@example.com UPDATE_CHECK_INTERVAL : 10d POSTFIX_INET_PROTOCOLS : ipv4 ONE_DIR : '1' ENABLE_CLAMAV : '1' ENABLE_POSTGREY : '0' ENABLE_FAIL2BAN : '1' AMAVIS_LOGLEVEL : '-1' SPOOF_PROTECTION : '1' MOVE_SPAM_TO_JUNK : '1' ENABLE_UPDATE_CHECK : '1' ENABLE_SPAMASSASSIN : '1' SUPERVISOR_LOGLEVEL : warn SPAMASSASSIN_SPAM_TO_INBOX : '1' # here, we provide an example for the SSL configuration SSL_TYPE : manual SSL_CERT_PATH : /secrets/ssl/rsa/tls.crt SSL_KEY_PATH : /secrets/ssl/rsa/tls.key We can also make use of user-provided configuration files, e.g. user-patches.sh , postfix-accounts.cf and more, to adjust docker-mailserver to our likings. We encourage you to have a look at Kustomize for creating ConfigMap s from multiple files, but for now, we will provide a simple, hand-written example. This example is absolutely minimal and only goes to show what can be done. --- apiVersion : v1 kind : ConfigMap metadata : name : mailserver.files data : postfix-accounts.cf : | test@example.com|{SHA512-CRYPT}$6$someHashValueHere other@example.com|{SHA512-CRYPT}$6$someOtherHashValueHere Static Configuration With the configuration shown above, you can not dynamically add accounts as the configuration file mounted into the mail server can not be written to. Use persistent volumes for production deployments. Persistence Thereafter, we need persistence for our data. Make sure you have a storage provisioner and that you choose the correct storageClassName . --- apiVersion : v1 kind : PersistentVolumeClaim metadata : name : data spec : storageClassName : local-path accessModes : - ReadWriteOnce resources : requests : storage : 25Gi Service A Service is required for getting the traffic to the pod itself. The service is somewhat crucial. Its configuration determines whether the original IP from the sender will be kept. More about this further down below . The configuration you're seeing does keep the original IP, but you will not be able to scale this way. We have chosen to go this route in this case because we think most Kubernetes users will only want to have one instance. --- apiVersion : v1 kind : Service metadata : name : mailserver labels : app : mailserver spec : type : LoadBalancer selector : app : mailserver ports : # Transfer - name : transfer port : 25 targetPort : transfer protocol : TCP # ESMTP with implicit TLS - name : esmtp-implicit port : 465 targetPort : esmtp-implicit protocol : TCP # ESMTP with explicit TLS (STARTTLS) - name : esmtp-explicit port : 587 targetPort : esmtp-explicit protocol : TCP # IMAPS with implicit TLS - name : imap-implicit port : 993 targetPort : imap-implicit protocol : TCP Deployments Last but not least, the Deployment becomes the most complex component. It instructs Kubernetes how to run the docker-mailserver container and how to apply your ConfigMaps , persisted storage, etc. Additionally, we can set options to enforce runtime security here. --- apiVersion : apps/v1 kind : Deployment metadata : name : mailserver annotations : ignore-check.kube-linter.io/run-as-non-root : >- 'mailserver' needs to run as root ignore-check.kube-linter.io/privileged-ports : >- 'mailserver' needs privilegdes ports ignore-check.kube-linter.io/no-read-only-root-fs : >- There are too many files written to make The root FS read-only spec : replicas : 1 selector : matchLabels : app : mailserver template : metadata : labels : app : mailserver annotations : container.apparmor.security.beta.kubernetes.io/mailserver : runtime/default spec : hostname : mail containers : - name : mailserver image : docker.io/mailserver/docker-mailserver:latest imagePullPolicy : IfNotPresent securityContext : allowPrivilegeEscalation : false readOnlyRootFilesystem : false runAsUser : 0 runAsGroup : 0 runAsNonRoot : false privileged : false capabilities : add : # file permission capabilities - CHOWN - FOWNER - MKNOD - SETGID - SETUID - DAC_OVERRIDE # network capabilities - NET_ADMIN # needed for F2B - NET_RAW # needed for F2B - NET_BIND_SERVICE # miscellaneous capabilities - SYS_CHROOT - KILL drop : [ ALL ] seccompProfile : type : RuntimeDefault # You want to tune this to your needs. If you disable ClamAV, # you can use less RAM and CPU. This becomes important in # case you're low on resources and Kubernetes refuses to # schedule new pods. resources : limits : memory : 4Gi cpu : 1500m requests : memory : 2Gi cpu : 600m volumeMounts : - name : files subPath : postfix-accounts.cf mountPath : /tmp/docker-mailserver/postfix-accounts.cf readOnly : true # PVCs - name : data mountPath : /var/mail subPath : data readOnly : false - name : data mountPath : /var/mail-state subPath : state readOnly : false - name : data mountPath : /var/log/mail subPath : log readOnly : false # certificates - name : certificates-rsa mountPath : /secrets/ssl/rsa/ readOnly : true # other - name : tmp-files mountPath : /tmp readOnly : false ports : - name : transfer containerPort : 25 protocol : TCP - name : esmtp-implicit containerPort : 465 protocol : TCP - name : esmtp-explicit containerPort : 587 - name : imap-implicit containerPort : 993 protocol : TCP envFrom : - configMapRef : name : mailserver.environment restartPolicy : Always volumes : # configuration files - name : files configMap : name : mailserver.files # PVCs - name : data persistentVolumeClaim : claimName : data # certificates - name : certificates-rsa secret : secretName : mail-tls-certificate-rsa items : - key : tls.key path : tls.key - key : tls.crt path : tls.crt # other - name : tmp-files emptyDir : {} Certificates - An Example In this example, we use cert-manager to supply RSA certificates. You can also supply RSA certificates as fallback certificates, which docker-mailserver supports out of the box with SSL_ALT_CERT_PATH and SSL_ALT_KEY_PATH , and provide ECDSA as the proper certificates. --- apiVersion : cert-manager.io/v1 kind : Certificate metadata : name : mail-tls-certificate-rsa spec : secretName : mail-tls-certificate-rsa isCA : false privateKey : algorithm : RSA encoding : PKCS1 size : 2048 dnsNames : [ mail.example.com ] issuerRef : name : mail-issuer kind : Issuer Attention You will need to have cert-manager configured. Especially the issue will need to be configured. Since we do not know how you want or need your certificates to be supplied, we do not provide more configuration here. The documentation for cert-manager is excellent. Sensitive Data Sensitive Data For storing OpenDKIM keys, TLS certificates or any sort of sensitive data, you should be using Secret s. You can mount secrets like ConfigMap s and use them the same way. The TLS docs page provides guidance when it comes to certificates and transport layer security. Always provide sensitive information vai Secrets . Exposing your Mail-Server to the Outside World The more difficult part with Kubernetes is to expose a deployed docker-mailserver to the outside world. Kubernetes provides multiple ways for doing that; each has downsides and complexity. The major problem with exposing docker-mailserver to outside world in Kubernetes is to preserve the real client IP . The real client IP is required by docker-mailserver for performing IP-based SPF checks and spam checks. If you do not require SPF checks for incoming mails, you may disable them in your Postfix configuration by dropping the line that states: check_policy_service unix:private/policyd-spf . The easiest approach was covered above, using externalTrafficPolicy : Local , which disables the service proxy, but makes the service local as well (which does not scale). This approach only works when you are given the correct (that is, a public and routable) IP address by a load balancer (like MetalLB). In this sense, the approach above is similar to the next example below. We want to provide you with a few alternatives too. But we also want to communicate the idea of another simple method: you could use a load-balancer without an external IP and DNAT the network traffic to the mail-server. After all, this does not interfere with SPF checks because it keeps the origin IP address. If no dedicated external IP address is available, you could try the latter approach, if one is available, use the former. External IPs Service The simplest way is to expose docker-mailserver as a Service with external IPs . This is very similar to the approach taken above. Here, an external IP is given to the service directly by you. With the approach above, you tell your load-balancer to do this. --- apiVersion : v1 kind : Service metadata : name : mailserver labels : app : mailserver spec : selector : app : mailserver ports : - name : smtp port : 25 targetPort : smtp # ... externalIPs : - 80.11.12.10 This approach does not preserve the real client IP, so SPF check of incoming mail will fail. requires you to specify the exposed IPs explicitly. Proxy port to Service The proxy pod helps to avoid the necessity of specifying external IPs explicitly. This comes at the cost of complexity; you must deploy a proxy pod on each Node you want to expose docker-mailserver on. This approach does not preserve the real client IP, so SPF check of incoming mail will fail. Bind to concrete Node and use host network One way to preserve the real client IP is to use hostPort and hostNetwork: true . This comes at the cost of availability; you can reach docker-mailserver from the outside world only via IPs of Node where docker-mailserver is deployed. --- apiVersion : extensions/v1beta1 kind : Deployment metadata : name : mailserver # ... spec : hostNetwork : true # ... containers : # ... ports : - name : smtp containerPort : 25 hostPort : 25 - name : smtp-auth containerPort : 587 hostPort : 587 - name : imap-secure containerPort : 993 hostPort : 993 # ... With this approach, it is not possible to access docker-mailserver via other cluster Nodes, only via the Node docker-mailserver was deployed at. every Port within the Container is exposed on the Host side. Proxy Port to Service via PROXY Protocol This way is ideologically the same as using a proxy pod , but instead of a separate proxy pod, you configure your ingress to proxy TCP traffic to the docker-mailserver pod using the PROXY protocol, which preserves the real client IP. Configure your Ingress With an NGINX ingress controller , set externalTrafficPolicy: Local for its service, and add the following to the TCP services config map (as described here ): 25 : \"mailserver/mailserver:25::PROXY\" 465 : \"mailserver/mailserver:465::PROXY\" 587 : \"mailserver/mailserver:587::PROXY\" 993 : \"mailserver/mailserver:993::PROXY\" HAProxy With HAProxy , the configuration should look similar to the above. If you know what it actually looks like, add an example here. Configure the Mailserver Then, configure both Postfix and Dovecot to expect the PROXY protocol: HAProxy Example kind : ConfigMap apiVersion : v1 metadata : name : mailserver.config labels : app : mailserver data : postfix-main.cf : | postscreen_upstream_proxy_protocol = haproxy postfix-master.cf : | smtp/inet/postscreen_upstream_proxy_protocol=haproxy submission/inet/smtpd_upstream_proxy_protocol=haproxy smtps/inet/smtpd_upstream_proxy_protocol=haproxy dovecot.cf : | # Assuming your ingress controller is bound to 10.0.0.0/8 haproxy_trusted_networks = 10.0.0.0/8, 127.0.0.0/8 service imap-login { inet_listener imap { haproxy = yes } inet_listener imaps { haproxy = yes } } # ... --- kind : Deployment apiVersion : extensions/v1beta1 metadata : name : mailserver spec : template : spec : containers : - name : docker-mailserver volumeMounts : - name : config subPath : postfix-main.cf mountPath : /tmp/docker-mailserver/postfix-main.cf readOnly : true - name : config subPath : postfix-master.cf mountPath : /tmp/docker-mailserver/postfix-master.cf readOnly : true - name : config subPath : dovecot.cf mountPath : /tmp/docker-mailserver/dovecot.cf readOnly : true With this approach, it is not possible to access docker-mailserver via cluster-DNS, as the PROXY protocol is required for incoming connections.","title":"Kubernetes"},{"location":"config/advanced/kubernetes/#introduction","text":"This article describes how to deploy docker-mailserver to Kubernetes. Please note that there is also a Helm chart available. Requirements We assume basic knowledge about Kubernetes from the reader. Moreover, we assume the reader to have a basic understanding of mail servers. Ideally, the reader has deployed docker-mailserver before in an easier setup with Docker (Compose). About Support for Kubernetes Please note that Kubernetes is not officially supported and we do not build images specifically designed for it. When opening an issue, please remember that only Docker & Docker Compose are officially supported. This content is entirely community-supported. If you find errors, please open an issue and provide a PR.","title":"Introduction"},{"location":"config/advanced/kubernetes/#manifests","text":"","title":"Manifests"},{"location":"config/advanced/kubernetes/#configuration","text":"We want to provide the basic configuration in the form of environment variables with a ConfigMap . Note that this is just an example configuration; tune the ConfigMap to your needs. --- apiVersion : v1 kind : ConfigMap metadata : name : mailserver.environment immutable : false data : TLS_LEVEL : modern POSTSCREEN_ACTION : drop OVERRIDE_HOSTNAME : mail.example.com FAIL2BAN_BLOCKTYPE : drop POSTMASTER_ADDRESS : postmaster@example.com UPDATE_CHECK_INTERVAL : 10d POSTFIX_INET_PROTOCOLS : ipv4 ONE_DIR : '1' ENABLE_CLAMAV : '1' ENABLE_POSTGREY : '0' ENABLE_FAIL2BAN : '1' AMAVIS_LOGLEVEL : '-1' SPOOF_PROTECTION : '1' MOVE_SPAM_TO_JUNK : '1' ENABLE_UPDATE_CHECK : '1' ENABLE_SPAMASSASSIN : '1' SUPERVISOR_LOGLEVEL : warn SPAMASSASSIN_SPAM_TO_INBOX : '1' # here, we provide an example for the SSL configuration SSL_TYPE : manual SSL_CERT_PATH : /secrets/ssl/rsa/tls.crt SSL_KEY_PATH : /secrets/ssl/rsa/tls.key We can also make use of user-provided configuration files, e.g. user-patches.sh , postfix-accounts.cf and more, to adjust docker-mailserver to our likings. We encourage you to have a look at Kustomize for creating ConfigMap s from multiple files, but for now, we will provide a simple, hand-written example. This example is absolutely minimal and only goes to show what can be done. --- apiVersion : v1 kind : ConfigMap metadata : name : mailserver.files data : postfix-accounts.cf : | test@example.com|{SHA512-CRYPT}$6$someHashValueHere other@example.com|{SHA512-CRYPT}$6$someOtherHashValueHere Static Configuration With the configuration shown above, you can not dynamically add accounts as the configuration file mounted into the mail server can not be written to. Use persistent volumes for production deployments.","title":"Configuration"},{"location":"config/advanced/kubernetes/#persistence","text":"Thereafter, we need persistence for our data. Make sure you have a storage provisioner and that you choose the correct storageClassName . --- apiVersion : v1 kind : PersistentVolumeClaim metadata : name : data spec : storageClassName : local-path accessModes : - ReadWriteOnce resources : requests : storage : 25Gi","title":"Persistence"},{"location":"config/advanced/kubernetes/#service","text":"A Service is required for getting the traffic to the pod itself. The service is somewhat crucial. Its configuration determines whether the original IP from the sender will be kept. More about this further down below . The configuration you're seeing does keep the original IP, but you will not be able to scale this way. We have chosen to go this route in this case because we think most Kubernetes users will only want to have one instance. --- apiVersion : v1 kind : Service metadata : name : mailserver labels : app : mailserver spec : type : LoadBalancer selector : app : mailserver ports : # Transfer - name : transfer port : 25 targetPort : transfer protocol : TCP # ESMTP with implicit TLS - name : esmtp-implicit port : 465 targetPort : esmtp-implicit protocol : TCP # ESMTP with explicit TLS (STARTTLS) - name : esmtp-explicit port : 587 targetPort : esmtp-explicit protocol : TCP # IMAPS with implicit TLS - name : imap-implicit port : 993 targetPort : imap-implicit protocol : TCP","title":"Service"},{"location":"config/advanced/kubernetes/#deployments","text":"Last but not least, the Deployment becomes the most complex component. It instructs Kubernetes how to run the docker-mailserver container and how to apply your ConfigMaps , persisted storage, etc. Additionally, we can set options to enforce runtime security here. --- apiVersion : apps/v1 kind : Deployment metadata : name : mailserver annotations : ignore-check.kube-linter.io/run-as-non-root : >- 'mailserver' needs to run as root ignore-check.kube-linter.io/privileged-ports : >- 'mailserver' needs privilegdes ports ignore-check.kube-linter.io/no-read-only-root-fs : >- There are too many files written to make The root FS read-only spec : replicas : 1 selector : matchLabels : app : mailserver template : metadata : labels : app : mailserver annotations : container.apparmor.security.beta.kubernetes.io/mailserver : runtime/default spec : hostname : mail containers : - name : mailserver image : docker.io/mailserver/docker-mailserver:latest imagePullPolicy : IfNotPresent securityContext : allowPrivilegeEscalation : false readOnlyRootFilesystem : false runAsUser : 0 runAsGroup : 0 runAsNonRoot : false privileged : false capabilities : add : # file permission capabilities - CHOWN - FOWNER - MKNOD - SETGID - SETUID - DAC_OVERRIDE # network capabilities - NET_ADMIN # needed for F2B - NET_RAW # needed for F2B - NET_BIND_SERVICE # miscellaneous capabilities - SYS_CHROOT - KILL drop : [ ALL ] seccompProfile : type : RuntimeDefault # You want to tune this to your needs. If you disable ClamAV, # you can use less RAM and CPU. This becomes important in # case you're low on resources and Kubernetes refuses to # schedule new pods. resources : limits : memory : 4Gi cpu : 1500m requests : memory : 2Gi cpu : 600m volumeMounts : - name : files subPath : postfix-accounts.cf mountPath : /tmp/docker-mailserver/postfix-accounts.cf readOnly : true # PVCs - name : data mountPath : /var/mail subPath : data readOnly : false - name : data mountPath : /var/mail-state subPath : state readOnly : false - name : data mountPath : /var/log/mail subPath : log readOnly : false # certificates - name : certificates-rsa mountPath : /secrets/ssl/rsa/ readOnly : true # other - name : tmp-files mountPath : /tmp readOnly : false ports : - name : transfer containerPort : 25 protocol : TCP - name : esmtp-implicit containerPort : 465 protocol : TCP - name : esmtp-explicit containerPort : 587 - name : imap-implicit containerPort : 993 protocol : TCP envFrom : - configMapRef : name : mailserver.environment restartPolicy : Always volumes : # configuration files - name : files configMap : name : mailserver.files # PVCs - name : data persistentVolumeClaim : claimName : data # certificates - name : certificates-rsa secret : secretName : mail-tls-certificate-rsa items : - key : tls.key path : tls.key - key : tls.crt path : tls.crt # other - name : tmp-files emptyDir : {}","title":"Deployments"},{"location":"config/advanced/kubernetes/#certificates-an-example","text":"In this example, we use cert-manager to supply RSA certificates. You can also supply RSA certificates as fallback certificates, which docker-mailserver supports out of the box with SSL_ALT_CERT_PATH and SSL_ALT_KEY_PATH , and provide ECDSA as the proper certificates. --- apiVersion : cert-manager.io/v1 kind : Certificate metadata : name : mail-tls-certificate-rsa spec : secretName : mail-tls-certificate-rsa isCA : false privateKey : algorithm : RSA encoding : PKCS1 size : 2048 dnsNames : [ mail.example.com ] issuerRef : name : mail-issuer kind : Issuer Attention You will need to have cert-manager configured. Especially the issue will need to be configured. Since we do not know how you want or need your certificates to be supplied, we do not provide more configuration here. The documentation for cert-manager is excellent.","title":"Certificates - An Example"},{"location":"config/advanced/kubernetes/#sensitive-data","text":"Sensitive Data For storing OpenDKIM keys, TLS certificates or any sort of sensitive data, you should be using Secret s. You can mount secrets like ConfigMap s and use them the same way. The TLS docs page provides guidance when it comes to certificates and transport layer security. Always provide sensitive information vai Secrets .","title":"Sensitive Data"},{"location":"config/advanced/kubernetes/#exposing-your-mail-server-to-the-outside-world","text":"The more difficult part with Kubernetes is to expose a deployed docker-mailserver to the outside world. Kubernetes provides multiple ways for doing that; each has downsides and complexity. The major problem with exposing docker-mailserver to outside world in Kubernetes is to preserve the real client IP . The real client IP is required by docker-mailserver for performing IP-based SPF checks and spam checks. If you do not require SPF checks for incoming mails, you may disable them in your Postfix configuration by dropping the line that states: check_policy_service unix:private/policyd-spf . The easiest approach was covered above, using externalTrafficPolicy : Local , which disables the service proxy, but makes the service local as well (which does not scale). This approach only works when you are given the correct (that is, a public and routable) IP address by a load balancer (like MetalLB). In this sense, the approach above is similar to the next example below. We want to provide you with a few alternatives too. But we also want to communicate the idea of another simple method: you could use a load-balancer without an external IP and DNAT the network traffic to the mail-server. After all, this does not interfere with SPF checks because it keeps the origin IP address. If no dedicated external IP address is available, you could try the latter approach, if one is available, use the former.","title":"Exposing your Mail-Server to the Outside World"},{"location":"config/advanced/kubernetes/#external-ips-service","text":"The simplest way is to expose docker-mailserver as a Service with external IPs . This is very similar to the approach taken above. Here, an external IP is given to the service directly by you. With the approach above, you tell your load-balancer to do this. --- apiVersion : v1 kind : Service metadata : name : mailserver labels : app : mailserver spec : selector : app : mailserver ports : - name : smtp port : 25 targetPort : smtp # ... externalIPs : - 80.11.12.10 This approach does not preserve the real client IP, so SPF check of incoming mail will fail. requires you to specify the exposed IPs explicitly.","title":"External IPs Service"},{"location":"config/advanced/kubernetes/#proxy-port-to-service","text":"The proxy pod helps to avoid the necessity of specifying external IPs explicitly. This comes at the cost of complexity; you must deploy a proxy pod on each Node you want to expose docker-mailserver on. This approach does not preserve the real client IP, so SPF check of incoming mail will fail.","title":"Proxy port to Service"},{"location":"config/advanced/kubernetes/#bind-to-concrete-node-and-use-host-network","text":"One way to preserve the real client IP is to use hostPort and hostNetwork: true . This comes at the cost of availability; you can reach docker-mailserver from the outside world only via IPs of Node where docker-mailserver is deployed. --- apiVersion : extensions/v1beta1 kind : Deployment metadata : name : mailserver # ... spec : hostNetwork : true # ... containers : # ... ports : - name : smtp containerPort : 25 hostPort : 25 - name : smtp-auth containerPort : 587 hostPort : 587 - name : imap-secure containerPort : 993 hostPort : 993 # ... With this approach, it is not possible to access docker-mailserver via other cluster Nodes, only via the Node docker-mailserver was deployed at. every Port within the Container is exposed on the Host side.","title":"Bind to concrete Node and use host network"},{"location":"config/advanced/kubernetes/#proxy-port-to-service-via-proxy-protocol","text":"This way is ideologically the same as using a proxy pod , but instead of a separate proxy pod, you configure your ingress to proxy TCP traffic to the docker-mailserver pod using the PROXY protocol, which preserves the real client IP.","title":"Proxy Port to Service via PROXY Protocol"},{"location":"config/advanced/kubernetes/#configure-your-ingress","text":"With an NGINX ingress controller , set externalTrafficPolicy: Local for its service, and add the following to the TCP services config map (as described here ): 25 : \"mailserver/mailserver:25::PROXY\" 465 : \"mailserver/mailserver:465::PROXY\" 587 : \"mailserver/mailserver:587::PROXY\" 993 : \"mailserver/mailserver:993::PROXY\" HAProxy With HAProxy , the configuration should look similar to the above. If you know what it actually looks like, add an example here.","title":"Configure your Ingress"},{"location":"config/advanced/kubernetes/#configure-the-mailserver","text":"Then, configure both Postfix and Dovecot to expect the PROXY protocol: HAProxy Example kind : ConfigMap apiVersion : v1 metadata : name : mailserver.config labels : app : mailserver data : postfix-main.cf : | postscreen_upstream_proxy_protocol = haproxy postfix-master.cf : | smtp/inet/postscreen_upstream_proxy_protocol=haproxy submission/inet/smtpd_upstream_proxy_protocol=haproxy smtps/inet/smtpd_upstream_proxy_protocol=haproxy dovecot.cf : | # Assuming your ingress controller is bound to 10.0.0.0/8 haproxy_trusted_networks = 10.0.0.0/8, 127.0.0.0/8 service imap-login { inet_listener imap { haproxy = yes } inet_listener imaps { haproxy = yes } } # ... --- kind : Deployment apiVersion : extensions/v1beta1 metadata : name : mailserver spec : template : spec : containers : - name : docker-mailserver volumeMounts : - name : config subPath : postfix-main.cf mountPath : /tmp/docker-mailserver/postfix-main.cf readOnly : true - name : config subPath : postfix-master.cf mountPath : /tmp/docker-mailserver/postfix-master.cf readOnly : true - name : config subPath : dovecot.cf mountPath : /tmp/docker-mailserver/dovecot.cf readOnly : true With this approach, it is not possible to access docker-mailserver via cluster-DNS, as the PROXY protocol is required for incoming connections.","title":"Configure the Mailserver"},{"location":"config/advanced/mail-fetchmail/","text":"To enable the fetchmail service to retrieve e-mails set the environment variable ENABLE_FETCHMAIL to 1 . Your docker-compose.yml file should look like following snippet: environment : - ENABLE_FETCHMAIL=1 - FETCHMAIL_POLL=300 Generate a file called fetchmail.cf and place it in the docker-data/dms/config/ folder. Your docker-mailserver folder should look like this example: \u251c\u2500\u2500 docker-data/dms/config \u2502 \u251c\u2500\u2500 dovecot.cf \u2502 \u251c\u2500\u2500 fetchmail.cf \u2502 \u251c\u2500\u2500 postfix-accounts.cf \u2502 \u2514\u2500\u2500 postfix-virtual.cf \u251c\u2500\u2500 docker-compose.yml \u2514\u2500\u2500 README.md Configuration A detailed description of the configuration options can be found in the online version of the manual page . IMAP Configuration Example poll 'imap.gmail.com' proto imap user 'username' pass 'secret' is 'user1@example.com' ssl POP3 Configuration Example poll 'pop3.gmail.com' proto pop3 user 'username' pass 'secret' is 'user2@example.com' ssl Caution Don\u2019t forget the last line! ( eg: is 'user1@example.com' ). After is , you have to specify an email address from the configuration file: docker-data/dms/config/postfix-accounts.cf . More details how to configure fetchmail can be found in the fetchmail man page in the chapter \u201cThe run control file\u201d . Polling Interval By default the fetchmail service searches every 5 minutes for new mails on your external mail accounts. You can override this default value by changing the ENV variable FETCHMAIL_POLL : environment : - FETCHMAIL_POLL=60 You must specify a numeric argument which is a polling interval in seconds. The example above polls every minute for new mails. Debugging To debug your fetchmail.cf configuration run this command: ./setup.sh debug fetchmail For more informations about the configuration script setup.sh read the corresponding docs . Here a sample output of ./setup.sh debug fetchmail : fetchmail: 6.3.26 querying outlook.office365.com (protocol POP3) at Mon Aug 29 22:11:09 2016: poll started Trying to connect to 132.245.48.18/995...connected. fetchmail: Server certificate: fetchmail: Issuer Organization: Microsoft Corporation fetchmail: Issuer CommonName: Microsoft IT SSL SHA2 fetchmail: Subject CommonName: outlook.com fetchmail: Subject Alternative Name: outlook.com fetchmail: Subject Alternative Name: *.outlook.com fetchmail: Subject Alternative Name: office365.com fetchmail: Subject Alternative Name: *.office365.com fetchmail: Subject Alternative Name: *.live.com fetchmail: Subject Alternative Name: *.internal.outlook.com fetchmail: Subject Alternative Name: *.outlook.office365.com fetchmail: Subject Alternative Name: outlook.office.com fetchmail: Subject Alternative Name: attachment.outlook.office.net fetchmail: Subject Alternative Name: attachment.outlook.officeppe.net fetchmail: Subject Alternative Name: *.office.com fetchmail: outlook.office365.com key fingerprint: 3A:A4:58:42:56:CD:BD:11:19:5B:CF:1E:85:16:8E:4D fetchmail: POP3< +OK The Microsoft Exchange POP3 service is ready. [SABFADEAUABSADAAMQBDAEEAMAAwADAANwAuAGUAdQByAHAAcgBkADAAMQAuAHAAcgBvAGQALgBlAHgAYwBoAGEAbgBnAGUAbABhAGIAcwAuAGMAbwBtAA==] fetchmail: POP3> CAPA fetchmail: POP3< +OK fetchmail: POP3< TOP fetchmail: POP3< UIDL fetchmail: POP3< SASL PLAIN fetchmail: POP3< USER fetchmail: POP3< . fetchmail: POP3> USER user1@outlook.com fetchmail: POP3< +OK fetchmail: POP3> PASS * fetchmail: POP3< +OK User successfully logged on. fetchmail: POP3> STAT fetchmail: POP3< +OK 0 0 fetchmail: No mail for user1@outlook.com at outlook.office365.com fetchmail: POP3> QUIT fetchmail: POP3< +OK Microsoft Exchange Server 2016 POP3 server signing off. fetchmail: 6.3.26 querying outlook.office365.com (protocol POP3) at Mon Aug 29 22:11:11 2016: poll completed fetchmail: normal termination, status 1","title":"Email Gathering with Fetchmail"},{"location":"config/advanced/mail-fetchmail/#configuration","text":"A detailed description of the configuration options can be found in the online version of the manual page .","title":"Configuration"},{"location":"config/advanced/mail-fetchmail/#imap-configuration","text":"Example poll 'imap.gmail.com' proto imap user 'username' pass 'secret' is 'user1@example.com' ssl","title":"IMAP Configuration"},{"location":"config/advanced/mail-fetchmail/#pop3-configuration","text":"Example poll 'pop3.gmail.com' proto pop3 user 'username' pass 'secret' is 'user2@example.com' ssl Caution Don\u2019t forget the last line! ( eg: is 'user1@example.com' ). After is , you have to specify an email address from the configuration file: docker-data/dms/config/postfix-accounts.cf . More details how to configure fetchmail can be found in the fetchmail man page in the chapter \u201cThe run control file\u201d .","title":"POP3 Configuration"},{"location":"config/advanced/mail-fetchmail/#polling-interval","text":"By default the fetchmail service searches every 5 minutes for new mails on your external mail accounts. You can override this default value by changing the ENV variable FETCHMAIL_POLL : environment : - FETCHMAIL_POLL=60 You must specify a numeric argument which is a polling interval in seconds. The example above polls every minute for new mails.","title":"Polling Interval"},{"location":"config/advanced/mail-fetchmail/#debugging","text":"To debug your fetchmail.cf configuration run this command: ./setup.sh debug fetchmail For more informations about the configuration script setup.sh read the corresponding docs . Here a sample output of ./setup.sh debug fetchmail : fetchmail: 6.3.26 querying outlook.office365.com (protocol POP3) at Mon Aug 29 22:11:09 2016: poll started Trying to connect to 132.245.48.18/995...connected. fetchmail: Server certificate: fetchmail: Issuer Organization: Microsoft Corporation fetchmail: Issuer CommonName: Microsoft IT SSL SHA2 fetchmail: Subject CommonName: outlook.com fetchmail: Subject Alternative Name: outlook.com fetchmail: Subject Alternative Name: *.outlook.com fetchmail: Subject Alternative Name: office365.com fetchmail: Subject Alternative Name: *.office365.com fetchmail: Subject Alternative Name: *.live.com fetchmail: Subject Alternative Name: *.internal.outlook.com fetchmail: Subject Alternative Name: *.outlook.office365.com fetchmail: Subject Alternative Name: outlook.office.com fetchmail: Subject Alternative Name: attachment.outlook.office.net fetchmail: Subject Alternative Name: attachment.outlook.officeppe.net fetchmail: Subject Alternative Name: *.office.com fetchmail: outlook.office365.com key fingerprint: 3A:A4:58:42:56:CD:BD:11:19:5B:CF:1E:85:16:8E:4D fetchmail: POP3< +OK The Microsoft Exchange POP3 service is ready. [SABFADEAUABSADAAMQBDAEEAMAAwADAANwAuAGUAdQByAHAAcgBkADAAMQAuAHAAcgBvAGQALgBlAHgAYwBoAGEAbgBnAGUAbABhAGIAcwAuAGMAbwBtAA==] fetchmail: POP3> CAPA fetchmail: POP3< +OK fetchmail: POP3< TOP fetchmail: POP3< UIDL fetchmail: POP3< SASL PLAIN fetchmail: POP3< USER fetchmail: POP3< . fetchmail: POP3> USER user1@outlook.com fetchmail: POP3< +OK fetchmail: POP3> PASS * fetchmail: POP3< +OK User successfully logged on. fetchmail: POP3> STAT fetchmail: POP3< +OK 0 0 fetchmail: No mail for user1@outlook.com at outlook.office365.com fetchmail: POP3> QUIT fetchmail: POP3< +OK Microsoft Exchange Server 2016 POP3 server signing off. fetchmail: 6.3.26 querying outlook.office365.com (protocol POP3) at Mon Aug 29 22:11:11 2016: poll completed fetchmail: normal termination, status 1","title":"Debugging"},{"location":"config/advanced/mail-sieve/","text":"User-Defined Sieve Filters Sieve allows to specify filtering rules for incoming emails that allow for example sorting mails into different folders depending on the title of an email. There are global and user specific filters which are filtering the incoming emails in the following order: Global-before -> User specific -> Global-after Global filters are applied to EVERY incoming mail for EVERY email address. To specify a global Sieve filter provide a docker-data/dms/config/before.dovecot.sieve or a docker-data/dms/config/after.dovecot.sieve file with your filter rules. If any filter in this filtering chain discards an incoming mail, the delivery process will stop as well and the mail will not reach any following filters(e.g. global-before stops an incoming spam mail: The mail will get discarded and a user-specific filter won't get applied.) To specify a user-defined Sieve filter place a .dovecot.sieve file into a virtual user's mail folder e.g. /var/mail/example.com/user1/.dovecot.sieve . If this file exists dovecot will apply the filtering rules. It's even possible to install a user provided Sieve filter at startup during users setup: simply include a Sieve file in the docker-data/dms/config/ path for each user login that needs a filter. The file name provided should be in the form .dovecot.sieve , so for example for user1@example.com you should provide a Sieve file named docker-data/dms/config/user1@example.com.dovecot.sieve . An example of a sieve filter that moves mails to a folder INBOX/spam depending on the sender address: Example require [ \"fileinto\" , \"reject\" ]; if address :contains [ \"From\" ] \"spam@spam.com\" { fileinto \"INBOX.spam\" ; } else { keep ; } Warning That folders have to exist beforehand if sieve should move them. Another example of a sieve filter that forward mails to a different address: Example require [ \"copy\" ]; redirect :copy \"user2@not-example.com\" ; Just forward all incoming emails and do not save them locally: Example redirect \"user2@not-example.com\" ; You can also use external programs to filter or pipe (process) messages by adding executable scripts in docker-data/dms/config/sieve-pipe or docker-data/dms/config/sieve-filter . This can be used in lieu of a local alias file, for instance to forward an email to a webservice. These programs can then be referenced by filename, by all users. Note that the process running the scripts run as a privileged user. For further information see Dovecot's wiki . require [ \"vnd.dovecot.pipe\" ]; pipe \"external-program\" ; For more examples or a detailed description of the Sieve language have a look at the official site . Other resources are available on the internet where you can find several examples . Automatic Sorting Based on Subaddresses It is possible to sort subaddresses such as user+mailing-lists@example.com into a corresponding folder (here: INBOX/Mailing-lists ) automatically. require [ \"envelope\" , \"fileinto\" , \"mailbox\" , \"subaddress\" , \"variables\" ]; if envelope :detail :matches \"to\" \"*\" { set :lower :upperfirst \"tag\" \"${1}\" ; if mailbox exists \"INBOX.${1}\" { fileinto \"INBOX.${1}\" ; } else { fileinto :create \"INBOX.${tag}\" ; } } Manage Sieve The Manage Sieve extension allows users to modify their Sieve script by themselves. The authentication mechanisms are the same as for the main dovecot service. ManageSieve runs on port 4190 and needs to be enabled using the ENABLE_MANAGESIEVE=1 environment variable. Example # docker-compose.yml ports : - \"4190:4190\" environment : - ENABLE_MANAGESIEVE=1 All user defined sieve scripts that are managed by ManageSieve are stored in the user's home folder in /var/mail/example.com/user1/sieve . Just one sieve script might be active for a user and is sym-linked to /var/mail/example.com/user1/.dovecot.sieve automatically. Note ManageSieve makes sure to not overwrite an existing .dovecot.sieve file. If a user activates a new sieve script the old one is backuped and moved to the sieve folder. The extension is known to work with the following ManageSieve clients: Sieve Editor a portable standalone application based on the former Thunderbird plugin. Kmail the mail client of KDE 's Kontact Suite.","title":"Email Filtering with Sieve"},{"location":"config/advanced/mail-sieve/#user-defined-sieve-filters","text":"Sieve allows to specify filtering rules for incoming emails that allow for example sorting mails into different folders depending on the title of an email. There are global and user specific filters which are filtering the incoming emails in the following order: Global-before -> User specific -> Global-after Global filters are applied to EVERY incoming mail for EVERY email address. To specify a global Sieve filter provide a docker-data/dms/config/before.dovecot.sieve or a docker-data/dms/config/after.dovecot.sieve file with your filter rules. If any filter in this filtering chain discards an incoming mail, the delivery process will stop as well and the mail will not reach any following filters(e.g. global-before stops an incoming spam mail: The mail will get discarded and a user-specific filter won't get applied.) To specify a user-defined Sieve filter place a .dovecot.sieve file into a virtual user's mail folder e.g. /var/mail/example.com/user1/.dovecot.sieve . If this file exists dovecot will apply the filtering rules. It's even possible to install a user provided Sieve filter at startup during users setup: simply include a Sieve file in the docker-data/dms/config/ path for each user login that needs a filter. The file name provided should be in the form .dovecot.sieve , so for example for user1@example.com you should provide a Sieve file named docker-data/dms/config/user1@example.com.dovecot.sieve . An example of a sieve filter that moves mails to a folder INBOX/spam depending on the sender address: Example require [ \"fileinto\" , \"reject\" ]; if address :contains [ \"From\" ] \"spam@spam.com\" { fileinto \"INBOX.spam\" ; } else { keep ; } Warning That folders have to exist beforehand if sieve should move them. Another example of a sieve filter that forward mails to a different address: Example require [ \"copy\" ]; redirect :copy \"user2@not-example.com\" ; Just forward all incoming emails and do not save them locally: Example redirect \"user2@not-example.com\" ; You can also use external programs to filter or pipe (process) messages by adding executable scripts in docker-data/dms/config/sieve-pipe or docker-data/dms/config/sieve-filter . This can be used in lieu of a local alias file, for instance to forward an email to a webservice. These programs can then be referenced by filename, by all users. Note that the process running the scripts run as a privileged user. For further information see Dovecot's wiki . require [ \"vnd.dovecot.pipe\" ]; pipe \"external-program\" ; For more examples or a detailed description of the Sieve language have a look at the official site . Other resources are available on the internet where you can find several examples .","title":"User-Defined Sieve Filters"},{"location":"config/advanced/mail-sieve/#automatic-sorting-based-on-subaddresses","text":"It is possible to sort subaddresses such as user+mailing-lists@example.com into a corresponding folder (here: INBOX/Mailing-lists ) automatically. require [ \"envelope\" , \"fileinto\" , \"mailbox\" , \"subaddress\" , \"variables\" ]; if envelope :detail :matches \"to\" \"*\" { set :lower :upperfirst \"tag\" \"${1}\" ; if mailbox exists \"INBOX.${1}\" { fileinto \"INBOX.${1}\" ; } else { fileinto :create \"INBOX.${tag}\" ; } }","title":"Automatic Sorting Based on Subaddresses"},{"location":"config/advanced/mail-sieve/#manage-sieve","text":"The Manage Sieve extension allows users to modify their Sieve script by themselves. The authentication mechanisms are the same as for the main dovecot service. ManageSieve runs on port 4190 and needs to be enabled using the ENABLE_MANAGESIEVE=1 environment variable. Example # docker-compose.yml ports : - \"4190:4190\" environment : - ENABLE_MANAGESIEVE=1 All user defined sieve scripts that are managed by ManageSieve are stored in the user's home folder in /var/mail/example.com/user1/sieve . Just one sieve script might be active for a user and is sym-linked to /var/mail/example.com/user1/.dovecot.sieve automatically. Note ManageSieve makes sure to not overwrite an existing .dovecot.sieve file. If a user activates a new sieve script the old one is backuped and moved to the sieve folder. The extension is known to work with the following ManageSieve clients: Sieve Editor a portable standalone application based on the former Thunderbird plugin. Kmail the mail client of KDE 's Kontact Suite.","title":"Manage Sieve"},{"location":"config/advanced/optional-config/","text":"This is a list of all configuration files and directories which are optional or automatically generated in your docker-data/dms/config/ directory. Directories sieve-filter: directory for sieve filter scripts. (Docs: Sieve ) sieve-pipe: directory for sieve pipe scripts. (Docs: Sieve ) opendkim: DKIM directory. Auto-configurable via setup.sh config dkim . (Docs: DKIM ) ssl: SSL Certificate directory if SSL_TYPE is set to self-signed or custom . (Docs: SSL ) Files {user_email_address}.dovecot.sieve: User specific Sieve filter file. (Docs: Sieve ) before.dovecot.sieve: Global Sieve filter file, applied prior to the ${login}.dovecot.sieve filter. (Docs: Sieve ) after.dovecot.sieve : Global Sieve filter file, applied after the ${login}.dovecot.sieve filter. (Docs: Sieve ) postfix-main.cf: Every line will be added to the postfix main configuration. (Docs: Override Postfix Defaults ) postfix-master.cf: Every line will be added to the postfix master configuration. (Docs: Override Postfix Defaults ) postfix-accounts.cf: User accounts file. Modify via the setup.sh email script. postfix-send-access.cf: List of users denied sending. Modify via setup.sh email restrict . postfix-receive-access.cf: List of users denied receiving. Modify via setup.sh email restrict . postfix-virtual.cf: Alias configuration file. Modify via setup.sh alias . postfix-sasl-password.cf: listing of relayed domains with their respective : . Modify via setup.sh relay add-auth [] . (Docs: Relay-Hosts Auth ) postfix-relaymap.cf: domain-specific relays and exclusions. Modify via setup.sh relay add-domain and setup.sh relay exclude-domain . (Docs: Relay-Hosts Senders ) postfix-regexp.cf: Regular expression alias file. (Docs: Aliases ) ldap-users.cf: Configuration for the virtual user mapping virtual_mailbox_maps . See the setup-stack.sh script. ldap-groups.cf: Configuration for the virtual alias mapping virtual_alias_maps . See the setup-stack.sh script. ldap-aliases.cf: Configuration for the virtual alias mapping virtual_alias_maps . See the setup-stack.sh script. ldap-domains.cf: Configuration for the virtual domain mapping virtual_mailbox_domains . See the setup-stack.sh script. whitelist_clients.local: Whitelisted domains, not considered by postgrey. Enter one host or domain per line. spamassassin-rules.cf: Antispam rules for Spamassassin. (Docs: FAQ - SpamAssassin Rules ) fail2ban-fail2ban.cf: Additional config options for fail2ban.cf . (Docs: Fail2Ban ) fail2ban-jail.cf: Additional config options for fail2ban's jail behaviour. (Docs: Fail2Ban ) amavis.cf: replaces the /etc/amavis/conf.d/50-user file dovecot.cf: replaces /etc/dovecot/local.conf . (Docs: Override Dovecot Defaults ) dovecot-quotas.cf: list of custom quotas per mailbox. (Docs: Accounts ) user-patches.sh: this file will be run after all configuration files are set up, but before the postfix, amavis and other daemons are started. (Docs: FAQ - How to adjust settings with the user-patches.sh script ) rspamd-commands: list of simple commands to adjust Rspamd modules in an easy way (Docs: Rspamd )","title":"Optional Configuration"},{"location":"config/advanced/optional-config/#directories","text":"sieve-filter: directory for sieve filter scripts. (Docs: Sieve ) sieve-pipe: directory for sieve pipe scripts. (Docs: Sieve ) opendkim: DKIM directory. Auto-configurable via setup.sh config dkim . (Docs: DKIM ) ssl: SSL Certificate directory if SSL_TYPE is set to self-signed or custom . (Docs: SSL )","title":"Directories"},{"location":"config/advanced/optional-config/#files","text":"{user_email_address}.dovecot.sieve: User specific Sieve filter file. (Docs: Sieve ) before.dovecot.sieve: Global Sieve filter file, applied prior to the ${login}.dovecot.sieve filter. (Docs: Sieve ) after.dovecot.sieve : Global Sieve filter file, applied after the ${login}.dovecot.sieve filter. (Docs: Sieve ) postfix-main.cf: Every line will be added to the postfix main configuration. (Docs: Override Postfix Defaults ) postfix-master.cf: Every line will be added to the postfix master configuration. (Docs: Override Postfix Defaults ) postfix-accounts.cf: User accounts file. Modify via the setup.sh email script. postfix-send-access.cf: List of users denied sending. Modify via setup.sh email restrict . postfix-receive-access.cf: List of users denied receiving. Modify via setup.sh email restrict . postfix-virtual.cf: Alias configuration file. Modify via setup.sh alias . postfix-sasl-password.cf: listing of relayed domains with their respective : . Modify via setup.sh relay add-auth [] . (Docs: Relay-Hosts Auth ) postfix-relaymap.cf: domain-specific relays and exclusions. Modify via setup.sh relay add-domain and setup.sh relay exclude-domain . (Docs: Relay-Hosts Senders ) postfix-regexp.cf: Regular expression alias file. (Docs: Aliases ) ldap-users.cf: Configuration for the virtual user mapping virtual_mailbox_maps . See the setup-stack.sh script. ldap-groups.cf: Configuration for the virtual alias mapping virtual_alias_maps . See the setup-stack.sh script. ldap-aliases.cf: Configuration for the virtual alias mapping virtual_alias_maps . See the setup-stack.sh script. ldap-domains.cf: Configuration for the virtual domain mapping virtual_mailbox_domains . See the setup-stack.sh script. whitelist_clients.local: Whitelisted domains, not considered by postgrey. Enter one host or domain per line. spamassassin-rules.cf: Antispam rules for Spamassassin. (Docs: FAQ - SpamAssassin Rules ) fail2ban-fail2ban.cf: Additional config options for fail2ban.cf . (Docs: Fail2Ban ) fail2ban-jail.cf: Additional config options for fail2ban's jail behaviour. (Docs: Fail2Ban ) amavis.cf: replaces the /etc/amavis/conf.d/50-user file dovecot.cf: replaces /etc/dovecot/local.conf . (Docs: Override Dovecot Defaults ) dovecot-quotas.cf: list of custom quotas per mailbox. (Docs: Accounts ) user-patches.sh: this file will be run after all configuration files are set up, but before the postfix, amavis and other daemons are started. (Docs: FAQ - How to adjust settings with the user-patches.sh script ) rspamd-commands: list of simple commands to adjust Rspamd modules in an easy way (Docs: Rspamd )","title":"Files"},{"location":"config/advanced/podman/","text":"Introduction Podman is a daemonless container engine for developing, managing, and running OCI Containers on your Linux System. About Support for Podman Please note that Podman is not officially supported as docker-mailserver is built and verified on top of the Docker Engine . This content is entirely community supported. If you find errors, please open an issue and provide a PR. About this Guide This guide was tested with Fedora 34 using systemd and firewalld . Moreover, it requires Podman version >= 3.2. You may be able to substitute dnf - Fedora's package manager - with others such as apt . About Security Running podman in rootless mode requires additional modifications in order to keep your mailserver secure. Make sure to read the related documentation. Installation in Rootfull Mode While using Podman, you can just manage docker-mailserver as what you did with Docker. Your best friend setup.sh includes the minimum code in order to support Podman since it's 100% compatible with the Docker CLI. The installation is basically the same. Podman v3.2 introduced a RESTful API that is 100% compatible with the Docker API, so you can use docker-compose with Podman easily. Install Podman and docker-compose with your package manager first. sudo dnf install podman docker-compose Then enable podman.socket using systemctl . systemctl enable --now podman.socket This will create a unix socket locate under /run/podman/podman.sock , which is the entrypoint of Podman's API. Now, configure docker-mailserver and start it. export DOCKER_HOST = \"unix:///run/podman/podman.sock\" docker-compose up -d mailserver docker-compose ps You should see that docker-mailserver is running now. Self-start in Rootfull Mode Podman is daemonless, that means if you want docker-mailserver self-start while boot up the system, you have to generate a systemd file with Podman CLI. podman generate systemd mailserver > /etc/systemd/system/mailserver.service systemctl daemon-reload systemctl enable --now mailserver.service Installation in Rootless Mode Running rootless containers is one of Podman's major features. But due to some restrictions, deploying docker-mailserver in rootless mode is not as easy compared to rootfull mode. a rootless container is running in a user namespace so you cannot bind ports lower than 1024 a rootless container's systemd file can only be placed in folder under ~/.config a rootless container can result in an open relay, make sure to read the security section . Also notice that Podman's rootless mode is not about running as a non-root user inside the container, but about the mapping of (normal, non-root) host users to root inside the container. Warning In order to make rootless docker-mailserver work we must modify some settings in the Linux system, it requires some basic linux server knowledge so don't follow this guide if you not sure what this guide is talking about. Podman rootfull mode and Docker are still good and security enough for normal daily usage. First, enable podman.socket in systemd's userspace with a non-root user. systemctl enable --now --user podman.socket The socket file should be located at /var/run/user/$(id -u)/podman/podman.sock . Then, modify docker-compose.yml to make sure all ports are bindings are on non-privileged ports. services : mailserver : ports : - \"10025:25\" # SMTP (explicit TLS => STARTTLS) - \"10143:143\" # IMAP4 (explicit TLS => STARTTLS) - \"10465:465\" # ESMTP (implicit TLS) - \"10587:587\" # ESMTP (explicit TLS => STARTTLS) - \"10993:993\" # IMAP4 (implicit TLS) Then, setup your mailserver.env file follow the documentation and use docker-compose to start the container. export DOCKER_HOST = \"unix:///var/run/user/ $( id -u ) /podman/podman.sock\" docker-compose up -d mailserver docker-compose ps Security in Rootless Mode In rootless mode, podman resolves all incoming IPs as localhost, which results in an open gateway in the default configuration. There are two workarounds to fix this problem, both of which have their own drawbacks. Enforce authentication from localhost The PERMIT_DOCKER variable in the mailserver.env file allows to specify trusted networks that do not need to authenticate. If the variable is left empty, only requests from localhost and the container IP are allowed, but in the case of rootless podman any IP will be resolved as localhost. Setting PERMIT_DOCKER=none enforces authentication also from localhost, which prevents sending unauthenticated emails. Use the slip4netns network driver The second workaround is slightly more complicated because the docker-compose.yml has to be modified. As shown in the fail2ban section the slirp4netns network driver has to be enabled. This network driver enables podman to correctly resolve IP addresses but it is not compatible with user defined networks which might be a problem depending on your setup. Rootless Podman requires adding the value slirp4netns:port_handler=slirp4netns to the --network CLI option, or network_mode setting in your docker-compose.yml . You must also add the ENV NETWORK_INTERFACE=tap0 , because Podman uses a hard-coded interface name for slirp4netns . Example services : mailserver : network_mode : \"slirp4netns:port_handler=slirp4netns\" environment : - NETWORK_INTERFACE=tap0 ... Note podman-compose is not compatible with this configuration. Self-start in Rootless Mode Generate a systemd file with the Podman CLI. podman generate systemd mailserver > ~/.config/systemd/user/mailserver.service systemctl --user daemon-reload systemctl enable --user --now mailserver.service Systemd's user space service is only started when a specific user logs in and stops when you log out. In order to make it to start with the system, we need to enable linger with loginctl loginctl enable-linger Remember to run this command as root user. Port Forwarding When it comes to forwarding ports using firewalld , see https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/securing_networks/using-and-configuring-firewalld_securing-networks#port-forwarding_using-and-configuring-firewalld for more information. firewall-cmd --permanent --add-forward-port = port = < 25 | 143 | 465 | 587 | 993 >:proto = :toport = < 10025 | 10143 | 10465 | 10587 | 10993 > ... # After you set all ports up. firewall-cmd --reload Notice that this will only open the access to the external client. If you want to access privileges port in your server, do this: firewall-cmd --permanent --direct --add-rule nat OUTPUT 0 -p -o lo --dport < 25 | 143 | 465 | 587 | 993 > -j REDIRECT --to-ports < 10025 | 10143 | 10465 | 10587 | 10993 > ... # After you set all ports up. firewall-cmd --reload Just map all the privilege port with non-privilege port you set in docker-compose.yml before as root user.","title":"Podman"},{"location":"config/advanced/podman/#introduction","text":"Podman is a daemonless container engine for developing, managing, and running OCI Containers on your Linux System. About Support for Podman Please note that Podman is not officially supported as docker-mailserver is built and verified on top of the Docker Engine . This content is entirely community supported. If you find errors, please open an issue and provide a PR. About this Guide This guide was tested with Fedora 34 using systemd and firewalld . Moreover, it requires Podman version >= 3.2. You may be able to substitute dnf - Fedora's package manager - with others such as apt . About Security Running podman in rootless mode requires additional modifications in order to keep your mailserver secure. Make sure to read the related documentation.","title":"Introduction"},{"location":"config/advanced/podman/#installation-in-rootfull-mode","text":"While using Podman, you can just manage docker-mailserver as what you did with Docker. Your best friend setup.sh includes the minimum code in order to support Podman since it's 100% compatible with the Docker CLI. The installation is basically the same. Podman v3.2 introduced a RESTful API that is 100% compatible with the Docker API, so you can use docker-compose with Podman easily. Install Podman and docker-compose with your package manager first. sudo dnf install podman docker-compose Then enable podman.socket using systemctl . systemctl enable --now podman.socket This will create a unix socket locate under /run/podman/podman.sock , which is the entrypoint of Podman's API. Now, configure docker-mailserver and start it. export DOCKER_HOST = \"unix:///run/podman/podman.sock\" docker-compose up -d mailserver docker-compose ps You should see that docker-mailserver is running now.","title":"Installation in Rootfull Mode"},{"location":"config/advanced/podman/#self-start-in-rootfull-mode","text":"Podman is daemonless, that means if you want docker-mailserver self-start while boot up the system, you have to generate a systemd file with Podman CLI. podman generate systemd mailserver > /etc/systemd/system/mailserver.service systemctl daemon-reload systemctl enable --now mailserver.service","title":"Self-start in Rootfull Mode"},{"location":"config/advanced/podman/#installation-in-rootless-mode","text":"Running rootless containers is one of Podman's major features. But due to some restrictions, deploying docker-mailserver in rootless mode is not as easy compared to rootfull mode. a rootless container is running in a user namespace so you cannot bind ports lower than 1024 a rootless container's systemd file can only be placed in folder under ~/.config a rootless container can result in an open relay, make sure to read the security section . Also notice that Podman's rootless mode is not about running as a non-root user inside the container, but about the mapping of (normal, non-root) host users to root inside the container. Warning In order to make rootless docker-mailserver work we must modify some settings in the Linux system, it requires some basic linux server knowledge so don't follow this guide if you not sure what this guide is talking about. Podman rootfull mode and Docker are still good and security enough for normal daily usage. First, enable podman.socket in systemd's userspace with a non-root user. systemctl enable --now --user podman.socket The socket file should be located at /var/run/user/$(id -u)/podman/podman.sock . Then, modify docker-compose.yml to make sure all ports are bindings are on non-privileged ports. services : mailserver : ports : - \"10025:25\" # SMTP (explicit TLS => STARTTLS) - \"10143:143\" # IMAP4 (explicit TLS => STARTTLS) - \"10465:465\" # ESMTP (implicit TLS) - \"10587:587\" # ESMTP (explicit TLS => STARTTLS) - \"10993:993\" # IMAP4 (implicit TLS) Then, setup your mailserver.env file follow the documentation and use docker-compose to start the container. export DOCKER_HOST = \"unix:///var/run/user/ $( id -u ) /podman/podman.sock\" docker-compose up -d mailserver docker-compose ps","title":"Installation in Rootless Mode"},{"location":"config/advanced/podman/#security-in-rootless-mode","text":"In rootless mode, podman resolves all incoming IPs as localhost, which results in an open gateway in the default configuration. There are two workarounds to fix this problem, both of which have their own drawbacks.","title":"Security in Rootless Mode"},{"location":"config/advanced/podman/#enforce-authentication-from-localhost","text":"The PERMIT_DOCKER variable in the mailserver.env file allows to specify trusted networks that do not need to authenticate. If the variable is left empty, only requests from localhost and the container IP are allowed, but in the case of rootless podman any IP will be resolved as localhost. Setting PERMIT_DOCKER=none enforces authentication also from localhost, which prevents sending unauthenticated emails.","title":"Enforce authentication from localhost"},{"location":"config/advanced/podman/#use-the-slip4netns-network-driver","text":"The second workaround is slightly more complicated because the docker-compose.yml has to be modified. As shown in the fail2ban section the slirp4netns network driver has to be enabled. This network driver enables podman to correctly resolve IP addresses but it is not compatible with user defined networks which might be a problem depending on your setup. Rootless Podman requires adding the value slirp4netns:port_handler=slirp4netns to the --network CLI option, or network_mode setting in your docker-compose.yml . You must also add the ENV NETWORK_INTERFACE=tap0 , because Podman uses a hard-coded interface name for slirp4netns . Example services : mailserver : network_mode : \"slirp4netns:port_handler=slirp4netns\" environment : - NETWORK_INTERFACE=tap0 ... Note podman-compose is not compatible with this configuration.","title":"Use the slip4netns network driver"},{"location":"config/advanced/podman/#self-start-in-rootless-mode","text":"Generate a systemd file with the Podman CLI. podman generate systemd mailserver > ~/.config/systemd/user/mailserver.service systemctl --user daemon-reload systemctl enable --user --now mailserver.service Systemd's user space service is only started when a specific user logs in and stops when you log out. In order to make it to start with the system, we need to enable linger with loginctl loginctl enable-linger Remember to run this command as root user.","title":"Self-start in Rootless Mode"},{"location":"config/advanced/podman/#port-forwarding","text":"When it comes to forwarding ports using firewalld , see https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/securing_networks/using-and-configuring-firewalld_securing-networks#port-forwarding_using-and-configuring-firewalld for more information. firewall-cmd --permanent --add-forward-port = port = < 25 | 143 | 465 | 587 | 993 >:proto = :toport = < 10025 | 10143 | 10465 | 10587 | 10993 > ... # After you set all ports up. firewall-cmd --reload Notice that this will only open the access to the external client. If you want to access privileges port in your server, do this: firewall-cmd --permanent --direct --add-rule nat OUTPUT 0 -p -o lo --dport < 25 | 143 | 465 | 587 | 993 > -j REDIRECT --to-ports < 10025 | 10143 | 10465 | 10587 | 10993 > ... # After you set all ports up. firewall-cmd --reload Just map all the privilege port with non-privilege port you set in docker-compose.yml before as root user.","title":"Port Forwarding"},{"location":"config/advanced/mail-forwarding/aws-ses/","text":"Amazon SES (Simple Email Service) is intended to provide a simple way for cloud based applications to send email and receive email. For the purposes of this project only sending email via SES is supported. Older versions of docker-mailserver used AWS_SES_HOST and AWS_SES_USERPASS to configure sending, this has changed and the setup is mananged through Configure Relay Hosts . You will need to create some Amazon SES SMTP credentials . The SMTP credentials you create will be used to populate the RELAY_USER and RELAY_PASSWORD environment variables. The RELAY_HOST should match your AWS SES region , the RELAY_PORT will be 587. If all of your email is being forwarded through AWS SES, DEFAULT_RELAY_HOST should be set accordingly. Example: DEFAULT_RELAY_HOST=[email-smtp.us-west-2.amazonaws.com]:587 Note If you set up AWS Easy DKIM you can safely skip setting up DKIM as the AWS SES will take care of signing your outgoing email. To verify proper operation, send an email to some external account of yours and inspect the mail headers. You will also see the connection to SES in the mail logs. For example: May 23 07:09:36 mail postfix/smtp[692]: Trusted TLS connection established to email-smtp.us-east-1.amazonaws.com[107.20.142.169]:25: TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits) May 23 07:09:36 mail postfix/smtp[692]: 8C82A7E7: to=, relay=email-smtp.us-east-1.amazonaws.com[107.20.142.169]:25, delay=0.35, delays=0/0.02/0.13/0.2, dsn=2.0.0, status=sent (250 Ok 01000154dc729264-93fdd7ea-f039-43d6-91ed-653e8547867c-000000)","title":"AWS SES"},{"location":"config/advanced/mail-forwarding/relay-hosts/","text":"Introduction Rather than having Postfix deliver mail directly, you can configure Postfix to send mail via another mail relay (smarthost). Examples include Mailgun , Sendgrid and AWS SES . Depending on the domain of the sender, you may want to send via a different relay, or authenticate in a different way. Basic Configuration Basic configuration is done via environment variables: RELAY_HOST : default host to relay mail through, empty (aka '', or no ENV set) will disable this feature RELAY_PORT : port on default relay, defaults to port 25 RELAY_USER : username for the default relay RELAY_PASSWORD : password for the default user Setting these environment variables will cause mail for all sender domains to be routed via the specified host, authenticating with the user/password combination. Warning For users of the previous AWS_SES_* variables: please update your configuration to use these new variables, no other configuration is required. Advanced Configuration Sender-dependent Authentication Sender dependent authentication is done in docker-data/dms/config/postfix-sasl-password.cf . You can create this file manually, or use: setup.sh relay add-auth [ ] An example configuration file looks like this: @domain1.com relay_user_1:password_1 @domain2.com relay_user_2:password_2 If there is no other configuration, this will cause Postfix to deliver email through the relay specified in RELAY_HOST env variable, authenticating as relay_user_1 when sent from domain1.com and authenticating as relay_user_2 when sending from domain2.com . Note To activate the configuration you must either restart the container, or you can also trigger an update by modifying a mail account. Sender-dependent Relay Host Sender dependent relay hosts are configured in docker-data/dms/config/postfix-relaymap.cf . You can create this file manually, or use: setup.sh relay add-domain [ ] An example configuration file looks like this: @domain1.com [relay1.org]:587 @domain2.com [relay2.org]:2525 Combined with the previous configuration in docker-data/dms/config/postfix-sasl-password.cf , this will cause Postfix to deliver mail sent from domain1.com via relay1.org:587 , authenticating as relay_user_1 , and mail sent from domain2.com via relay2.org:2525 authenticating as relay_user_2 . Note You still have to define RELAY_HOST to activate the feature Excluding Sender Domains If you want mail sent from some domains to be delivered directly, you can exclude them from being delivered via the default relay by adding them to docker-data/dms/config/postfix-relaymap.cf with no destination. You can also do this via: setup.sh relay exclude-domain Extending the configuration file from above: @domain1.com [relay1.org]:587 @domain2.com [relay2.org]:2525 @domain3.com This will cause email sent from domain3.com to be delivered directly.","title":"Relay Hosts"},{"location":"config/advanced/mail-forwarding/relay-hosts/#introduction","text":"Rather than having Postfix deliver mail directly, you can configure Postfix to send mail via another mail relay (smarthost). Examples include Mailgun , Sendgrid and AWS SES . Depending on the domain of the sender, you may want to send via a different relay, or authenticate in a different way.","title":"Introduction"},{"location":"config/advanced/mail-forwarding/relay-hosts/#basic-configuration","text":"Basic configuration is done via environment variables: RELAY_HOST : default host to relay mail through, empty (aka '', or no ENV set) will disable this feature RELAY_PORT : port on default relay, defaults to port 25 RELAY_USER : username for the default relay RELAY_PASSWORD : password for the default user Setting these environment variables will cause mail for all sender domains to be routed via the specified host, authenticating with the user/password combination. Warning For users of the previous AWS_SES_* variables: please update your configuration to use these new variables, no other configuration is required.","title":"Basic Configuration"},{"location":"config/advanced/mail-forwarding/relay-hosts/#advanced-configuration","text":"","title":"Advanced Configuration"},{"location":"config/advanced/mail-forwarding/relay-hosts/#sender-dependent-authentication","text":"Sender dependent authentication is done in docker-data/dms/config/postfix-sasl-password.cf . You can create this file manually, or use: setup.sh relay add-auth [ ] An example configuration file looks like this: @domain1.com relay_user_1:password_1 @domain2.com relay_user_2:password_2 If there is no other configuration, this will cause Postfix to deliver email through the relay specified in RELAY_HOST env variable, authenticating as relay_user_1 when sent from domain1.com and authenticating as relay_user_2 when sending from domain2.com . Note To activate the configuration you must either restart the container, or you can also trigger an update by modifying a mail account.","title":"Sender-dependent Authentication"},{"location":"config/advanced/mail-forwarding/relay-hosts/#sender-dependent-relay-host","text":"Sender dependent relay hosts are configured in docker-data/dms/config/postfix-relaymap.cf . You can create this file manually, or use: setup.sh relay add-domain [ ] An example configuration file looks like this: @domain1.com [relay1.org]:587 @domain2.com [relay2.org]:2525 Combined with the previous configuration in docker-data/dms/config/postfix-sasl-password.cf , this will cause Postfix to deliver mail sent from domain1.com via relay1.org:587 , authenticating as relay_user_1 , and mail sent from domain2.com via relay2.org:2525 authenticating as relay_user_2 . Note You still have to define RELAY_HOST to activate the feature","title":"Sender-dependent Relay Host"},{"location":"config/advanced/mail-forwarding/relay-hosts/#excluding-sender-domains","text":"If you want mail sent from some domains to be delivered directly, you can exclude them from being delivered via the default relay by adding them to docker-data/dms/config/postfix-relaymap.cf with no destination. You can also do this via: setup.sh relay exclude-domain Extending the configuration file from above: @domain1.com [relay1.org]:587 @domain2.com [relay2.org]:2525 @domain3.com This will cause email sent from domain3.com to be delivered directly.","title":"Excluding Sender Domains"},{"location":"config/advanced/maintenance/update-and-cleanup/","text":"Automatic Update Docker images are handy but it can become a hassle to keep them updated. Also when a repository is automated you want to get these images when they get out. One could setup a complex action/hook-based workflow using probes, but there is a nice, easy to use docker image that solves this issue and could prove useful: watchtower . A docker-compose example: services : watchtower : restart : always image : containrrr/watchtower:latest volumes : - /var/run/docker.sock:/var/run/docker.sock For more details, see the manual Automatic Cleanup When you are pulling new images in automatically, it would be nice to have them cleaned up as well. There is also a docker image for this: spotify/docker-gc . A docker-compose example: services : docker-gc : restart : always image : spotify/docker-gc:latest volumes : - /var/run/docker.sock:/var/run/docker.sock For more details, see the manual Or you can just use the --cleanup option provided by containrrr/watchtower .","title":"Update and Cleanup"},{"location":"config/advanced/maintenance/update-and-cleanup/#automatic-update","text":"Docker images are handy but it can become a hassle to keep them updated. Also when a repository is automated you want to get these images when they get out. One could setup a complex action/hook-based workflow using probes, but there is a nice, easy to use docker image that solves this issue and could prove useful: watchtower . A docker-compose example: services : watchtower : restart : always image : containrrr/watchtower:latest volumes : - /var/run/docker.sock:/var/run/docker.sock For more details, see the manual","title":"Automatic Update"},{"location":"config/advanced/maintenance/update-and-cleanup/#automatic-cleanup","text":"When you are pulling new images in automatically, it would be nice to have them cleaned up as well. There is also a docker image for this: spotify/docker-gc . A docker-compose example: services : docker-gc : restart : always image : spotify/docker-gc:latest volumes : - /var/run/docker.sock:/var/run/docker.sock For more details, see the manual Or you can just use the --cleanup option provided by containrrr/watchtower .","title":"Automatic Cleanup"},{"location":"config/advanced/override-defaults/dovecot/","text":"Add Configuration The Dovecot default configuration can easily be extended providing a docker-data/dms/config/dovecot.cf file. Dovecot documentation remains the best place to find configuration options. Your docker-mailserver folder should look like this example: \u251c\u2500\u2500 docker-data/dms/config \u2502 \u251c\u2500\u2500 dovecot.cf \u2502 \u251c\u2500\u2500 postfix-accounts.cf \u2502 \u2514\u2500\u2500 postfix-virtual.cf \u251c\u2500\u2500 docker-compose.yml \u2514\u2500\u2500 README.md One common option to change is the maximum number of connections per user: mail_max_userip_connections = 100 Another important option is the default_process_limit (defaults to 100 ). If high-security mode is enabled you'll need to make sure this count is higher than the maximum number of users that can be logged in simultaneously. This limit is quickly reached if users connect to the docker-mailserver with multiple end devices. Override Configuration For major configuration changes it\u2019s best to override the dovecot configuration files. For each configuration file you want to override, add a list entry under the volumes key. services : mailserver : volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/config/dovecot/10-master.conf:/etc/dovecot/conf.d/10-master.conf You will first need to obtain the configuration from the running container ( where mailserver is the container name ): mkdir -p ./docker-data/dms/config/dovecot docker cp mailserver:/etc/dovecot/conf.d/10-master.conf ./docker-data/dms/config/dovecot/10-master.conf Debugging To debug your dovecot configuration you can use: This command: ./setup.sh debug login doveconf | grep Or: docker exec -it mailserver doveconf | grep Note setup.sh is included in the docker-mailserver repository. Make sure to use the one matching your image version release. The file docker-data/dms/config/dovecot.cf is copied internally to /etc/dovecot/local.conf . To verify the file content, run: docker exec -it mailserver cat /etc/dovecot/local.conf","title":"Dovecot"},{"location":"config/advanced/override-defaults/dovecot/#add-configuration","text":"The Dovecot default configuration can easily be extended providing a docker-data/dms/config/dovecot.cf file. Dovecot documentation remains the best place to find configuration options. Your docker-mailserver folder should look like this example: \u251c\u2500\u2500 docker-data/dms/config \u2502 \u251c\u2500\u2500 dovecot.cf \u2502 \u251c\u2500\u2500 postfix-accounts.cf \u2502 \u2514\u2500\u2500 postfix-virtual.cf \u251c\u2500\u2500 docker-compose.yml \u2514\u2500\u2500 README.md One common option to change is the maximum number of connections per user: mail_max_userip_connections = 100 Another important option is the default_process_limit (defaults to 100 ). If high-security mode is enabled you'll need to make sure this count is higher than the maximum number of users that can be logged in simultaneously. This limit is quickly reached if users connect to the docker-mailserver with multiple end devices.","title":"Add Configuration"},{"location":"config/advanced/override-defaults/dovecot/#override-configuration","text":"For major configuration changes it\u2019s best to override the dovecot configuration files. For each configuration file you want to override, add a list entry under the volumes key. services : mailserver : volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/config/dovecot/10-master.conf:/etc/dovecot/conf.d/10-master.conf You will first need to obtain the configuration from the running container ( where mailserver is the container name ): mkdir -p ./docker-data/dms/config/dovecot docker cp mailserver:/etc/dovecot/conf.d/10-master.conf ./docker-data/dms/config/dovecot/10-master.conf","title":"Override Configuration"},{"location":"config/advanced/override-defaults/dovecot/#debugging","text":"To debug your dovecot configuration you can use: This command: ./setup.sh debug login doveconf | grep Or: docker exec -it mailserver doveconf | grep Note setup.sh is included in the docker-mailserver repository. Make sure to use the one matching your image version release. The file docker-data/dms/config/dovecot.cf is copied internally to /etc/dovecot/local.conf . To verify the file content, run: docker exec -it mailserver cat /etc/dovecot/local.conf","title":"Debugging"},{"location":"config/advanced/override-defaults/postfix/","text":"Our default Postfix configuration can easily be extended to add parameters or modify existing ones by providing a docker-data/dms/config/postfix-main.cf . This file uses the same format as Postfix main.cf does ( See official docs for all parameters and syntax rules). Example One can easily increase the backwards-compatibility level and set new Postscreen options: # increase the compatibility level from 2 (default) to 3 compatibility_level = 3 # set a threshold value for Spam detection postscreen_dnsbl_threshold = 4 How are your changes applied? The custom configuration you supply is appended to the default configuration located at /etc/postfix/main.cf , and then postconf -nf is run to remove earlier duplicate entries that have since been replaced. This happens early during container startup before Postfix is started. Similarly, it is possible to add a custom docker-data/dms/config/postfix-master.cf file that will override the standard master.cf . Note : Each line in this file will be passed to postconf -P , i.e. the file is not appended as a whole to /etc/postfix/master.cf like docker-data/dms/config/postfix-main.cf ! The expected format is // , for example: # adjust the submission \"reject_unlisted_recipient\" option submission/inet/smtpd_reject_unlisted_recipient = no Attention There should be no space between the parameter and the value. Run postconf -Mf in the container without arguments to see the active master options.","title":"Postfix"},{"location":"config/advanced/override-defaults/user-patches/","text":"If you'd like to change, patch or alter files or behavior of docker-mailserver , you can use a script. In case you cloned this repository, you can copy the file user-patches.sh.dist ( under config/ ) with cp config/user-patches.sh.dist docker-data/dms/config/user-patches.sh in order to create the user-patches.sh script. If you are managing your directory structure yourself, create a docker-data/dms/config/ directory and add the user-patches.sh file yourself. # 1. Either create the docker-data/dms/config/ directory yourself # or let docker-mailserver create it on initial startup /tmp $ mkdir -p docker-data/dms/config/ && cd docker-data/dms/config/ # 2. Create the user-patches.sh file and edit it /tmp/docker-data/dms/config $ touch user-patches.sh /tmp/docker-data/dms/config $ nano user-patches.sh The contents could look like this: #!/bin/bash cat >/etc/amavis/conf.d/50-user << \"END\" use strict ; $undecipherable_subject_tag = undef ; $admin_maps_by_ccat { +CC_UNCHECKED } = undef ; #------------ Do not modify anything below this line ------------- 1 ; # ensure a defined return END 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. Note Many \"patches\" can already be done with the Docker Compose-/Stack-file. Adding hostnames to /etc/hosts is done with the extra_hosts : section, sysctl commands can be managed with the sysctls : section, etc.","title":"Modifications via Script"},{"location":"config/best-practices/autodiscover/","text":"Email auto-discovery means a client email is able to automagically find out about what ports and security options to use, based on the mail-server URI. It can help simplify the tedious / confusing task of adding own's email account for non-tech savvy users. Email clients will search for auto-discoverable settings and prefill almost everything when a user enters its email address There exists autodiscover-email-settings on which provides IMAP/POP/SMTP/LDAP autodiscover capabilities on Microsoft Outlook/Apple Mail, autoconfig capabilities for Thunderbird or kmail and configuration profiles for iOS/Apple Mail.","title":"Auto-discovery"},{"location":"config/best-practices/dkim/","text":"DKIM is a security measure targeting email spoofing. It is greatly recommended one activates it. Note See the Wikipedia page for more details on DKIM. Enabling DKIM Signature To enable DKIM signature, you must have created at least one email account . Once its done, just run the following command to generate the signature: ./setup.sh config dkim After generating DKIM keys, you should restart docker-mailserver . DNS edits may take a few minutes to hours to propagate. The script should ideally be run with a volume for config attached (eg: ./docker-data/dms/config/:/tmp/docker-mailserver/ ), otherwise by default it will mount ./config/:/tmp/docker-mailserver/ . The default keysize when generating the signature is 4096 bits for now. If you need to change it (e.g. your DNS provider limits the size), then provide the size as the first parameter of the command: ./setup.sh config dkim keysize For LDAP systems that do not have any directly created user account you can run the following command (since 8.0.0 ) to generate the signature by additionally providing the desired domain name (if you have multiple domains use the command multiple times or provide a comma-separated list of domains): ./setup.sh config dkim keysize domain [ , ] Now the keys are generated, you can configure your DNS server with DKIM signature, simply by adding a TXT record. If you have direct access to your DNS zone file, then it's only a matter of pasting the content of docker-data/dms/config/opendkim/keys/example.com/mail.txt in your example.com.hosts zone. $ dig mail._domainkey.example.com TXT --- ;; ANSWER SECTION mail._domainkey. 300 IN TXT \"v=DKIM1; k=rsa; p=AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN\" Configuration using a Web Interface Generate a new record of the type TXT . Paste mail._domainkey the Name txt field. In the Target or Value field fill in v=DKIM1; k=rsa; p=AZERTYUGHJKLMWX... . In TTL (time to live): Time span in seconds. How long the DNS server should cache the TXT record. Save. Note Sometimes the key in docker-data/dms/config/opendkim/keys/example.com/mail.txt can be on multiple lines. If so then you need to concatenate the values in the TXT record: $ dig mail._domainkey.example.com TXT --- ;; ANSWER SECTION mail._domainkey. 300 IN TXT \"v=DKIM1; k=rsa; \" \"p=AZERTYUIOPQSDF...\" \"asdfQWERTYUIOPQSDF...\" The target (or value) field must then have all the parts together: v=DKIM1; k=rsa; p=AZERTYUIOPQSDF...asdfQWERTYUIOPQSDF... Verify-Only If you want DKIM to only verify incoming emails, the following version of /etc/opendkim.conf may be useful (right now there is no easy mechanism for installing it other than forking the repo): # This is a simple config file verifying messages only #LogWhy yes Syslog yes SyslogSuccess yes Socket inet:12301@localhost PidFile /var/run/opendkim/opendkim.pid ReportAddress postmaster@example.com SendReports yes Mode v Switch Off DKIM Simply remove the DKIM key by recreating (not just relaunching) the docker-mailserver container. Debugging DKIM-verifer : A add-on for the mail client Thunderbird. You can debug your TXT records with the dig tool. $ dig TXT mail._domainkey.example.com --- ; <<>> DiG 9.10.3-P4-Debian <<>> TXT mail._domainkey.example.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39669 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 512 ;; QUESTION SECTION: ;mail._domainkey.example.com. IN TXT ;; ANSWER SECTION: mail._domainkey.example.com. 3600 IN TXT \"v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxBSjG6RnWAdU3oOlqsdf2WC0FOUmU8uHVrzxPLW2R3yRBPGLrGO1++yy3tv6kMieWZwEBHVOdefM6uQOQsZ4brahu9lhG8sFLPX4MaKYN/NR6RK4gdjrZu+MYSdfk3THgSbNwIDAQAB\" ;; Query time: 50 msec ;; SERVER: 127.0.1.1#53(127.0.1.1) ;; WHEN: Wed Sep 07 18:22:57 CEST 2016 ;; MSG SIZE rcvd: 310 Key sizes >=4096-bit Keys of 4096 bits could de denied by some mail-servers. According to https://tools.ietf.org/html/rfc6376 keys are preferably between 512 and 2048 bits. See issue #1854 .","title":"DKIM"},{"location":"config/best-practices/dkim/#enabling-dkim-signature","text":"To enable DKIM signature, you must have created at least one email account . Once its done, just run the following command to generate the signature: ./setup.sh config dkim After generating DKIM keys, you should restart docker-mailserver . DNS edits may take a few minutes to hours to propagate. The script should ideally be run with a volume for config attached (eg: ./docker-data/dms/config/:/tmp/docker-mailserver/ ), otherwise by default it will mount ./config/:/tmp/docker-mailserver/ . The default keysize when generating the signature is 4096 bits for now. If you need to change it (e.g. your DNS provider limits the size), then provide the size as the first parameter of the command: ./setup.sh config dkim keysize For LDAP systems that do not have any directly created user account you can run the following command (since 8.0.0 ) to generate the signature by additionally providing the desired domain name (if you have multiple domains use the command multiple times or provide a comma-separated list of domains): ./setup.sh config dkim keysize domain [ , ] Now the keys are generated, you can configure your DNS server with DKIM signature, simply by adding a TXT record. If you have direct access to your DNS zone file, then it's only a matter of pasting the content of docker-data/dms/config/opendkim/keys/example.com/mail.txt in your example.com.hosts zone. $ dig mail._domainkey.example.com TXT --- ;; ANSWER SECTION mail._domainkey. 300 IN TXT \"v=DKIM1; k=rsa; p=AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN\"","title":"Enabling DKIM Signature"},{"location":"config/best-practices/dkim/#configuration-using-a-web-interface","text":"Generate a new record of the type TXT . Paste mail._domainkey the Name txt field. In the Target or Value field fill in v=DKIM1; k=rsa; p=AZERTYUGHJKLMWX... . In TTL (time to live): Time span in seconds. How long the DNS server should cache the TXT record. Save. Note Sometimes the key in docker-data/dms/config/opendkim/keys/example.com/mail.txt can be on multiple lines. If so then you need to concatenate the values in the TXT record: $ dig mail._domainkey.example.com TXT --- ;; ANSWER SECTION mail._domainkey. 300 IN TXT \"v=DKIM1; k=rsa; \" \"p=AZERTYUIOPQSDF...\" \"asdfQWERTYUIOPQSDF...\" The target (or value) field must then have all the parts together: v=DKIM1; k=rsa; p=AZERTYUIOPQSDF...asdfQWERTYUIOPQSDF...","title":"Configuration using a Web Interface"},{"location":"config/best-practices/dkim/#verify-only","text":"If you want DKIM to only verify incoming emails, the following version of /etc/opendkim.conf may be useful (right now there is no easy mechanism for installing it other than forking the repo): # This is a simple config file verifying messages only #LogWhy yes Syslog yes SyslogSuccess yes Socket inet:12301@localhost PidFile /var/run/opendkim/opendkim.pid ReportAddress postmaster@example.com SendReports yes Mode v","title":"Verify-Only"},{"location":"config/best-practices/dkim/#switch-off-dkim","text":"Simply remove the DKIM key by recreating (not just relaunching) the docker-mailserver container.","title":"Switch Off DKIM"},{"location":"config/best-practices/dkim/#debugging","text":"DKIM-verifer : A add-on for the mail client Thunderbird. You can debug your TXT records with the dig tool. $ dig TXT mail._domainkey.example.com --- ; <<>> DiG 9.10.3-P4-Debian <<>> TXT mail._domainkey.example.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39669 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 512 ;; QUESTION SECTION: ;mail._domainkey.example.com. IN TXT ;; ANSWER SECTION: mail._domainkey.example.com. 3600 IN TXT \"v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxBSjG6RnWAdU3oOlqsdf2WC0FOUmU8uHVrzxPLW2R3yRBPGLrGO1++yy3tv6kMieWZwEBHVOdefM6uQOQsZ4brahu9lhG8sFLPX4MaKYN/NR6RK4gdjrZu+MYSdfk3THgSbNwIDAQAB\" ;; Query time: 50 msec ;; SERVER: 127.0.1.1#53(127.0.1.1) ;; WHEN: Wed Sep 07 18:22:57 CEST 2016 ;; MSG SIZE rcvd: 310 Key sizes >=4096-bit Keys of 4096 bits could de denied by some mail-servers. According to https://tools.ietf.org/html/rfc6376 keys are preferably between 512 and 2048 bits. See issue #1854 .","title":"Debugging"},{"location":"config/best-practices/dmarc/","text":"More information at DMARC Guide . Enabling DMARC In docker-mailserver , DMARC is pre-configured out of the box. The only thing you need to do in order to enable it, is to add new TXT entry to your DNS. In contrast with DKIM , the DMARC DNS entry does not require any keys, but merely setting the configuration values . You can either handcraft the entry by yourself or use one of available generators (like this one ). Typically something like this should be good to start with ( don't forget to replace @example.com to your actual domain ): _dmarc.example.com. IN TXT \"v=DMARC1; p=none; rua=mailto:dmarc.report@example.com; ruf=mailto:dmarc.report@example.com; sp=none; ri=86400\" Or a bit more strict policies ( mind p=quarantine and sp=quarantine ): _dmarc IN TXT \"v=DMARC1; p=quarantine; rua=mailto:dmarc.report@example.com; ruf=mailto:dmarc.report@example.com; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; sp=quarantine\" DMARC status is not being displayed instantly in Gmail for instance. If you want to check it directly after DNS entries, you can use some services around the Internet such as from Global Cyber Alliance or RedSift . In other cases, email clients will show \"DMARC: PASS\" in ~1 day or so. Reference: #1511","title":"DMARC"},{"location":"config/best-practices/dmarc/#enabling-dmarc","text":"In docker-mailserver , DMARC is pre-configured out of the box. The only thing you need to do in order to enable it, is to add new TXT entry to your DNS. In contrast with DKIM , the DMARC DNS entry does not require any keys, but merely setting the configuration values . You can either handcraft the entry by yourself or use one of available generators (like this one ). Typically something like this should be good to start with ( don't forget to replace @example.com to your actual domain ): _dmarc.example.com. IN TXT \"v=DMARC1; p=none; rua=mailto:dmarc.report@example.com; ruf=mailto:dmarc.report@example.com; sp=none; ri=86400\" Or a bit more strict policies ( mind p=quarantine and sp=quarantine ): _dmarc IN TXT \"v=DMARC1; p=quarantine; rua=mailto:dmarc.report@example.com; ruf=mailto:dmarc.report@example.com; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; sp=quarantine\" DMARC status is not being displayed instantly in Gmail for instance. If you want to check it directly after DNS entries, you can use some services around the Internet such as from Global Cyber Alliance or RedSift . In other cases, email clients will show \"DMARC: PASS\" in ~1 day or so. Reference: #1511","title":"Enabling DMARC"},{"location":"config/best-practices/spf/","text":"From Wikipedia : Quote Sender Policy Framework (SPF) is a simple email-validation system designed to detect email spoofing by providing a mechanism to allow receiving mail exchangers to check that incoming mail from a domain comes from a host authorized by that domain's administrators. The list of authorized sending hosts for a domain is published in the Domain Name System (DNS) records for that domain in the form of a specially formatted TXT record. Email spam and phishing often use forged \"from\" addresses, so publishing and checking SPF records can be considered anti-spam techniques. Note For a more technical review: https://github.com/internetstandards/toolbox-wiki/blob/master/SPF-how-to.md Add a SPF Record To add a SPF record in your DNS, insert the following line in your DNS zone: ; MX record must be declared for SPF to work example.com. IN MX 1 mail.example.com. ; SPF record example.com. IN TXT \"v=spf1 mx ~all\" This enables the Softfail mode for SPF. You could first add this SPF record with a very low TTL. SoftFail is a good setting for getting started and testing, as it lets all email through, with spams tagged as such in the mailbox. After verification, you might want to change your SPF record to v=spf1 mx -all so as to enforce the HardFail policy. See http://www.open-spf.org/SPF_Record_Syntax for more details about SPF policies. In any case, increment the SPF record's TTL to its final value. Backup MX, Secondary MX For whitelisting a IP Address from the SPF test, you can create a config file (see policyd-spf.conf ) and mount that file into /etc/postfix-policyd-spf-python/policyd-spf.conf . Example: Create and edit a policyd-spf.conf file at docker-data/dms/config/postfix-policyd-spf.conf : debugLevel = 1 #0(only errors)-4(complete data received) skip_addresses = 127.0.0.0/8,::ffff:127.0.0.0/104,::1 # Preferably use IP-Addresses for whitelist lookups: Whitelist = 192.168.0.0/31,192.168.1.0/30 # Domain_Whitelist = mx1.not-example.com,mx2.not-example.com Then add this line to docker-compose.yml : volumes : - ./docker-data/dms/config/postfix-policyd-spf.conf:/etc/postfix-policyd-spf-python/policyd-spf.conf","title":"SPF"},{"location":"config/best-practices/spf/#add-a-spf-record","text":"To add a SPF record in your DNS, insert the following line in your DNS zone: ; MX record must be declared for SPF to work example.com. IN MX 1 mail.example.com. ; SPF record example.com. IN TXT \"v=spf1 mx ~all\" This enables the Softfail mode for SPF. You could first add this SPF record with a very low TTL. SoftFail is a good setting for getting started and testing, as it lets all email through, with spams tagged as such in the mailbox. After verification, you might want to change your SPF record to v=spf1 mx -all so as to enforce the HardFail policy. See http://www.open-spf.org/SPF_Record_Syntax for more details about SPF policies. In any case, increment the SPF record's TTL to its final value.","title":"Add a SPF Record"},{"location":"config/best-practices/spf/#backup-mx-secondary-mx","text":"For whitelisting a IP Address from the SPF test, you can create a config file (see policyd-spf.conf ) and mount that file into /etc/postfix-policyd-spf-python/policyd-spf.conf . Example: Create and edit a policyd-spf.conf file at docker-data/dms/config/postfix-policyd-spf.conf : debugLevel = 1 #0(only errors)-4(complete data received) skip_addresses = 127.0.0.0/8,::ffff:127.0.0.0/104,::1 # Preferably use IP-Addresses for whitelist lookups: Whitelist = 192.168.0.0/31,192.168.1.0/30 # Domain_Whitelist = mx1.not-example.com,mx2.not-example.com Then add this line to docker-compose.yml : volumes : - ./docker-data/dms/config/postfix-policyd-spf.conf:/etc/postfix-policyd-spf-python/policyd-spf.conf","title":"Backup MX, Secondary MX"},{"location":"config/security/fail2ban/","text":"Fail2Ban is installed automatically and bans IP addresses for 3 hours after 3 failed attempts in 10 minutes by default. Configuration files If you want to change this, you can easily edit our github example file: config-examples/fail2ban-jail.cf . You can do the same with the values from fail2ban.conf , e.g dbpurgeage . In that case you need to edit: config-examples/fail2ban-fail2ban.cf . The configuration files need to be located at the root of the /tmp/docker-mailserver/ volume bind (usually ./docker-data/dms/config/:/tmp/docker-mailserver/ ). This following configuration files from /tmp/docker-mailserver/ will be copied during container startup. fail2ban-jail.cf -> /etc/fail2ban/jail.d/user-jail.local fail2ban-fail2ban.cf -> /etc/fail2ban/fail2ban.local Docker-compose config Example configuration volume bind: volumes : - ./docker-data/dms/config/:/tmp/docker-mailserver/ Attention docker-mailserver must be launched with the NET_ADMIN capability in order to be able to install the nftables rules that actually ban IP addresses. Thus either include --cap-add=NET_ADMIN in the docker run command, or the equivalent in docker-compose.yml : cap_add : - NET_ADMIN Running fail2ban in a rootless container RootlessKit is the fakeroot implementation for supporting rootless mode in Docker and Podman. By default RootlessKit uses the builtin port forwarding driver , which does not propagate source IP addresses. It is necessary for fail2ban to have access to the real source IP addresses in order to correctly identify clients. This is achieved by changing the port forwarding driver to slirp4netns , which is slower than builtin but does preserve the real source IPs. Docker with slirp4netns port driver For rootless mode in Docker, create ~/.config/systemd/user/docker.service.d/override.conf with the following content: [Service] Environment=\"DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=slirp4netns\" And then restart the daemon: $ systemctl --user daemon-reload $ systemctl --user restart docker Note This changes the port driver for all rootless containers managed by Docker. Per container configuration is not supported, if you need that consider Podman instead. Podman with slirp4netns port driver Rootless Podman requires adding the value slirp4netns:port_handler=slirp4netns to the --network CLI option, or network_mode setting in your docker-compose.yml . You must also add the ENV NETWORK_INTERFACE=tap0 , because Podman uses a hard-coded interface name for slirp4netns . Example services : mailserver : network_mode : \"slirp4netns:port_handler=slirp4netns\" environment : - ENABLE_FAIL2BAN=1 - NETWORK_INTERFACE=tap0 ... Note slirp4netns is not compatible with user-defined networks. Manage bans You can also manage and list the banned IPs with the setup.sh script. List bans ./setup.sh fail2ban Un-ban Here 192.168.1.15 is our banned IP. ./setup.sh fail2ban unban 192 .168.1.15","title":"Fail2Ban"},{"location":"config/security/fail2ban/#configuration-files","text":"If you want to change this, you can easily edit our github example file: config-examples/fail2ban-jail.cf . You can do the same with the values from fail2ban.conf , e.g dbpurgeage . In that case you need to edit: config-examples/fail2ban-fail2ban.cf . The configuration files need to be located at the root of the /tmp/docker-mailserver/ volume bind (usually ./docker-data/dms/config/:/tmp/docker-mailserver/ ). This following configuration files from /tmp/docker-mailserver/ will be copied during container startup. fail2ban-jail.cf -> /etc/fail2ban/jail.d/user-jail.local fail2ban-fail2ban.cf -> /etc/fail2ban/fail2ban.local","title":"Configuration files"},{"location":"config/security/fail2ban/#docker-compose-config","text":"Example configuration volume bind: volumes : - ./docker-data/dms/config/:/tmp/docker-mailserver/ Attention docker-mailserver must be launched with the NET_ADMIN capability in order to be able to install the nftables rules that actually ban IP addresses. Thus either include --cap-add=NET_ADMIN in the docker run command, or the equivalent in docker-compose.yml : cap_add : - NET_ADMIN","title":"Docker-compose config"},{"location":"config/security/fail2ban/#running-fail2ban-in-a-rootless-container","text":"RootlessKit is the fakeroot implementation for supporting rootless mode in Docker and Podman. By default RootlessKit uses the builtin port forwarding driver , which does not propagate source IP addresses. It is necessary for fail2ban to have access to the real source IP addresses in order to correctly identify clients. This is achieved by changing the port forwarding driver to slirp4netns , which is slower than builtin but does preserve the real source IPs.","title":"Running fail2ban in a rootless container"},{"location":"config/security/fail2ban/#docker-with-slirp4netns-port-driver","text":"For rootless mode in Docker, create ~/.config/systemd/user/docker.service.d/override.conf with the following content: [Service] Environment=\"DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=slirp4netns\" And then restart the daemon: $ systemctl --user daemon-reload $ systemctl --user restart docker Note This changes the port driver for all rootless containers managed by Docker. Per container configuration is not supported, if you need that consider Podman instead.","title":"Docker with slirp4netns port driver"},{"location":"config/security/fail2ban/#podman-with-slirp4netns-port-driver","text":"Rootless Podman requires adding the value slirp4netns:port_handler=slirp4netns to the --network CLI option, or network_mode setting in your docker-compose.yml . You must also add the ENV NETWORK_INTERFACE=tap0 , because Podman uses a hard-coded interface name for slirp4netns . Example services : mailserver : network_mode : \"slirp4netns:port_handler=slirp4netns\" environment : - ENABLE_FAIL2BAN=1 - NETWORK_INTERFACE=tap0 ... Note slirp4netns is not compatible with user-defined networks.","title":"Podman with slirp4netns port driver"},{"location":"config/security/fail2ban/#manage-bans","text":"You can also manage and list the banned IPs with the setup.sh script.","title":"Manage bans"},{"location":"config/security/fail2ban/#list-bans","text":"./setup.sh fail2ban","title":"List bans"},{"location":"config/security/fail2ban/#un-ban","text":"Here 192.168.1.15 is our banned IP. ./setup.sh fail2ban unban 192 .168.1.15","title":"Un-ban"},{"location":"config/security/mail_crypt/","text":"Info The Mail crypt plugin is used to secure email messages stored in a Dovecot system. Messages are encrypted before written to storage and decrypted after reading. Both operations are transparent to the user. In case of unauthorized access to the storage backend, the messages will, without access to the decryption keys, be unreadable to the offending party. There can be a single encryption key for the whole system or each user can have a key of their own. The used cryptographical methods are widely used standards and keys are stored in portable formats, when possible. Official Dovecot documentation: https://doc.dovecot.org/configuration_manual/mail_crypt_plugin/ Single Encryption Key / Global Method Create 10-custom.conf and populate it with the following: # Enables mail_crypt for all services (imap, pop3, etc) mail_plugins = $mail_plugins mail_crypt plugin { mail_crypt_global_private_key = File Names & Extensions For command 1 - 3, we append the .conf suffix to the module name to get the correct file name automatically. For commands 4 - 6, the file name is fixed (you don't even need to provide it). For command 7, you will need to provide the whole file name (including the suffix) yourself! You can also have comments (the line starts with # ) and blank lines in rspamd-modules.conf - they are properly handled and not evaluated. Adjusting Modules This Way These simple commands are meant to give users the ability to easily alter modules and their options. As a consequence, they are not powerful enough to enable multi-line adjustments. If you need to do something more complex, we advise to do that manually ! Advanced Configuration Abusix Integration This subsection gives information about the integration of Abusix , \"a set of blocklists that work as an additional email security layer for your existing mail environment\". The setup is straight-forward and well documented: Create an account Retrieve your API key Navigate to the \"Getting Started\" documentation for Rspamd and follow the steps described there Make sure to change to your private API key We recommend mounting the files directly into the container, as they are rather big and not manageable with the modules script . If mounted to the correct location, Rspamd will automatically pick them up. While Abusix can be integrated into Postfix, Postscreen and a multitude of other software, we recommend integrating Abusix only into a single piece of software running in your mail server - everything else would be excessive and wasting queries. Moreover, we recommend the integration into suitable filtering software and not Postfix itself, as software like Postscreen or Rspamd can properly evaluate the return codes and other configuration.","title":"Rspamd"},{"location":"config/security/rspamd/#about","text":"Rspamd is a \"fast, free and open-source spam filtering system\" . DMS integrates Rspamd like any other service. You will need to enable Rspamd (via ENABLE_RSPAMD=1 ) manually as it is disabled by default. We provide a very simple but easy to maintain setup of Rspamd. If you want to have a look at the default configuration files for Rspamd that DMS packs, navigate to target/rspamd/ inside the repository . Please consult the section \"The Default Configuration\" section down below for a written overview. If you want to adjust Rspamd's configuration, have a look at the \"Providing Custom Settings & Overriding Settings\" section down below. AMD64 vs ARM64 We are currently doing a best-effort installation of Rspamd for ARM64 (from the Debian backports repository for Debian 11). The current version difference is two minor versions (AMD64 is at version 3.4, ARM64 at 3.2 [13th Feb 2023]). Maintainers noticed only few differences, some of them with a big impact though. For those running Rspamd on ARM64, we recommend disabling the DKIM signing module if you don't use it.","title":"About"},{"location":"config/security/rspamd/#the-default-configuration","text":"","title":"The Default Configuration"},{"location":"config/security/rspamd/#mode-of-operation","text":"The proxy worker operates in self-scan mode . This simplifies the setup as we do not require a normal worker. You can easily change this though by overriding the configuration by DMS . DMS does not set a default password for the controller worker. You may want to do that yourself. In setup where you already have an authentication provider in front of the Rspamd webpage, you may add secure_ip = \"0.0.0.0/0\"; to worker-controller.inc to disable password authentication inside Rspamd completely.","title":"Mode of Operation"},{"location":"config/security/rspamd/#modules","text":"You can find a list of all Rspamd modules on their website .","title":"Modules"},{"location":"config/security/rspamd/#disabled-by-default","text":"DMS disables certain modules (clickhouse, elastic, greylist, neural, reputation, spamassassin, url_redirector, metric_exporter) by default. We believe these are not required in a standard setup, and they would otherwise needlessly use system resources.","title":"Disabled By Default"},{"location":"config/security/rspamd/#anti-virus-clamav","text":"You can choose to enable ClamAV, and Rspamd will then use it to check for viruses. Just set the environment variable ENABLE_CLAMAV=1 .","title":"Anti-Virus (ClamAV)"},{"location":"config/security/rspamd/#rbls-realtime-blacklists-dnsbls-dns-based-blacklists","text":"The RBL module is enabled by default. As a consequence, Rspamd will perform DNS lookups to a variety of blacklists. Whether an RBL or a DNSBL is queried depends on where the domain name was obtained: RBL servers are queried with IP addresses extracted from message headers, DNSBL server are queried with domains and IP addresses extracted from the message body [ source ]. Rspamd and DNS Block Lists When the RBL module is enabled, Rspamd will do a variety of DNS requests to (amongst other things) DNSBLs. There are a variety of issues involved when using DNSBLs. Rspamd will try to mitigate some of them by properly evaluating all return codes. This evaluation is a best effort though, so if the DNSBL operators change or add return codes, it may take a while for Rspamd to adjust as well. If you want to use DNSBLs, try to use your own DNS resolver and make sure it is set up correctly, i.e. it should be a non-public & recursive resolver. Otherwise, you might not be able ( see this Spamhaus post ) to make use of the block lists.","title":"RBLs (Realtime Blacklists) / DNSBLs (DNS-based Blacklists)"},{"location":"config/security/rspamd/#missing-in-the-current-implementation","text":"We currently lack easy integration for DKIM signing outgoing mails . We use OpenDKIM though which works just as well. If you want to use Rspamd for DKIM signing, you need to provide all settings yourself and probably also set the environment variable ENABLE_OPENDKIM=0 . Rspamd will still check for valid DKIM signatures for incoming mail by default.","title":"Missing in the Current Implementation"},{"location":"config/security/rspamd/#providing-custom-settings-overriding-settings","text":"","title":"Providing Custom Settings & Overriding Settings"},{"location":"config/security/rspamd/#manually","text":"DMS brings sane default settings for Rspamd. They are located at /etc/rspamd/local.d/ inside the container (or target/rspamd/local.d/ in the repository). If you want to change these settings and / or provide your own settings, you can place files at /etc/rspamd/override.d/ which will override Rspamd settings and DMS settings (re-)place files at /etc/rspamd/local.d/ to override DMS settings and merge them with Rspamd settings Clashing Overrides Note that when also using the rspamd-commands file , files in override.d may be overwritten in case you adjust them manually and with the help of the file.","title":"Manually"},{"location":"config/security/rspamd/#with-the-help-of-a-custom-file","text":"DMS provides the ability to do simple adjustments to Rspamd modules with the help of a single file. Just place a file called rspamd-modules.conf into the directory docker-data/dms/config/ (which translates to /tmp/docker-mailserver/ in the container). If this file is present, DMS will evaluate it. The structure is very simple. Each line in the file looks like this: COMMAND ARGUMENT1 ARGUMENT2 ARGUMENT3 where COMMAND can be: disable-module : disables the module with name ARGUMENT1 enable-module : explicitly enables the module with name ARGUMENT1 set-option-for-module : sets the value for option ARGUMENT2 to ARGUMENT3 inside module ARGUMENT1 set-option-for-controller : set the value of option ARGUMENT1 to ARGUMENT2 for the controller worker set-option-for-proxy : set the value of option ARGUMENT1 to ARGUMENT2 for the proxy worker set-common-option : set the option ARGUMENT1 that defines basic Rspamd behaviour to value ARGUMENT2 add-line : this will add the complete line after ARGUMENT1 (with all characters) to the file /etc/rspamd/override.d/ File Names & Extensions For command 1 - 3, we append the .conf suffix to the module name to get the correct file name automatically. For commands 4 - 6, the file name is fixed (you don't even need to provide it). For command 7, you will need to provide the whole file name (including the suffix) yourself! You can also have comments (the line starts with # ) and blank lines in rspamd-modules.conf - they are properly handled and not evaluated. Adjusting Modules This Way These simple commands are meant to give users the ability to easily alter modules and their options. As a consequence, they are not powerful enough to enable multi-line adjustments. If you need to do something more complex, we advise to do that manually !","title":"With the Help of a Custom File"},{"location":"config/security/rspamd/#advanced-configuration","text":"","title":"Advanced Configuration"},{"location":"config/security/rspamd/#abusix-integration","text":"This subsection gives information about the integration of Abusix , \"a set of blocklists that work as an additional email security layer for your existing mail environment\". The setup is straight-forward and well documented: Create an account Retrieve your API key Navigate to the \"Getting Started\" documentation for Rspamd and follow the steps described there Make sure to change to your private API key We recommend mounting the files directly into the container, as they are rather big and not manageable with the modules script . If mounted to the correct location, Rspamd will automatically pick them up. While Abusix can be integrated into Postfix, Postscreen and a multitude of other software, we recommend integrating Abusix only into a single piece of software running in your mail server - everything else would be excessive and wasting queries. Moreover, we recommend the integration into suitable filtering software and not Postfix itself, as software like Postscreen or Rspamd can properly evaluate the return codes and other configuration.","title":"Abusix Integration"},{"location":"config/security/ssl/","text":"There are multiple options to enable SSL (via SSL_TYPE ): Using letsencrypt (recommended) Using Caddy Using Traefik Using self-signed certificates Using your own certificates After installation, you can test your setup with: checktls.com testssl.sh Exposure of DNS labels through Certificate Transparency All public Certificate Authorities (CAs) are required to log certificates they issue publicly via Certificate Transparency . This helps to better establish trust. When using a public CA for certificates used in private networks, be aware that the associated DNS labels in the certificate are logged publicly and easily searchable . These logs are append only , you cannot redact this information. You could use a wildcard certificate . This avoids accidentally leaking information to the internet, but keep in mind the potential security risks of wildcard certs. The FQDN An FQDN ( Fully Qualified Domain Name ) such as mail.example.com is required for docker-mailserver to function correctly, especially for looking up the correct SSL certificate to use. Internally, hostname -f will be used to retrieve the FQDN as configured in the below examples. Wildcard certificates (eg: *.example.com ) are supported for SSL_TYPE=letsencrypt . Your configured FQDN below may be mail.example.com , and your wildcard certificate provisioned to /etc/letsencrypt/live/example.com which will be checked as a fallback FQDN by docker-mailserver . Docker CLI options --hostname and optionally --domainname docker run --hostname mail --domainname example.com # `--domainname` is not required: docker run --hostname mail.example.com docker-compose.yml config services : mailserver : hostname : mail domainname : example.com # `domainname` is not required: services : mailserver : hostname : mail.example.com Bare domains (eg: example.com ) should only use the hostname option docker run --hostname example.com services : mailserver : hostname : example.com Provisioning methods Let's Encrypt (Recommended) To enable Let's Encrypt for docker-mailserver , you have to: Get your certificate using the Let's Encrypt client Certbot . For your docker-mailserver container: Add the environment variable SSL_TYPE=letsencrypt . Mount your local letsencrypt folder as a volume to /etc/letsencrypt . You don't have to do anything else. Enjoy! Note /etc/letsencrypt/live stores provisioned certificates in individual folders named by their FQDN. Make sure that the entire folder is mounted to docker-mailserver as there are typically symlinks from /etc/letsencrypt/live/mail.example.com to /etc/letsencrypt/archive . Example Add these additions to the mailserver service in your docker-compose.yml : services : mailserver : # For the FQDN 'mail.example.com': hostname : mail domainname : example.com environment : - SSL_TYPE=letsencrypt volumes : - /etc/letsencrypt:/etc/letsencrypt Example using Docker for Let's Encrypt Certbot provisions certificates to /etc/letsencrypt . Add a volume to store these, so that they can later be accessed by docker-mailserver container. You may also want to persist Certbot logs , just in case you need to troubleshoot. Getting a certificate is this simple! ( Referencing: Certbot docker instructions and certonly --standalone mode ): # Change `mail.example.com` below to your own FQDN. # Requires access to port 80 from the internet, adjust your firewall if needed. docker run --rm -it \\ -v \" ${ PWD } /docker-data/certbot/certs/:/etc/letsencrypt/\" \\ -v \" ${ PWD } /docker-data/certbot/logs/:/var/log/letsencrypt/\" \\ -p 80 :80 \\ certbot/certbot certonly --standalone -d mail.example.com Add a volume for docker-mailserver that maps the local certbot/certs/ folder to the container path /etc/letsencrypt/ . Example Add these additions to the mailserver service in your docker-compose.yml : services : mailserver : # For the FQDN 'mail.example.com': hostname : mail domainname : example.com environment : - SSL_TYPE=letsencrypt volumes : - ./docker-data/certbot/certs/:/etc/letsencrypt The certificate setup is complete, but remember it will expire . Consider automating renewals. Renewing Certificates When running the above certonly --standalone snippet again, the existing certificate is renewed if it would expire within 30 days. Alternatively, Certbot can look at all the certificates it manages, and only renew those nearing their expiry via the renew command : # This will need access to port 443 from the internet, adjust your firewall if needed. docker run --rm -it \\ -v \" ${ PWD } /docker-data/certbot/certs/:/etc/letsencrypt/\" \\ -v \" ${ PWD } /docker-data/certbot/logs/:/var/log/letsencrypt/\" \\ -p 80 :80 \\ -p 443 :443 \\ certbot/certbot renew This process can also be automated via cron or systemd timers . Using a different ACME CA Certbot does support alternative certificate providers via the --server option. In most cases you'll want to use the default Let's Encrypt . Example using certbot-dns-cloudflare with Docker If you are unable get a certificate via the HTTP-01 (port 80) or TLS-ALPN-01 (port 443) challenge types , the DNS-01 challenge can be useful ( this challenge can additionally issue wildcard certificates ). This guide shows how to use the DNS-01 challenge with Cloudflare as your DNS provider. Obtain a Cloudflare API token: Login into your Cloudflare dashboard. Navigate to the API Tokens page . Click \"Create Token\", and choose the Edit zone DNS template ( Certbot requires the ZONE:DNS:Edit permission ). Only include the necessary Zone resource configuration Be sure to configure \"Zone Resources\" section on this page to Include -> Specific zone -> . This restricts the API token to only this zone (domain) which is an important security measure. Store the API token you received in a file cloudflare.ini with content: dns_cloudflare_api_token = YOUR_CLOUDFLARE_TOKEN_HERE As this is sensitive data, you should restrict access to it with chmod 600 and chown 0:0 . Store the file in a folder if you like, such as docker-data/certbot/secrets/ . Your docker-compose.yml should include the following: services : mailserver : environments : # Set SSL certificate type. - SSL_TYPE=letsencrypt volumes : # Mount the cert folder generated by Certbot into mail-server: - ./docker-data/certbot/certs/:/etc/letsencrypt/:ro certbot-cloudflare : image : certbot/dns-cloudflare:latest command : certonly --dns-cloudflare --dns-cloudflare-credentials /run/secrets/cloudflare-api-token -d mail.example.com volumes : - ./docker-data/certbot/certs/:/etc/letsencrypt/ - ./docker-data/certbot/logs/:/var/log/letsencrypt/ secrets : - cloudflare-api-token # Docs: https://docs.docker.com/engine/swarm/secrets/#use-secrets-in-compose # WARNING: In compose configs without swarm, the long syntax options have no effect, # Ensure that you properly `chmod 600` and `chown 0:0` the file on disk. Effectively treated as a bind mount. secrets : cloudflare-api-token : file : ./docker-data/certbot/secrets/cloudflare.ini Alternative using the docker run command ( secrets feature is not available): docker run \\ --volume \" ${ PWD } /docker-data/certbot/certs/:/etc/letsencrypt/\" \\ --volume \" ${ PWD } /docker-data/certbot/logs/:/var/log/letsencrypt/\" \\ --volume \" ${ PWD } /docker-data/certbot/secrets/:/tmp/secrets/certbot/\" certbot/dns-cloudflare \\ certonly --dns-cloudflare --dns-cloudflare-credentials /tmp/secrets/certbot/cloudflare.ini -d mail.example.com Run the service to provision a certificate: docker-compose run certbot-cloudflare You should see the following log output: Saving debug log to /var/log/letsencrypt/letsencrypt. log | Requesting a certificate for mail.example.com Waiting 10 seconds for DNS changes to propagate Successfully received certificate. Certificate is saved at: /etc/letsencrypt/live/mail.example.com/fullchain.pem Key is saved at: /etc/letsencrypt/live/mail.example.com/privkey.pem This certificate expires on YYYY-MM-DD. These files will be updated when the certificate renews. NEXT STEPS: - The certificate will need to be renewed before it expires. Certbot can automatically renew the certificate in background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal structions. After completing the steps above, your certificate should be ready to use. Renewing a certificate (Optional) We've only demonstrated how to provision a certificate, but it will expire in 90 days and need to be renewed before then. In the following example, add a new service ( certbot-cloudflare-renew ) into docker-compose.yml that will handle certificate renewals: services : certbot-cloudflare-renew : image : certbot/dns-cloudflare:latest command : renew --dns-cloudflare --dns-cloudflare-credentials /run/secrets/cloudflare-api-token volumes : - ./docker-data/certbot/certs/:/etc/letsencrtypt/ - ./docker-data/certbot/logs/:/var/log/letsencrypt/ secrets : - cloudflare-api-token You can manually run this service to renew the cert within 90 days: docker-compose run certbot-cloudflare-renew You should see the following output (The following log was generated with --dry-run options) Saving debug log to /var/log/letsencrypt/letsencrypt.log - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Processing /etc/letsencrypt/renewal/mail.example.com.conf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Account registered. Simulating renewal of an existing certificate for mail.example.com Waiting 10 seconds for DNS changes to propagate - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/mail.example.com/fullchain.pem (success) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - It is recommended to automate this renewal via a task scheduler like a systemd timer or in crontab ( crontab example: Checks every day if the certificate should be renewed) 0 0 * * * docker-compose -f PATH_TO_YOUR_DOCKER_COMPOSE_YML up certbot-cloudflare-renew Example using nginx-proxy and acme-companion with Docker If you are running a web server already, port 80 will be in use which Certbot requires. You could use the Certbot --webroot feature, but it is more common to leverage a reverse proxy that manages the provisioning and renewal of certificates for your services automatically. In the following example, we show how docker-mailserver can be run alongside the docker containers nginx-proxy and acme-companion ( Referencing: acme-companion documentation ): Start the reverse proxy ( nginx-proxy ): docker run --detach \\ --name nginx-proxy \\ --restart always \\ --publish 80 :80 \\ --publish 443 :443 \\ --volume \" ${ PWD } /docker-data/nginx-proxy/html/:/usr/share/nginx/html/\" \\ --volume \" ${ PWD } /docker-data/nginx-proxy/vhost.d/:/etc/nginx/vhost.d/\" \\ --volume \" ${ PWD } /docker-data/acme-companion/certs/:/etc/nginx/certs/:ro\" \\ --volume '/var/run/docker.sock:/tmp/docker.sock:ro' \\ nginxproxy/nginx-proxy Then start the certificate provisioner ( acme-companion ), which will provide certificates to nginx-proxy : # Inherit `nginx-proxy` volumes via `--volumes-from`, but make `certs/` writeable: docker run --detach \\ --name nginx-proxy-acme \\ --restart always \\ --volumes-from nginx-proxy \\ --volume \" ${ PWD } /docker-data/acme-companion/certs/:/etc/nginx/certs/:rw\" \\ --volume \" ${ PWD } /docker-data/acme-companion/acme-state/:/etc/acme.sh/\" \\ --volume '/var/run/docker.sock:/var/run/docker.sock:ro' \\ --env 'DEFAULT_EMAIL=admin@example.com' \\ nginxproxy/acme-companion Start the rest of your web server containers as usual. Start a dummy container to provision certificates for your FQDN (eg: mail.example.com ). acme-companion will detect the container and generate a Let's Encrypt certificate for your domain, which can be used by docker-mailserver : docker run --detach \\ --name webmail \\ --env 'VIRTUAL_HOST=mail.example.com' \\ --env 'LETSENCRYPT_HOST=mail.example.com' \\ --env 'LETSENCRYPT_EMAIL=admin@example.com' \\ nginx You may want to add --env LETSENCRYPT_TEST=true to the above while testing, to avoid the Let's Encrypt certificate generation rate limits. Make sure your mount path to the letsencrypt certificates directory is correct. Edit your docker-compose.yml for the mailserver service to have volumes added like below: volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - ./docker-data/acme-companion/certs/:/etc/letsencrypt/live/:ro Then from the docker-compose.yml project directory, run: docker-compose up -d mailserver . Example using nginx-proxy and acme-companion with docker-compose The following example is the basic setup you need for using nginx-proxy and acme-companion with docker-mailserver ( Referencing: acme-companion documentation ): Example: docker-compose.yml You should have an existing docker-compose.yml with a mailserver service. Below are the modifications to add for integrating with nginx-proxy and acme-companion services: version : '3.8' services : # Add the following `environment` and `volumes` to your existing `mailserver` service: mailserver : environment : # SSL_TYPE: Uses the `letsencrypt` method to find mounted certificates. # VIRTUAL_HOST: The FQDN that `nginx-proxy` will configure itself to handle for HTTP[S] connections. # LETSENCRYPT_HOST: The FQDN for a certificate that `acme-companion` will provision and renew. - SSL_TYPE=letsencrypt - VIRTUAL_HOST=mail.example.com - LETSENCRYPT_HOST=mail.example.com volumes : - ./docker-data/acme-companion/certs/:/etc/letsencrypt/live/:ro # If you don't yet have your own `nginx-proxy` and `acme-companion` setup, # here is an example you can use: reverse-proxy : image : nginxproxy/nginx-proxy container_name : nginx-proxy restart : always ports : # Port 80: Required for HTTP-01 challenges to `acme-companion`. # Port 443: Only required for containers that need access over HTTPS. TLS-ALPN-01 challenge not supported. - \"80:80\" - \"443:443\" volumes : # `certs/`: Managed by the `acme-companion` container (_read-only_). # `docker.sock`: Required to interact with containers via the Docker API. # `dhparam`: A named data volume to prevent `nginx-proxy` creating an anonymous volume each time. - ./docker-data/nginx-proxy/html/:/usr/share/nginx/html/ - ./docker-data/nginx-proxy/vhost.d/:/etc/nginx/vhost.d/ - ./docker-data/acme-companion/certs/:/etc/nginx/certs/:ro - /var/run/docker.sock:/tmp/docker.sock:ro - dhparam:/etc/nginx/dhparam acme-companion : image : nginxproxy/acme-companion container_name : nginx-proxy-acme restart : always environment : # Only docker-compose v2 supports: `volumes_from: [nginx-proxy]`, # reference the _reverse-proxy_ `container_name` here: - NGINX_PROXY_CONTAINER=nginx-proxy volumes : # `html/`: Write ACME HTTP-01 challenge files that `nginx-proxy` will serve. # `vhost.d/`: To enable web access via `nginx-proxy` to HTTP-01 challenge files. # `certs/`: To store certificates and private keys. # `acme-state/`: To persist config and state for the ACME provisioner (`acme.sh`). # `docker.sock`: Required to interact with containers via the Docker API. - ./docker-data/nginx-proxy/html/:/usr/share/nginx/html/ - ./docker-data/nginx-proxy/vhost.d/:/etc/nginx/vhost.d/ - ./docker-data/acme-companion/certs/:/etc/nginx/certs/:rw - ./docker-data/acme-companion/acme-state/:/etc/acme.sh/ - /var/run/docker.sock:/var/run/docker.sock:ro # Once `nginx-proxy` fixes their Dockerfile, this named data volume can be removed from docs. # Users can opt for a local bind mount volume like all others if they prefer, but this volume # is only intended to be temporary. volumes : dhparam : Optional ENV vars worth knowing about Per container ENV that acme-companion will detect to override default provisioning settings: LETSENCRYPT_TEST=true : Recommended during initial setup . Otherwise the default production endpoint has a rate limit of 5 duplicate certificates per week . Overrides ACME_CA_URI to use the Let's Encrypt staging endpoint. LETSENCRYPT_EMAIL : For when you don't use DEFAULT_EMAIL on acme-companion , or want to assign a different email contact for this container. LETSENCRYPT_KEYSIZE : Allows you to configure the type (RSA or ECDSA) and size of the private key for your certificate. Default is RSA 4096. LETSENCRYPT_RESTART_CONTAINER=true : When the certificate is renewed, the entire container will be restarted to ensure the new certificate is used. acme-companion ENV for default settings that apply to all containers using LETSENCRYPT_HOST : DEFAULT_EMAIL : An email address that the CA ( eg: Let's Encrypt ) can contact you about expiring certificates, failed renewals, or for account recovery. You may want to use an email address not handled by your mail-server to ensure deliverability in the event your mail-server breaks. CERTS_UPDATE_INTERVAL : If you need to adjust the frequency to check for renewals. 3600 seconds (1 hour) by default. DEBUG=1 : Should be helpful when troubleshooting provisioning issues from acme-companion logs. ACME_CA_URI : Useful in combination with CA_BUNDLE to use a private CA. To change the default Let's Encrypt endpoint to the staging endpoint, use https://acme-staging-v02.api.letsencrypt.org/directory . CA_BUNDLE : If you want to use a private CA instead of Let's Encrypt . Alternative to required ENV on mailserver service While you will still need both nginx-proxy and acme-companion containers, you can manage certificates without adding ENV vars to containers. Instead the ENV is moved into a file and uses the acme-companion feature Standalone certificates . This requires adding another shared volume between nginx-proxy and acme-companion : services : reverse-proxy : volumes : - ./docker-data/nginx-proxy/conf.d/:/etc/nginx/conf.d/ acme-companion : volumes : - ./docker-data/nginx-proxy/conf.d/:/etc/nginx/conf.d/ - ./docker-data/acme-companion/standalone.sh:/app/letsencrypt_user_data:ro acme-companion mounts a shell script ( standalone.sh ), which defines variables to customize certificate provisioning: # A list IDs for certificates to provision: LETSENCRYPT_STANDALONE_CERTS =( 'mail' ) # Each ID inserts itself into the standard `acme-companion` supported container ENV vars below. # The LETSENCRYPT__HOST var is a list of FQDNs to provision a certificate for as the SAN field: LETSENCRYPT_mail_HOST =( 'mail.example.com' ) # Optional variables: LETSENCRYPT_mail_TEST = true LETSENCRYPT_mail_EMAIL = 'admin@example.com' # RSA-4096 => `4096`, ECDSA-256 => `ec-256`: LETSENCRYPT_mail_KEYSIZE = 4096 Unlike with the equivalent ENV for containers, changes to this file will not be detected automatically . You would need to wait until the next renewal check by acme-companion ( every hour by default ), restart acme-companion , or manually invoke the service loop : docker exec nginx-proxy-acme /app/signal_le_service Example using Let's Encrypt Certificates with a Synology NAS Version 6.2 and later of the Synology NAS DSM OS now come with an interface to generate and renew letencrypt certificates. Navigation into your DSM control panel and go to Security, then click on the tab Certificate to generate and manage letsencrypt certificates. Amongst other things, you can use these to secure your mail-server. DSM locates the generated certificates in a folder below /usr/syno/etc/certificate/_archive/ . Navigate to that folder and note the 6 character random folder name of the certificate you'd like to use. Then, add the following to your docker-compose.yml declaration file: # Note: If you have an existing setup that was working pre docker-mailserver v10.2, # '/tmp/dms/custom-certs' below has replaced the previous '/tmp/ssl' container path. volumes : - /usr/syno/etc/certificate/_archive//:/tmp/dms/custom-certs/ environment : - SSL_TYPE=manual - SSL_CERT_PATH=/tmp/dms/custom-certs/fullchain.pem - SSL_KEY_PATH=/tmp/dms/custom-certs/privkey.pem DSM-generated letsencrypt certificates get auto-renewed every three months. Caddy If you are using Caddy to renew your certificates, please note that only RSA certificates work. Read #1440 for details. In short for Caddy v1 the Caddyfile should look something like: https://mail.example.com { tls admin@example.com { key_type rsa2048 } } For Caddy v2 you can specify the key_type in your server's global settings, which would end up looking something like this if you're using a Caddyfile : { debug admin localhost:2019 http_port 80 https_port 443 default_sni example.com key_type rsa4096 } If you are instead using a json config for Caddy v2, you can set it in your site's TLS automation policies: Example Code { \"apps\" : { \"http\" : { \"servers\" : { \"srv0\" : { \"listen\" : [ \":443\" ], \"routes\" : [ { \"match\" : [ { \"host\" : [ \"mail.example.com\" , ] } ], \"handle\" : [ { \"handler\" : \"subroute\" , \"routes\" : [ { \"handle\" : [ { \"body\" : \"\" , \"handler\" : \"static_response\" } ] } ] } ], \"terminal\" : true }, ] } } }, \"tls\" : { \"automation\" : { \"policies\" : [ { \"subjects\" : [ \"mail.example.com\" , ], \"key_type\" : \"rsa2048\" , \"issuer\" : { \"email\" : \"admin@example.com\" , \"module\" : \"acme\" } }, { \"issuer\" : { \"email\" : \"admin@example.com\" , \"module\" : \"acme\" } } ] } } } } The generated certificates can be mounted: volumes : - ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.crt:/etc/letsencrypt/live/mail.example.com/fullchain.pem - ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.key:/etc/letsencrypt/live/mail.example.com/privkey.pem EC certificates fail in the TLS handshake: CONNECTED(00000003) 140342221178112:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:ssl/record/rec_layer_s3.c:1543:SSL alert number 40 no peer certificate available No client certificate CA names sent Traefik v2 Traefik is an open-source application proxy using the ACME protocol . Traefik can request certificates for domains and subdomains, and it will take care of renewals, challenge negotiations, etc. We strongly recommend to use Traefik 's major version 2. Traefik 's storage format is natively supported if the acme.json store is mounted into the container at /etc/letsencrypt/acme.json . The file is also monitored for changes and will trigger a reload of the mail services (Postfix and Dovecot). Wildcard certificates are supported. If your FQDN is mail.example.com and your wildcard certificate is *.example.com , add the ENV: SSL_DOMAIN = example.com . The mail-server will select it's certificate from acme.json checking these ENV for a matching FQDN ( in order of priority ): ${ SSL_DOMAIN } ${ HOSTNAME } ${ DOMAINNAME } This setup only comes with one caveat: The domain has to be configured on another service for Traefik to actually request it from Let's Encrypt , i.e. Traefik will not issue a certificate without a service / router demanding it. Example Code Here is an example setup for docker-compose : version : '3.8' services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail domainname : example.com volumes : - ./docker-data/traefik/acme.json:/etc/letsencrypt/acme.json:ro environment : SSL_TYPE : letsencrypt SSL_DOMAIN : mail.example.com # for a wildcard certificate, use # SSL_DOMAIN: example.com reverse-proxy : image : docker.io/traefik:latest #v2.5 container_name : docker-traefik ports : - \"80:80\" - \"443:443\" command : - --providers.docker - --entrypoints.http.address=:80 - --entrypoints.http.http.redirections.entryPoint.to=https - --entrypoints.http.http.redirections.entryPoint.scheme=https - --entrypoints.https.address=:443 - --entrypoints.https.http.tls.certResolver=letsencrypt - --certificatesresolvers.letsencrypt.acme.email=admin@example.com - --certificatesresolvers.letsencrypt.acme.storage=/acme.json - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=http volumes : - ./docker-data/traefik/acme.json:/acme.json - /var/run/docker.sock:/var/run/docker.sock:ro whoami : image : docker.io/traefik/whoami:latest labels : - \"traefik.http.routers.whoami.rule=Host(`mail.example.com`)\" Self-Signed Certificates Warning Use self-signed certificates only for testing purposes! This feature requires you to provide the following files into your docker-data/dms/config/ssl/ directory ( internal location: /tmp/docker-mailserver/ssl/ ): -key.pem -cert.pem demoCA/cacert.pem Where is the FQDN you've configured for your docker-mailserver container. Add SSL_TYPE=self-signed to your docker-mailserver environment variables. Postfix and Dovecot will be configured to use the provided certificate ( .pem files above ) during container startup. Generating a self-signed certificate Note Since docker-mailserver v10, support in setup.sh for generating a self-signed SSL certificate internally was removed. One way to generate self-signed certificates is with Smallstep's step CLI . This is exactly what docker-mailserver does for creating test certificates . For example with the FQDN mail.example.test , you can generate the required files by running: #! /bin/sh mkdir -p demoCA step certificate create \"Smallstep Root CA\" \"demoCA/cacert.pem\" \"demoCA/cakey.pem\" \\ --no-password --insecure \\ --profile root-ca \\ --not-before \"2021-01-01T00:00:00+00:00\" \\ --not-after \"2031-01-01T00:00:00+00:00\" \\ --san \"example.test\" \\ --san \"mail.example.test\" \\ --kty RSA --size 2048 step certificate create \"Smallstep Leaf\" mail.example.test-cert.pem mail.example.test-key.pem \\ --no-password --insecure \\ --profile leaf \\ --ca \"demoCA/cacert.pem\" \\ --ca-key \"demoCA/cakey.pem\" \\ --not-before \"2021-01-01T00:00:00+00:00\" \\ --not-after \"2031-01-01T00:00:00+00:00\" \\ --san \"example.test\" \\ --san \"mail.example.test\" \\ --kty RSA --size 2048 If you'd rather not install the CLI tool locally to run the step commands above; you can save the script above to a file such as generate-certs.sh ( and make it executable chmod +x generate-certs.sh ) in a directory that you want the certs to be placed (eg: docker-data/dms/custom-certs/ ), then use docker to run that script in a container: # '--user' is to keep ownership of the files written to # the local volume to use your systems User and Group ID values. docker run --rm -it \\ --user \" $( id -u ) : $( id -g ) \" \\ --volume \" ${ PWD } /docker-data/dms/custom-certs/:/tmp/step-ca/\" \\ --workdir \"/tmp/step-ca/\" \\ --entrypoint \"/tmp/step-ca/generate-certs.sh\" \\ smallstep/step-ca Bring Your Own Certificates You can also provide your own certificate files. Add these entries to your docker-compose.yml : volumes : - ./docker-data/dms/custom-certs/:/tmp/dms/custom-certs/:ro environment : - SSL_TYPE=manual # Values should match the file paths inside the container: - SSL_CERT_PATH=/tmp/dms/custom-certs/public.crt - SSL_KEY_PATH=/tmp/dms/custom-certs/private.key This will mount the path where your certificate files reside locally into the read-only container folder: /tmp/dms/custom-certs . The local and internal paths may be whatever you prefer, so long as both SSL_CERT_PATH and SSL_KEY_PATH point to the correct internal file paths. The certificate files may also be named to your preference, but should be PEM encoded. SSL_ALT_CERT_PATH and SSL_ALT_KEY_PATH are additional ENV vars to support a 2nd certificate as a fallback. Commonly known as hybrid or dual certificate support. This is useful for using a modern ECDSA as your primary certificate, and RSA as your fallback for older connections. They work in the same manner as the non- ALT versions. Info You may have to restart docker-mailserver once the certificates change. Testing a Certificate is Valid From your host: docker exec mailserver openssl s_client \\ -connect 0 .0.0.0:25 \\ -starttls smtp \\ -CApath /etc/ssl/certs/ Or: docker exec mailserver openssl s_client \\ -connect 0 .0.0.0:143 \\ -starttls imap \\ -CApath /etc/ssl/certs/ And you should see the certificate chain, the server certificate and: Verify return code: 0 (ok) In addition, to verify certificate dates: docker exec mailserver openssl s_client \\ -connect 0 .0.0.0:25 \\ -starttls smtp \\ -CApath /etc/ssl/certs/ \\ 2 >/dev/null | openssl x509 -noout -dates Plain-Text Access Warning Not recommended for purposes other than testing. Add this to docker-data/dms/config/dovecot.cf : ssl = yes disable_plaintext_auth = no These options in conjunction mean: SSL/TLS is offered to the client, but the client isn't required to use it. The client is allowed to login with plaintext authentication even when SSL/TLS isn't enabled on the connection. This is insecure , because the plaintext password is exposed to the internet. Importing Certificates Obtained via Another Source If you have another source for SSL/TLS certificates you can import them into the server via an external script. The external script can be found here: external certificate import script . Only compatible with docker-mailserver releases < v10.2 The script expects /etc/postfix/ssl/cert and /etc/postfix/ssl/key files to be configured paths for both Postfix and Dovecot to use. Since the docker-mailserver 10.2 release, certificate files have moved to /etc/dms/tls/ , and the file name may differ depending on provisioning method. This third-party script also has fullchain.pem and privkey.pem as hard-coded, thus is incompatible with other filenames. Additionally it has never supported handling ALT fallback certificates (for supporting dual/hybrid, RSA + ECDSA). The steps to follow are these: Transfer the new certificates to ./docker-data/dms/custom-certs/ (volume mounted to: /tmp/ssl/ ) You should provide fullchain.key and privkey.pem Place the script in ./docker-data/dms/config/ (volume mounted to: /tmp/docker-mailserver/ ) Make the script executable ( chmod +x tomav-renew-certs.sh ) Run the script: docker exec mailserver /tmp/docker-mailserver/tomav-renew-certs.sh If an error occurs the script will inform you. If not you will see both postfix and dovecot restart. After the certificates have been loaded you can check the certificate: openssl s_client \\ -servername mail.example.com \\ -connect 192 .168.0.72:465 \\ 2 >/dev/null | openssl x509 # or openssl s_client \\ -servername mail.example.com \\ -connect mail.example.com:465 \\ 2 >/dev/null | openssl x509 Or you can check how long the new certificate is valid with commands like: export SITE_URL = \"mail.example.com\" export SITE_IP_URL = \"192.168.0.72\" # can also use `mail.example.com` export SITE_SSL_PORT = \"993\" # imap port dovecot ##works: check if certificate will expire in two weeks #2 weeks is 1209600 seconds #3 weeks is 1814400 #12 weeks is 7257600 #15 weeks is 9072000 certcheck_2weeks = ` openssl s_client -connect ${ SITE_IP_URL } : ${ SITE_SSL_PORT } \\ -servername ${ SITE_URL } 2 > /dev/null | openssl x509 -noout -checkend 1209600 ` #################################### #notes: output could be either: #Certificate will not expire #Certificate will expire #################### What does the script that imports the certificates do: Check if there are new certs in the internal container folder: /tmp/ssl . Check with the ssl cert fingerprint if they differ from the current certificates. If so it will copy the certs to the right places. And restart postfix and dovecot. You can of course run the script by cron once a week or something. In that way you could automate cert renewal. If you do so it is probably wise to run an automated check on certificate expiry as well. Such a check could look something like this: # This script is run inside docker-mailserver via 'docker exec ...', using the 'mail' command to send alerts. ## code below will alert if certificate expires in less than two weeks ## please adjust varables! ## make sure the 'mail -s' command works! Test! export SITE_URL = \"mail.example.com\" export SITE_IP_URL = \"192.168.2.72\" # can also use `mail.example.com` export SITE_SSL_PORT = \"993\" # imap port dovecot # Below can be from a different domain; like your personal email, not handled by this docker-mailserver: export ALERT_EMAIL_ADDR = \"external-account@gmail.com\" certcheck_2weeks = ` openssl s_client -connect ${ SITE_IP_URL } : ${ SITE_SSL_PORT } \\ -servername ${ SITE_URL } 2 > /dev/null | openssl x509 -noout -checkend 1209600 ` #################################### #notes: output can be #Certificate will not expire #Certificate will expire #################### #echo \"certcheck 2 weeks gives $certcheck_2weeks\" ##automated check you might run by cron or something ## does the certificate expire within two weeks? if [ \" $certcheck_2weeks \" = \"Certificate will not expire\" ] ; then echo \"all is well, certwatch 2 weeks says $certcheck_2weeks \" else echo \"Cert seems to be expiring pretty soon, within two weeks: $certcheck_2weeks \" echo \"we will send an alert email and log as well\" logger Certwatch: cert $SITE_URL will expire in two weeks echo \"Certwatch: cert $SITE_URL will expire in two weeks\" | mail -s \"cert $SITE_URL expires in two weeks \" $ALERT_EMAIL_ADDR fi Custom DH Parameters By default docker-mailserver uses ffdhe4096 from IETF RFC 7919 . These are standardized pre-defined DH groups and the only available DH groups for TLS 1.3. It is discouraged to generate your own DH parameters as it is often less secure. Despite this, if you must use non-standard DH parameters or you would like to swap ffdhe4096 for a different group (eg ffdhe2048 ); Add your own PEM encoded DH params file via a volume to /tmp/docker-mailserver/dhparams.pem . This will replace DH params for both Dovecot and Postfix services during container startup.","title":"SSL/TLS"},{"location":"config/security/ssl/#the-fqdn","text":"An FQDN ( Fully Qualified Domain Name ) such as mail.example.com is required for docker-mailserver to function correctly, especially for looking up the correct SSL certificate to use. Internally, hostname -f will be used to retrieve the FQDN as configured in the below examples. Wildcard certificates (eg: *.example.com ) are supported for SSL_TYPE=letsencrypt . Your configured FQDN below may be mail.example.com , and your wildcard certificate provisioned to /etc/letsencrypt/live/example.com which will be checked as a fallback FQDN by docker-mailserver . Docker CLI options --hostname and optionally --domainname docker run --hostname mail --domainname example.com # `--domainname` is not required: docker run --hostname mail.example.com docker-compose.yml config services : mailserver : hostname : mail domainname : example.com # `domainname` is not required: services : mailserver : hostname : mail.example.com Bare domains (eg: example.com ) should only use the hostname option docker run --hostname example.com services : mailserver : hostname : example.com","title":"The FQDN"},{"location":"config/security/ssl/#provisioning-methods","text":"","title":"Provisioning methods"},{"location":"config/security/ssl/#lets-encrypt-recommended","text":"To enable Let's Encrypt for docker-mailserver , you have to: Get your certificate using the Let's Encrypt client Certbot . For your docker-mailserver container: Add the environment variable SSL_TYPE=letsencrypt . Mount your local letsencrypt folder as a volume to /etc/letsencrypt . You don't have to do anything else. Enjoy! Note /etc/letsencrypt/live stores provisioned certificates in individual folders named by their FQDN. Make sure that the entire folder is mounted to docker-mailserver as there are typically symlinks from /etc/letsencrypt/live/mail.example.com to /etc/letsencrypt/archive . Example Add these additions to the mailserver service in your docker-compose.yml : services : mailserver : # For the FQDN 'mail.example.com': hostname : mail domainname : example.com environment : - SSL_TYPE=letsencrypt volumes : - /etc/letsencrypt:/etc/letsencrypt","title":"Let's Encrypt (Recommended)"},{"location":"config/security/ssl/#example-using-docker-for-lets-encrypt","text":"Certbot provisions certificates to /etc/letsencrypt . Add a volume to store these, so that they can later be accessed by docker-mailserver container. You may also want to persist Certbot logs , just in case you need to troubleshoot. Getting a certificate is this simple! ( Referencing: Certbot docker instructions and certonly --standalone mode ): # Change `mail.example.com` below to your own FQDN. # Requires access to port 80 from the internet, adjust your firewall if needed. docker run --rm -it \\ -v \" ${ PWD } /docker-data/certbot/certs/:/etc/letsencrypt/\" \\ -v \" ${ PWD } /docker-data/certbot/logs/:/var/log/letsencrypt/\" \\ -p 80 :80 \\ certbot/certbot certonly --standalone -d mail.example.com Add a volume for docker-mailserver that maps the local certbot/certs/ folder to the container path /etc/letsencrypt/ . Example Add these additions to the mailserver service in your docker-compose.yml : services : mailserver : # For the FQDN 'mail.example.com': hostname : mail domainname : example.com environment : - SSL_TYPE=letsencrypt volumes : - ./docker-data/certbot/certs/:/etc/letsencrypt The certificate setup is complete, but remember it will expire . Consider automating renewals. Renewing Certificates When running the above certonly --standalone snippet again, the existing certificate is renewed if it would expire within 30 days. Alternatively, Certbot can look at all the certificates it manages, and only renew those nearing their expiry via the renew command : # This will need access to port 443 from the internet, adjust your firewall if needed. docker run --rm -it \\ -v \" ${ PWD } /docker-data/certbot/certs/:/etc/letsencrypt/\" \\ -v \" ${ PWD } /docker-data/certbot/logs/:/var/log/letsencrypt/\" \\ -p 80 :80 \\ -p 443 :443 \\ certbot/certbot renew This process can also be automated via cron or systemd timers . Using a different ACME CA Certbot does support alternative certificate providers via the --server option. In most cases you'll want to use the default Let's Encrypt .","title":"Certbot with Docker"},{"location":"config/security/ssl/#example-using-certbot-dns-cloudflare-with-docker","text":"If you are unable get a certificate via the HTTP-01 (port 80) or TLS-ALPN-01 (port 443) challenge types , the DNS-01 challenge can be useful ( this challenge can additionally issue wildcard certificates ). This guide shows how to use the DNS-01 challenge with Cloudflare as your DNS provider. Obtain a Cloudflare API token: Login into your Cloudflare dashboard. Navigate to the API Tokens page . Click \"Create Token\", and choose the Edit zone DNS template ( Certbot requires the ZONE:DNS:Edit permission ). Only include the necessary Zone resource configuration Be sure to configure \"Zone Resources\" section on this page to Include -> Specific zone -> . This restricts the API token to only this zone (domain) which is an important security measure. Store the API token you received in a file cloudflare.ini with content: dns_cloudflare_api_token = YOUR_CLOUDFLARE_TOKEN_HERE As this is sensitive data, you should restrict access to it with chmod 600 and chown 0:0 . Store the file in a folder if you like, such as docker-data/certbot/secrets/ . Your docker-compose.yml should include the following: services : mailserver : environments : # Set SSL certificate type. - SSL_TYPE=letsencrypt volumes : # Mount the cert folder generated by Certbot into mail-server: - ./docker-data/certbot/certs/:/etc/letsencrypt/:ro certbot-cloudflare : image : certbot/dns-cloudflare:latest command : certonly --dns-cloudflare --dns-cloudflare-credentials /run/secrets/cloudflare-api-token -d mail.example.com volumes : - ./docker-data/certbot/certs/:/etc/letsencrypt/ - ./docker-data/certbot/logs/:/var/log/letsencrypt/ secrets : - cloudflare-api-token # Docs: https://docs.docker.com/engine/swarm/secrets/#use-secrets-in-compose # WARNING: In compose configs without swarm, the long syntax options have no effect, # Ensure that you properly `chmod 600` and `chown 0:0` the file on disk. Effectively treated as a bind mount. secrets : cloudflare-api-token : file : ./docker-data/certbot/secrets/cloudflare.ini Alternative using the docker run command ( secrets feature is not available): docker run \\ --volume \" ${ PWD } /docker-data/certbot/certs/:/etc/letsencrypt/\" \\ --volume \" ${ PWD } /docker-data/certbot/logs/:/var/log/letsencrypt/\" \\ --volume \" ${ PWD } /docker-data/certbot/secrets/:/tmp/secrets/certbot/\" certbot/dns-cloudflare \\ certonly --dns-cloudflare --dns-cloudflare-credentials /tmp/secrets/certbot/cloudflare.ini -d mail.example.com Run the service to provision a certificate: docker-compose run certbot-cloudflare You should see the following log output: Saving debug log to /var/log/letsencrypt/letsencrypt. log | Requesting a certificate for mail.example.com Waiting 10 seconds for DNS changes to propagate Successfully received certificate. Certificate is saved at: /etc/letsencrypt/live/mail.example.com/fullchain.pem Key is saved at: /etc/letsencrypt/live/mail.example.com/privkey.pem This certificate expires on YYYY-MM-DD. These files will be updated when the certificate renews. NEXT STEPS: - The certificate will need to be renewed before it expires. Certbot can automatically renew the certificate in background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal structions. After completing the steps above, your certificate should be ready to use. Renewing a certificate (Optional) We've only demonstrated how to provision a certificate, but it will expire in 90 days and need to be renewed before then. In the following example, add a new service ( certbot-cloudflare-renew ) into docker-compose.yml that will handle certificate renewals: services : certbot-cloudflare-renew : image : certbot/dns-cloudflare:latest command : renew --dns-cloudflare --dns-cloudflare-credentials /run/secrets/cloudflare-api-token volumes : - ./docker-data/certbot/certs/:/etc/letsencrtypt/ - ./docker-data/certbot/logs/:/var/log/letsencrypt/ secrets : - cloudflare-api-token You can manually run this service to renew the cert within 90 days: docker-compose run certbot-cloudflare-renew You should see the following output (The following log was generated with --dry-run options) Saving debug log to /var/log/letsencrypt/letsencrypt.log - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Processing /etc/letsencrypt/renewal/mail.example.com.conf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Account registered. Simulating renewal of an existing certificate for mail.example.com Waiting 10 seconds for DNS changes to propagate - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/mail.example.com/fullchain.pem (success) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - It is recommended to automate this renewal via a task scheduler like a systemd timer or in crontab ( crontab example: Checks every day if the certificate should be renewed) 0 0 * * * docker-compose -f PATH_TO_YOUR_DOCKER_COMPOSE_YML up certbot-cloudflare-renew","title":"certbot-dns-cloudflare with Docker"},{"location":"config/security/ssl/#example-using-nginx-proxy-and-acme-companion-with-docker","text":"If you are running a web server already, port 80 will be in use which Certbot requires. You could use the Certbot --webroot feature, but it is more common to leverage a reverse proxy that manages the provisioning and renewal of certificates for your services automatically. In the following example, we show how docker-mailserver can be run alongside the docker containers nginx-proxy and acme-companion ( Referencing: acme-companion documentation ): Start the reverse proxy ( nginx-proxy ): docker run --detach \\ --name nginx-proxy \\ --restart always \\ --publish 80 :80 \\ --publish 443 :443 \\ --volume \" ${ PWD } /docker-data/nginx-proxy/html/:/usr/share/nginx/html/\" \\ --volume \" ${ PWD } /docker-data/nginx-proxy/vhost.d/:/etc/nginx/vhost.d/\" \\ --volume \" ${ PWD } /docker-data/acme-companion/certs/:/etc/nginx/certs/:ro\" \\ --volume '/var/run/docker.sock:/tmp/docker.sock:ro' \\ nginxproxy/nginx-proxy Then start the certificate provisioner ( acme-companion ), which will provide certificates to nginx-proxy : # Inherit `nginx-proxy` volumes via `--volumes-from`, but make `certs/` writeable: docker run --detach \\ --name nginx-proxy-acme \\ --restart always \\ --volumes-from nginx-proxy \\ --volume \" ${ PWD } /docker-data/acme-companion/certs/:/etc/nginx/certs/:rw\" \\ --volume \" ${ PWD } /docker-data/acme-companion/acme-state/:/etc/acme.sh/\" \\ --volume '/var/run/docker.sock:/var/run/docker.sock:ro' \\ --env 'DEFAULT_EMAIL=admin@example.com' \\ nginxproxy/acme-companion Start the rest of your web server containers as usual. Start a dummy container to provision certificates for your FQDN (eg: mail.example.com ). acme-companion will detect the container and generate a Let's Encrypt certificate for your domain, which can be used by docker-mailserver : docker run --detach \\ --name webmail \\ --env 'VIRTUAL_HOST=mail.example.com' \\ --env 'LETSENCRYPT_HOST=mail.example.com' \\ --env 'LETSENCRYPT_EMAIL=admin@example.com' \\ nginx You may want to add --env LETSENCRYPT_TEST=true to the above while testing, to avoid the Let's Encrypt certificate generation rate limits. Make sure your mount path to the letsencrypt certificates directory is correct. Edit your docker-compose.yml for the mailserver service to have volumes added like below: volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - ./docker-data/acme-companion/certs/:/etc/letsencrypt/live/:ro Then from the docker-compose.yml project directory, run: docker-compose up -d mailserver .","title":"nginx-proxy with Docker"},{"location":"config/security/ssl/#example-using-nginx-proxy-and-acme-companion-with-docker-compose","text":"The following example is the basic setup you need for using nginx-proxy and acme-companion with docker-mailserver ( Referencing: acme-companion documentation ): Example: docker-compose.yml You should have an existing docker-compose.yml with a mailserver service. Below are the modifications to add for integrating with nginx-proxy and acme-companion services: version : '3.8' services : # Add the following `environment` and `volumes` to your existing `mailserver` service: mailserver : environment : # SSL_TYPE: Uses the `letsencrypt` method to find mounted certificates. # VIRTUAL_HOST: The FQDN that `nginx-proxy` will configure itself to handle for HTTP[S] connections. # LETSENCRYPT_HOST: The FQDN for a certificate that `acme-companion` will provision and renew. - SSL_TYPE=letsencrypt - VIRTUAL_HOST=mail.example.com - LETSENCRYPT_HOST=mail.example.com volumes : - ./docker-data/acme-companion/certs/:/etc/letsencrypt/live/:ro # If you don't yet have your own `nginx-proxy` and `acme-companion` setup, # here is an example you can use: reverse-proxy : image : nginxproxy/nginx-proxy container_name : nginx-proxy restart : always ports : # Port 80: Required for HTTP-01 challenges to `acme-companion`. # Port 443: Only required for containers that need access over HTTPS. TLS-ALPN-01 challenge not supported. - \"80:80\" - \"443:443\" volumes : # `certs/`: Managed by the `acme-companion` container (_read-only_). # `docker.sock`: Required to interact with containers via the Docker API. # `dhparam`: A named data volume to prevent `nginx-proxy` creating an anonymous volume each time. - ./docker-data/nginx-proxy/html/:/usr/share/nginx/html/ - ./docker-data/nginx-proxy/vhost.d/:/etc/nginx/vhost.d/ - ./docker-data/acme-companion/certs/:/etc/nginx/certs/:ro - /var/run/docker.sock:/tmp/docker.sock:ro - dhparam:/etc/nginx/dhparam acme-companion : image : nginxproxy/acme-companion container_name : nginx-proxy-acme restart : always environment : # Only docker-compose v2 supports: `volumes_from: [nginx-proxy]`, # reference the _reverse-proxy_ `container_name` here: - NGINX_PROXY_CONTAINER=nginx-proxy volumes : # `html/`: Write ACME HTTP-01 challenge files that `nginx-proxy` will serve. # `vhost.d/`: To enable web access via `nginx-proxy` to HTTP-01 challenge files. # `certs/`: To store certificates and private keys. # `acme-state/`: To persist config and state for the ACME provisioner (`acme.sh`). # `docker.sock`: Required to interact with containers via the Docker API. - ./docker-data/nginx-proxy/html/:/usr/share/nginx/html/ - ./docker-data/nginx-proxy/vhost.d/:/etc/nginx/vhost.d/ - ./docker-data/acme-companion/certs/:/etc/nginx/certs/:rw - ./docker-data/acme-companion/acme-state/:/etc/acme.sh/ - /var/run/docker.sock:/var/run/docker.sock:ro # Once `nginx-proxy` fixes their Dockerfile, this named data volume can be removed from docs. # Users can opt for a local bind mount volume like all others if they prefer, but this volume # is only intended to be temporary. volumes : dhparam : Optional ENV vars worth knowing about Per container ENV that acme-companion will detect to override default provisioning settings: LETSENCRYPT_TEST=true : Recommended during initial setup . Otherwise the default production endpoint has a rate limit of 5 duplicate certificates per week . Overrides ACME_CA_URI to use the Let's Encrypt staging endpoint. LETSENCRYPT_EMAIL : For when you don't use DEFAULT_EMAIL on acme-companion , or want to assign a different email contact for this container. LETSENCRYPT_KEYSIZE : Allows you to configure the type (RSA or ECDSA) and size of the private key for your certificate. Default is RSA 4096. LETSENCRYPT_RESTART_CONTAINER=true : When the certificate is renewed, the entire container will be restarted to ensure the new certificate is used. acme-companion ENV for default settings that apply to all containers using LETSENCRYPT_HOST : DEFAULT_EMAIL : An email address that the CA ( eg: Let's Encrypt ) can contact you about expiring certificates, failed renewals, or for account recovery. You may want to use an email address not handled by your mail-server to ensure deliverability in the event your mail-server breaks. CERTS_UPDATE_INTERVAL : If you need to adjust the frequency to check for renewals. 3600 seconds (1 hour) by default. DEBUG=1 : Should be helpful when troubleshooting provisioning issues from acme-companion logs. ACME_CA_URI : Useful in combination with CA_BUNDLE to use a private CA. To change the default Let's Encrypt endpoint to the staging endpoint, use https://acme-staging-v02.api.letsencrypt.org/directory . CA_BUNDLE : If you want to use a private CA instead of Let's Encrypt . Alternative to required ENV on mailserver service While you will still need both nginx-proxy and acme-companion containers, you can manage certificates without adding ENV vars to containers. Instead the ENV is moved into a file and uses the acme-companion feature Standalone certificates . This requires adding another shared volume between nginx-proxy and acme-companion : services : reverse-proxy : volumes : - ./docker-data/nginx-proxy/conf.d/:/etc/nginx/conf.d/ acme-companion : volumes : - ./docker-data/nginx-proxy/conf.d/:/etc/nginx/conf.d/ - ./docker-data/acme-companion/standalone.sh:/app/letsencrypt_user_data:ro acme-companion mounts a shell script ( standalone.sh ), which defines variables to customize certificate provisioning: # A list IDs for certificates to provision: LETSENCRYPT_STANDALONE_CERTS =( 'mail' ) # Each ID inserts itself into the standard `acme-companion` supported container ENV vars below. # The LETSENCRYPT__HOST var is a list of FQDNs to provision a certificate for as the SAN field: LETSENCRYPT_mail_HOST =( 'mail.example.com' ) # Optional variables: LETSENCRYPT_mail_TEST = true LETSENCRYPT_mail_EMAIL = 'admin@example.com' # RSA-4096 => `4096`, ECDSA-256 => `ec-256`: LETSENCRYPT_mail_KEYSIZE = 4096 Unlike with the equivalent ENV for containers, changes to this file will not be detected automatically . You would need to wait until the next renewal check by acme-companion ( every hour by default ), restart acme-companion , or manually invoke the service loop : docker exec nginx-proxy-acme /app/signal_le_service","title":"nginx-proxy with docker-compose"},{"location":"config/security/ssl/#example-using-lets-encrypt-certificates-with-a-synology-nas","text":"Version 6.2 and later of the Synology NAS DSM OS now come with an interface to generate and renew letencrypt certificates. Navigation into your DSM control panel and go to Security, then click on the tab Certificate to generate and manage letsencrypt certificates. Amongst other things, you can use these to secure your mail-server. DSM locates the generated certificates in a folder below /usr/syno/etc/certificate/_archive/ . Navigate to that folder and note the 6 character random folder name of the certificate you'd like to use. Then, add the following to your docker-compose.yml declaration file: # Note: If you have an existing setup that was working pre docker-mailserver v10.2, # '/tmp/dms/custom-certs' below has replaced the previous '/tmp/ssl' container path. volumes : - /usr/syno/etc/certificate/_archive//:/tmp/dms/custom-certs/ environment : - SSL_TYPE=manual - SSL_CERT_PATH=/tmp/dms/custom-certs/fullchain.pem - SSL_KEY_PATH=/tmp/dms/custom-certs/privkey.pem DSM-generated letsencrypt certificates get auto-renewed every three months.","title":"Synology NAS"},{"location":"config/security/ssl/#caddy","text":"If you are using Caddy to renew your certificates, please note that only RSA certificates work. Read #1440 for details. In short for Caddy v1 the Caddyfile should look something like: https://mail.example.com { tls admin@example.com { key_type rsa2048 } } For Caddy v2 you can specify the key_type in your server's global settings, which would end up looking something like this if you're using a Caddyfile : { debug admin localhost:2019 http_port 80 https_port 443 default_sni example.com key_type rsa4096 } If you are instead using a json config for Caddy v2, you can set it in your site's TLS automation policies: Example Code { \"apps\" : { \"http\" : { \"servers\" : { \"srv0\" : { \"listen\" : [ \":443\" ], \"routes\" : [ { \"match\" : [ { \"host\" : [ \"mail.example.com\" , ] } ], \"handle\" : [ { \"handler\" : \"subroute\" , \"routes\" : [ { \"handle\" : [ { \"body\" : \"\" , \"handler\" : \"static_response\" } ] } ] } ], \"terminal\" : true }, ] } } }, \"tls\" : { \"automation\" : { \"policies\" : [ { \"subjects\" : [ \"mail.example.com\" , ], \"key_type\" : \"rsa2048\" , \"issuer\" : { \"email\" : \"admin@example.com\" , \"module\" : \"acme\" } }, { \"issuer\" : { \"email\" : \"admin@example.com\" , \"module\" : \"acme\" } } ] } } } } The generated certificates can be mounted: volumes : - ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.crt:/etc/letsencrypt/live/mail.example.com/fullchain.pem - ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.key:/etc/letsencrypt/live/mail.example.com/privkey.pem EC certificates fail in the TLS handshake: CONNECTED(00000003) 140342221178112:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:ssl/record/rec_layer_s3.c:1543:SSL alert number 40 no peer certificate available No client certificate CA names sent","title":"Caddy"},{"location":"config/security/ssl/#traefik-v2","text":"Traefik is an open-source application proxy using the ACME protocol . Traefik can request certificates for domains and subdomains, and it will take care of renewals, challenge negotiations, etc. We strongly recommend to use Traefik 's major version 2. Traefik 's storage format is natively supported if the acme.json store is mounted into the container at /etc/letsencrypt/acme.json . The file is also monitored for changes and will trigger a reload of the mail services (Postfix and Dovecot). Wildcard certificates are supported. If your FQDN is mail.example.com and your wildcard certificate is *.example.com , add the ENV: SSL_DOMAIN = example.com . The mail-server will select it's certificate from acme.json checking these ENV for a matching FQDN ( in order of priority ): ${ SSL_DOMAIN } ${ HOSTNAME } ${ DOMAINNAME } This setup only comes with one caveat: The domain has to be configured on another service for Traefik to actually request it from Let's Encrypt , i.e. Traefik will not issue a certificate without a service / router demanding it. Example Code Here is an example setup for docker-compose : version : '3.8' services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail domainname : example.com volumes : - ./docker-data/traefik/acme.json:/etc/letsencrypt/acme.json:ro environment : SSL_TYPE : letsencrypt SSL_DOMAIN : mail.example.com # for a wildcard certificate, use # SSL_DOMAIN: example.com reverse-proxy : image : docker.io/traefik:latest #v2.5 container_name : docker-traefik ports : - \"80:80\" - \"443:443\" command : - --providers.docker - --entrypoints.http.address=:80 - --entrypoints.http.http.redirections.entryPoint.to=https - --entrypoints.http.http.redirections.entryPoint.scheme=https - --entrypoints.https.address=:443 - --entrypoints.https.http.tls.certResolver=letsencrypt - --certificatesresolvers.letsencrypt.acme.email=admin@example.com - --certificatesresolvers.letsencrypt.acme.storage=/acme.json - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=http volumes : - ./docker-data/traefik/acme.json:/acme.json - /var/run/docker.sock:/var/run/docker.sock:ro whoami : image : docker.io/traefik/whoami:latest labels : - \"traefik.http.routers.whoami.rule=Host(`mail.example.com`)\"","title":"Traefik v2"},{"location":"config/security/ssl/#self-signed-certificates","text":"Warning Use self-signed certificates only for testing purposes! This feature requires you to provide the following files into your docker-data/dms/config/ssl/ directory ( internal location: /tmp/docker-mailserver/ssl/ ): -key.pem -cert.pem demoCA/cacert.pem Where is the FQDN you've configured for your docker-mailserver container. Add SSL_TYPE=self-signed to your docker-mailserver environment variables. Postfix and Dovecot will be configured to use the provided certificate ( .pem files above ) during container startup.","title":"Self-Signed Certificates"},{"location":"config/security/ssl/#generating-a-self-signed-certificate","text":"Note Since docker-mailserver v10, support in setup.sh for generating a self-signed SSL certificate internally was removed. One way to generate self-signed certificates is with Smallstep's step CLI . This is exactly what docker-mailserver does for creating test certificates . For example with the FQDN mail.example.test , you can generate the required files by running: #! /bin/sh mkdir -p demoCA step certificate create \"Smallstep Root CA\" \"demoCA/cacert.pem\" \"demoCA/cakey.pem\" \\ --no-password --insecure \\ --profile root-ca \\ --not-before \"2021-01-01T00:00:00+00:00\" \\ --not-after \"2031-01-01T00:00:00+00:00\" \\ --san \"example.test\" \\ --san \"mail.example.test\" \\ --kty RSA --size 2048 step certificate create \"Smallstep Leaf\" mail.example.test-cert.pem mail.example.test-key.pem \\ --no-password --insecure \\ --profile leaf \\ --ca \"demoCA/cacert.pem\" \\ --ca-key \"demoCA/cakey.pem\" \\ --not-before \"2021-01-01T00:00:00+00:00\" \\ --not-after \"2031-01-01T00:00:00+00:00\" \\ --san \"example.test\" \\ --san \"mail.example.test\" \\ --kty RSA --size 2048 If you'd rather not install the CLI tool locally to run the step commands above; you can save the script above to a file such as generate-certs.sh ( and make it executable chmod +x generate-certs.sh ) in a directory that you want the certs to be placed (eg: docker-data/dms/custom-certs/ ), then use docker to run that script in a container: # '--user' is to keep ownership of the files written to # the local volume to use your systems User and Group ID values. docker run --rm -it \\ --user \" $( id -u ) : $( id -g ) \" \\ --volume \" ${ PWD } /docker-data/dms/custom-certs/:/tmp/step-ca/\" \\ --workdir \"/tmp/step-ca/\" \\ --entrypoint \"/tmp/step-ca/generate-certs.sh\" \\ smallstep/step-ca","title":"Generating a self-signed certificate"},{"location":"config/security/ssl/#bring-your-own-certificates","text":"You can also provide your own certificate files. Add these entries to your docker-compose.yml : volumes : - ./docker-data/dms/custom-certs/:/tmp/dms/custom-certs/:ro environment : - SSL_TYPE=manual # Values should match the file paths inside the container: - SSL_CERT_PATH=/tmp/dms/custom-certs/public.crt - SSL_KEY_PATH=/tmp/dms/custom-certs/private.key This will mount the path where your certificate files reside locally into the read-only container folder: /tmp/dms/custom-certs . The local and internal paths may be whatever you prefer, so long as both SSL_CERT_PATH and SSL_KEY_PATH point to the correct internal file paths. The certificate files may also be named to your preference, but should be PEM encoded. SSL_ALT_CERT_PATH and SSL_ALT_KEY_PATH are additional ENV vars to support a 2nd certificate as a fallback. Commonly known as hybrid or dual certificate support. This is useful for using a modern ECDSA as your primary certificate, and RSA as your fallback for older connections. They work in the same manner as the non- ALT versions. Info You may have to restart docker-mailserver once the certificates change.","title":"Bring Your Own Certificates"},{"location":"config/security/ssl/#testing-a-certificate-is-valid","text":"From your host: docker exec mailserver openssl s_client \\ -connect 0 .0.0.0:25 \\ -starttls smtp \\ -CApath /etc/ssl/certs/ Or: docker exec mailserver openssl s_client \\ -connect 0 .0.0.0:143 \\ -starttls imap \\ -CApath /etc/ssl/certs/ And you should see the certificate chain, the server certificate and: Verify return code: 0 (ok) In addition, to verify certificate dates: docker exec mailserver openssl s_client \\ -connect 0 .0.0.0:25 \\ -starttls smtp \\ -CApath /etc/ssl/certs/ \\ 2 >/dev/null | openssl x509 -noout -dates","title":"Testing a Certificate is Valid"},{"location":"config/security/ssl/#plain-text-access","text":"Warning Not recommended for purposes other than testing. Add this to docker-data/dms/config/dovecot.cf : ssl = yes disable_plaintext_auth = no These options in conjunction mean: SSL/TLS is offered to the client, but the client isn't required to use it. The client is allowed to login with plaintext authentication even when SSL/TLS isn't enabled on the connection. This is insecure , because the plaintext password is exposed to the internet.","title":"Plain-Text Access"},{"location":"config/security/ssl/#importing-certificates-obtained-via-another-source","text":"If you have another source for SSL/TLS certificates you can import them into the server via an external script. The external script can be found here: external certificate import script . Only compatible with docker-mailserver releases < v10.2 The script expects /etc/postfix/ssl/cert and /etc/postfix/ssl/key files to be configured paths for both Postfix and Dovecot to use. Since the docker-mailserver 10.2 release, certificate files have moved to /etc/dms/tls/ , and the file name may differ depending on provisioning method. This third-party script also has fullchain.pem and privkey.pem as hard-coded, thus is incompatible with other filenames. Additionally it has never supported handling ALT fallback certificates (for supporting dual/hybrid, RSA + ECDSA). The steps to follow are these: Transfer the new certificates to ./docker-data/dms/custom-certs/ (volume mounted to: /tmp/ssl/ ) You should provide fullchain.key and privkey.pem Place the script in ./docker-data/dms/config/ (volume mounted to: /tmp/docker-mailserver/ ) Make the script executable ( chmod +x tomav-renew-certs.sh ) Run the script: docker exec mailserver /tmp/docker-mailserver/tomav-renew-certs.sh If an error occurs the script will inform you. If not you will see both postfix and dovecot restart. After the certificates have been loaded you can check the certificate: openssl s_client \\ -servername mail.example.com \\ -connect 192 .168.0.72:465 \\ 2 >/dev/null | openssl x509 # or openssl s_client \\ -servername mail.example.com \\ -connect mail.example.com:465 \\ 2 >/dev/null | openssl x509 Or you can check how long the new certificate is valid with commands like: export SITE_URL = \"mail.example.com\" export SITE_IP_URL = \"192.168.0.72\" # can also use `mail.example.com` export SITE_SSL_PORT = \"993\" # imap port dovecot ##works: check if certificate will expire in two weeks #2 weeks is 1209600 seconds #3 weeks is 1814400 #12 weeks is 7257600 #15 weeks is 9072000 certcheck_2weeks = ` openssl s_client -connect ${ SITE_IP_URL } : ${ SITE_SSL_PORT } \\ -servername ${ SITE_URL } 2 > /dev/null | openssl x509 -noout -checkend 1209600 ` #################################### #notes: output could be either: #Certificate will not expire #Certificate will expire #################### What does the script that imports the certificates do: Check if there are new certs in the internal container folder: /tmp/ssl . Check with the ssl cert fingerprint if they differ from the current certificates. If so it will copy the certs to the right places. And restart postfix and dovecot. You can of course run the script by cron once a week or something. In that way you could automate cert renewal. If you do so it is probably wise to run an automated check on certificate expiry as well. Such a check could look something like this: # This script is run inside docker-mailserver via 'docker exec ...', using the 'mail' command to send alerts. ## code below will alert if certificate expires in less than two weeks ## please adjust varables! ## make sure the 'mail -s' command works! Test! export SITE_URL = \"mail.example.com\" export SITE_IP_URL = \"192.168.2.72\" # can also use `mail.example.com` export SITE_SSL_PORT = \"993\" # imap port dovecot # Below can be from a different domain; like your personal email, not handled by this docker-mailserver: export ALERT_EMAIL_ADDR = \"external-account@gmail.com\" certcheck_2weeks = ` openssl s_client -connect ${ SITE_IP_URL } : ${ SITE_SSL_PORT } \\ -servername ${ SITE_URL } 2 > /dev/null | openssl x509 -noout -checkend 1209600 ` #################################### #notes: output can be #Certificate will not expire #Certificate will expire #################### #echo \"certcheck 2 weeks gives $certcheck_2weeks\" ##automated check you might run by cron or something ## does the certificate expire within two weeks? if [ \" $certcheck_2weeks \" = \"Certificate will not expire\" ] ; then echo \"all is well, certwatch 2 weeks says $certcheck_2weeks \" else echo \"Cert seems to be expiring pretty soon, within two weeks: $certcheck_2weeks \" echo \"we will send an alert email and log as well\" logger Certwatch: cert $SITE_URL will expire in two weeks echo \"Certwatch: cert $SITE_URL will expire in two weeks\" | mail -s \"cert $SITE_URL expires in two weeks \" $ALERT_EMAIL_ADDR fi","title":"Importing Certificates Obtained via Another Source"},{"location":"config/security/ssl/#custom-dh-parameters","text":"By default docker-mailserver uses ffdhe4096 from IETF RFC 7919 . These are standardized pre-defined DH groups and the only available DH groups for TLS 1.3. It is discouraged to generate your own DH parameters as it is often less secure. Despite this, if you must use non-standard DH parameters or you would like to swap ffdhe4096 for a different group (eg ffdhe2048 ); Add your own PEM encoded DH params file via a volume to /tmp/docker-mailserver/dhparams.pem . This will replace DH params for both Dovecot and Postfix services during container startup.","title":"Custom DH Parameters"},{"location":"config/security/understanding-the-ports/","text":"Quick Reference Prefer Implicit TLS ports, they're more secure and if you use a Reverse Proxy, should be less hassle (although it's probably wiser to expose these ports directly to docker-mailserver ). Overview of Email Ports Protocol Explicit TLS 1 Implicit TLS Purpose SMTP 25 N/A Transfer 2 ESMTP 587 465 3 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, docker-mailserver additionally filters for spam and viruses. For submitting email to the server to be sent to third-parties, you should prefer the submission ports(465, 587) - which require authentication. Unless a relay host is configured(eg SendGrid), 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 ). Previously a secure variant of port 25. What Ports Should I Use? (SMTP) flowchart LR subgraph your-server [\"Your Server\"] in_25(25) --> server in_465(465) --> server server((\"docker-mailserver
hello@world.com\")) server --- out_25(25) server --- out_465(465) end third-party(\"Third-party
(sending you email)\") ---|\"Receive email for
hello@world.com\"| in_25 subgraph clients [\"Clients (MUA)\"] mua-client(Thunderbird,
Webmail,
Mutt,
etc) mua-service(Backend software
on another server) end clients ---|\"Send email as
hello@world.com\"| in_465 out_25(25) -->|\"Direct
Delivery\"| tin_25 out_465(465) --> relay(\"MTA
Relay Server\") --> tin_25(25) subgraph third-party-server[\"Third-party Server\"] third-party-mta(\"MTA
friend@example.com\") tin_25(25) --> third-party-mta end Inbound Traffic (On the left) Port 25: Think of this like a physical mailbox, it is open to receive email from anyone who wants to. docker-mailserver will actively filter email delivered on this port for spam or viruses and refuse mail from known bad sources. While you could also use this port internally to send email outbound without requiring authentication, you really should prefer the Submission ports(587, 465). Port 465( and 587 ): This is the equivalent of a post office box where you would send email to be delivered on your behalf( docker-mailserver is that metaphorical post office, aka the MTA). Unlike port 25, these two ports are known as the Submission ports and require a valid email account on the server with a password to be able to send email to anyone outside of the server(an MTA you do not control, eg Outlook or Gmail). Prefer port 465 which provides Implicit TLS. Outbound Traffic (On the Right) Port 25: Send the email directly to the given email address MTA as possible. Like your own docker-mailserver port 25, this is the standard port for receiving email on, thus email will almost always arrive to the final MTA on this port. Note that, there may be additional MTAs further in the chain, but this would be the public facing one representing that email address. Port 465( and 587 ): SMTP Relays are a popular choice to hand-off delivery of email through. Services like SendGrid are useful for bulk email(marketing) or when your webhost or ISP are preventing you from using standard ports like port 25 to send out email(which can be abused by spammers). docker-mailserver can serve as a relay too, but the difference between a DIY relay and a professional service is reputation, which is referenced by MTAs you're delivering to such as Outlook, Gmail or others(perhaps another docker-mailserver server!), when deciding if email should be marked as junked or potentially not delivered at all. As a service like SendGrid has a reputation to maintain, relay is restricted to registered users who must authenticate(even on port 25), they do not store email, merely forward it to another MTA which could be delivered on a different port like 25. Explicit vs Implicit TLS Explicit TLS (aka Opportunistic TLS) - Opt-in Encryption Communication on these ports begin in cleartext , indicating support for STARTTLS . If both client and server support STARTTLS the connection will be secured over TLS, otherwise the connection is unable to use encryption to secure it. By default, docker-mailserver is configured to reject connections that fail to establish a secure connection when authentication is required, rather than allow an insecure connection ( Port 25 will allow receiving unencrypted deliveries which doesn't require authentication ). Support for STARTTLS is not always implemented correctly, which can lead to leaking credentials(client sending too early) prior to a TLS connection being established. Third-parties such as some ISPs have also been known to intercept the STARTTLS exchange, modifying network traffic to prevent establishing a secure connection. Due to these security concerns, RFC 8314 (Section 4.1) encourages you to prefer Implicit TLS ports where possible . Implicit TLS - Enforced Encryption Communication is always encrypted, avoiding the above mentioned issues with Explicit TLS. You may know of these ports as SMTPS, POP3S, IMAPS , which indicate the protocol in combination with a TLS connection. However, Explicit TLS ports provide the same benefit when STARTTLS is successfully negotiated; Implicit TLS better communicates the improved security to all three protocols (SMTP/POP3/IMAP over Implicit TLS). Additionally, referring to port 465 as SMTPS would be incorrect, as it is a submissions port requiring authentication to proceed via ESMTP , whereas ESMTPS has a different meaning(STARTTLS supported). Port 25 may lack Implicit TLS, but can be configured to be more secure between trusted parties via MTA-STS, STARTTLS Policy List, DNSSEC and DANE. Security Todo This section should provide any related configuration advice, and probably expand on and link to resources about DANE, DNSSEC, MTA-STS and STARTTLS Policy list, with advice on how to configure/setup these added security layers. Todo A related section or page on ciphers used may be useful, although less important for users to be concerned about. TLS connections for a Mail-Server, compared to web browsers Unlike with HTTP where a web browser client communicates directly with the server providing a website, a secure TLS connection as discussed below is not the equivalent safety that HTTPS provides when the transit of email (receiving or sending) is sent through third-parties, as the secure connection is only between two machines, any additional machines (MTAs) between the MUA and the MDA depends on them establishing secure connections between one another successfully. Other machines that facilitate a connection that generally aren't taken into account can exist between a client and server, such as those where your connection passes through your ISP provider are capable of compromising a cleartext connection through interception.","title":"Understanding the Ports"},{"location":"config/security/understanding-the-ports/#quick-reference","text":"Prefer Implicit TLS ports, they're more secure and if you use a Reverse Proxy, should be less hassle (although it's probably wiser to expose these ports directly to docker-mailserver ).","title":"Quick Reference"},{"location":"config/security/understanding-the-ports/#overview-of-email-ports","text":"Protocol Explicit TLS 1 Implicit TLS Purpose SMTP 25 N/A Transfer 2 ESMTP 587 465 3 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, docker-mailserver additionally filters for spam and viruses. For submitting email to the server to be sent to third-parties, you should prefer the submission ports(465, 587) - which require authentication. Unless a relay host is configured(eg SendGrid), 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 ). Previously a secure variant of port 25.","title":"Overview of Email Ports"},{"location":"config/security/understanding-the-ports/#what-ports-should-i-use-smtp","text":"flowchart LR subgraph your-server [\"Your Server\"] in_25(25) --> server in_465(465) --> server server((\"docker-mailserver
hello@world.com\")) server --- out_25(25) server --- out_465(465) end third-party(\"Third-party
(sending you email)\") ---|\"Receive email for
hello@world.com\"| in_25 subgraph clients [\"Clients (MUA)\"] mua-client(Thunderbird,
Webmail,
Mutt,
etc) mua-service(Backend software
on another server) end clients ---|\"Send email as
hello@world.com\"| in_465 out_25(25) -->|\"Direct
Delivery\"| tin_25 out_465(465) --> relay(\"MTA
Relay Server\") --> tin_25(25) subgraph third-party-server[\"Third-party Server\"] third-party-mta(\"MTA
friend@example.com\") tin_25(25) --> third-party-mta end","title":"What Ports Should I Use? (SMTP)"},{"location":"config/security/understanding-the-ports/#inbound-traffic-on-the-left","text":"Port 25: Think of this like a physical mailbox, it is open to receive email from anyone who wants to. docker-mailserver will actively filter email delivered on this port for spam or viruses and refuse mail from known bad sources. While you could also use this port internally to send email outbound without requiring authentication, you really should prefer the Submission ports(587, 465). Port 465( and 587 ): This is the equivalent of a post office box where you would send email to be delivered on your behalf( docker-mailserver is that metaphorical post office, aka the MTA). Unlike port 25, these two ports are known as the Submission ports and require a valid email account on the server with a password to be able to send email to anyone outside of the server(an MTA you do not control, eg Outlook or Gmail). Prefer port 465 which provides Implicit TLS.","title":"Inbound Traffic (On the left)"},{"location":"config/security/understanding-the-ports/#outbound-traffic-on-the-right","text":"Port 25: Send the email directly to the given email address MTA as possible. Like your own docker-mailserver port 25, this is the standard port for receiving email on, thus email will almost always arrive to the final MTA on this port. Note that, there may be additional MTAs further in the chain, but this would be the public facing one representing that email address. Port 465( and 587 ): SMTP Relays are a popular choice to hand-off delivery of email through. Services like SendGrid are useful for bulk email(marketing) or when your webhost or ISP are preventing you from using standard ports like port 25 to send out email(which can be abused by spammers). docker-mailserver can serve as a relay too, but the difference between a DIY relay and a professional service is reputation, which is referenced by MTAs you're delivering to such as Outlook, Gmail or others(perhaps another docker-mailserver server!), when deciding if email should be marked as junked or potentially not delivered at all. As a service like SendGrid has a reputation to maintain, relay is restricted to registered users who must authenticate(even on port 25), they do not store email, merely forward it to another MTA which could be delivered on a different port like 25.","title":"Outbound Traffic (On the Right)"},{"location":"config/security/understanding-the-ports/#explicit-vs-implicit-tls","text":"","title":"Explicit vs Implicit TLS"},{"location":"config/security/understanding-the-ports/#explicit-tls-aka-opportunistic-tls-opt-in-encryption","text":"Communication on these ports begin in cleartext , indicating support for STARTTLS . If both client and server support STARTTLS the connection will be secured over TLS, otherwise the connection is unable to use encryption to secure it. By default, docker-mailserver is configured to reject connections that fail to establish a secure connection when authentication is required, rather than allow an insecure connection ( Port 25 will allow receiving unencrypted deliveries which doesn't require authentication ). Support for STARTTLS is not always implemented correctly, which can lead to leaking credentials(client sending too early) prior to a TLS connection being established. Third-parties such as some ISPs have also been known to intercept the STARTTLS exchange, modifying network traffic to prevent establishing a secure connection. Due to these security concerns, RFC 8314 (Section 4.1) encourages you to prefer Implicit TLS ports where possible .","title":"Explicit TLS (aka Opportunistic TLS) - Opt-in Encryption"},{"location":"config/security/understanding-the-ports/#implicit-tls-enforced-encryption","text":"Communication is always encrypted, avoiding the above mentioned issues with Explicit TLS. You may know of these ports as SMTPS, POP3S, IMAPS , which indicate the protocol in combination with a TLS connection. However, Explicit TLS ports provide the same benefit when STARTTLS is successfully negotiated; Implicit TLS better communicates the improved security to all three protocols (SMTP/POP3/IMAP over Implicit TLS). Additionally, referring to port 465 as SMTPS would be incorrect, as it is a submissions port requiring authentication to proceed via ESMTP , whereas ESMTPS has a different meaning(STARTTLS supported). Port 25 may lack Implicit TLS, but can be configured to be more secure between trusted parties via MTA-STS, STARTTLS Policy List, DNSSEC and DANE.","title":"Implicit TLS - Enforced Encryption"},{"location":"config/security/understanding-the-ports/#security","text":"Todo This section should provide any related configuration advice, and probably expand on and link to resources about DANE, DNSSEC, MTA-STS and STARTTLS Policy list, with advice on how to configure/setup these added security layers. Todo A related section or page on ciphers used may be useful, although less important for users to be concerned about.","title":"Security"},{"location":"config/security/understanding-the-ports/#tls-connections-for-a-mail-server-compared-to-web-browsers","text":"Unlike with HTTP where a web browser client communicates directly with the server providing a website, a secure TLS connection as discussed below is not the equivalent safety that HTTPS provides when the transit of email (receiving or sending) is sent through third-parties, as the secure connection is only between two machines, any additional machines (MTAs) between the MUA and the MDA depends on them establishing secure connections between one another successfully. Other machines that facilitate a connection that generally aren't taken into account can exist between a client and server, such as those where your connection passes through your ISP provider are capable of compromising a cleartext connection through interception.","title":"TLS connections for a Mail-Server, compared to web browsers"},{"location":"config/troubleshooting/debugging/","text":"Contributions Welcome! Please contribute your solutions to help the community Enable Verbose Debugging Output You may find it useful to set LOG_LEVEL environment variable. Invalid Username or Password Shell into the container: docker exec -it bash Check log files in /var/log/mail could not find any mention of incorrect logins here neither in the dovecot logs. Check the supervisors logs in /var/log/supervisor . You can find the logs for startup of fetchmail, postfix and others here - they might indicate problems during startup. Make sure you set your hostname to mail or whatever you specified in your docker-compose.yml file or else your FQDN will be wrong. Installation Errors During setup, if you get errors trying to edit files inside of the container, you likely need to install vi : sudo su docker exec -it apt-get install -y vim Testing Connection I spent HOURS trying to debug \"Connection Refused\" and \"Connection closed by foreign host\" errors when trying to use telnet to troubleshoot my connection. I was also trying to connect from my email client (macOS mail) around the same time. Telnet had also worked earlier, so I was extremely confused as to why it suddenly stopped working. I stumbled upon fail2ban.log in my container. In short, when trying to get my macOS client working, I exceeded the number of failed login attempts and fail2ban put dovecot and postfix in jail! I got around it by whitelisting my ipaddresses (my ec2 instance and my local computer) sudo su docker exec -it mailserver bash cd /var/log cat fail2ban.log | grep dovecot # Whitelist IP addresses: fail2ban-client set dovecot addignoreip # Server fail2ban-client set postfix addignoreip fail2ban-client set dovecot addignoreip # Client fail2ban-client set postfix addignoreip # This will delete the jails entirely - nuclear option fail2ban-client stop dovecot fail2ban-client stop postfix Sent email is never received Some hosting provides have a stealth block on port 25. Make sure to check with your hosting provider that traffic on port 25 is allowed Common hosting providers known to have this issue: Azure AWS EC2","title":"Debugging"},{"location":"config/troubleshooting/debugging/#enable-verbose-debugging-output","text":"You may find it useful to set LOG_LEVEL environment variable.","title":"Enable Verbose Debugging Output"},{"location":"config/troubleshooting/debugging/#invalid-username-or-password","text":"Shell into the container: docker exec -it bash Check log files in /var/log/mail could not find any mention of incorrect logins here neither in the dovecot logs. Check the supervisors logs in /var/log/supervisor . You can find the logs for startup of fetchmail, postfix and others here - they might indicate problems during startup. Make sure you set your hostname to mail or whatever you specified in your docker-compose.yml file or else your FQDN will be wrong.","title":"Invalid Username or Password"},{"location":"config/troubleshooting/debugging/#installation-errors","text":"During setup, if you get errors trying to edit files inside of the container, you likely need to install vi : sudo su docker exec -it apt-get install -y vim","title":"Installation Errors"},{"location":"config/troubleshooting/debugging/#testing-connection","text":"I spent HOURS trying to debug \"Connection Refused\" and \"Connection closed by foreign host\" errors when trying to use telnet to troubleshoot my connection. I was also trying to connect from my email client (macOS mail) around the same time. Telnet had also worked earlier, so I was extremely confused as to why it suddenly stopped working. I stumbled upon fail2ban.log in my container. In short, when trying to get my macOS client working, I exceeded the number of failed login attempts and fail2ban put dovecot and postfix in jail! I got around it by whitelisting my ipaddresses (my ec2 instance and my local computer) sudo su docker exec -it mailserver bash cd /var/log cat fail2ban.log | grep dovecot # Whitelist IP addresses: fail2ban-client set dovecot addignoreip # Server fail2ban-client set postfix addignoreip fail2ban-client set dovecot addignoreip # Client fail2ban-client set postfix addignoreip # This will delete the jails entirely - nuclear option fail2ban-client stop dovecot fail2ban-client stop postfix","title":"Testing Connection"},{"location":"config/troubleshooting/debugging/#sent-email-is-never-received","text":"Some hosting provides have a stealth block on port 25. Make sure to check with your hosting provider that traffic on port 25 is allowed Common hosting providers known to have this issue: Azure AWS EC2","title":"Sent email is never received"},{"location":"config/user-management/accounts/","text":"Adding a New Account Users (email accounts) are managed in /tmp/docker-mailserver/postfix-accounts.cf . The best way to manage accounts is to use the reliable setup.sh script . Or you may directly add the full email address and its encrypted password, separated by a pipe: user1@example.com|{SHA512-CRYPT}$6$2YpW1nYtPBs2yLYS$z.5PGH1OEzsHHNhl3gJrc3D.YMZkvKw/vp.r5WIiwya6z7P/CQ9GDEJDr2G2V0cAfjDFeAQPUoopsuWPXLk3u1 user2@not-example.com|{SHA512-CRYPT}$6$2YpW1nYtPBs2yLYS$z.5PGH1OEzsHHNhl3gJrc3D.YMZkvKw/vp.r5WIiwya6z7P/CQ9GDEJDr2G2V0cAfjDFeAQPUoopsuWPXLk3u1 In the example above, we've added 2 mail accounts for 2 different domains. Consequently, the mail-server will automatically be configured for multi-domains. Therefore, to generate a new mail account data, directly from your docker host, you could for example run the following: docker run --rm \\ -e MAIL_USER = user1@example.com \\ -e MAIL_PASS = mypassword \\ -it mailserver/docker-mailserver:latest \\ /bin/sh -c 'echo \"$MAIL_USER|$(doveadm pw -s SHA512-CRYPT -u $MAIL_USER -p $MAIL_PASS)\"' >> docker-data/dms/config/postfix-accounts.cf You will then be asked for a password, and be given back the data for a new account entry, as text. To actually add this new account, just copy all the output text in docker-data/dms/config/postfix-accounts.cf file of your running container. Note doveadm pw command lets you choose between several encryption schemes for the password. Use doveadm pw -l to get a list of the currently supported encryption schemes. Note Changes to the accounts list require a restart of the container, using supervisord . See #552 . Notes imap-quota is enabled and allow clients to query their mailbox usage. When the mailbox is deleted, the quota directive is deleted as well. Dovecot quotas support LDAP, but it's not implemented ( PRs are welcome! ).","title":"Accounts"},{"location":"config/user-management/accounts/#adding-a-new-account","text":"Users (email accounts) are managed in /tmp/docker-mailserver/postfix-accounts.cf . The best way to manage accounts is to use the reliable setup.sh script . Or you may directly add the full email address and its encrypted password, separated by a pipe: user1@example.com|{SHA512-CRYPT}$6$2YpW1nYtPBs2yLYS$z.5PGH1OEzsHHNhl3gJrc3D.YMZkvKw/vp.r5WIiwya6z7P/CQ9GDEJDr2G2V0cAfjDFeAQPUoopsuWPXLk3u1 user2@not-example.com|{SHA512-CRYPT}$6$2YpW1nYtPBs2yLYS$z.5PGH1OEzsHHNhl3gJrc3D.YMZkvKw/vp.r5WIiwya6z7P/CQ9GDEJDr2G2V0cAfjDFeAQPUoopsuWPXLk3u1 In the example above, we've added 2 mail accounts for 2 different domains. Consequently, the mail-server will automatically be configured for multi-domains. Therefore, to generate a new mail account data, directly from your docker host, you could for example run the following: docker run --rm \\ -e MAIL_USER = user1@example.com \\ -e MAIL_PASS = mypassword \\ -it mailserver/docker-mailserver:latest \\ /bin/sh -c 'echo \"$MAIL_USER|$(doveadm pw -s SHA512-CRYPT -u $MAIL_USER -p $MAIL_PASS)\"' >> docker-data/dms/config/postfix-accounts.cf You will then be asked for a password, and be given back the data for a new account entry, as text. To actually add this new account, just copy all the output text in docker-data/dms/config/postfix-accounts.cf file of your running container. Note doveadm pw command lets you choose between several encryption schemes for the password. Use doveadm pw -l to get a list of the currently supported encryption schemes. Note Changes to the accounts list require a restart of the container, using supervisord . See #552 .","title":"Adding a New Account"},{"location":"config/user-management/accounts/#notes","text":"imap-quota is enabled and allow clients to query their mailbox usage. When the mailbox is deleted, the quota directive is deleted as well. Dovecot quotas support LDAP, but it's not implemented ( PRs are welcome! ).","title":"Notes"},{"location":"config/user-management/aliases/","text":"Please read the Postfix documentation on virtual aliases first. You can use setup.sh instead of creating and editing files manually. Aliases are managed in /tmp/docker-mailserver/postfix-virtual.cf . An alias is a full email address that will either be: delivered to an existing account registered in /tmp/docker-mailserver/postfix-accounts.cf redirected to one or more other email addresses Alias and target are space separated. An example on a server with example.com as its domain: # Alias delivered to an existing account alias1@example.com user1@example.com # Alias forwarded to an external email address alias2@example.com external-account@gmail.com Configuring RegExp Aliases Additional regexp aliases can be configured by placing them into docker-data/dms/config/postfix-regexp.cf . The regexp aliases get evaluated after the virtual aliases (container path: /tmp/docker-mailserver/postfix-virtual.cf ). For example, the following docker-data/dms/config/postfix-regexp.cf causes all email sent to \"test\" users to be delivered to qa@example.com instead: /^test[0-9][0-9]*@example.com/ qa@example.com Address Tags (Extension Delimiters) an Alternative to Aliases Postfix supports so-called address tags, in the form of plus (+) tags - i.e. address+tag@example.com will end up at address@example.com . This is configured by default and the (configurable !) separator is set to + . For more info, see the official documentation . Note If you do decide to change the configurable separator, you must add the same line to both docker-data/dms/config/postfix-main.cf and docker-data/dms/config/dovecot.cf , because Dovecot is acting as the delivery agent. For example, to switch to - , add: recipient_delimiter = -","title":"Aliases"},{"location":"config/user-management/aliases/#configuring-regexp-aliases","text":"Additional regexp aliases can be configured by placing them into docker-data/dms/config/postfix-regexp.cf . The regexp aliases get evaluated after the virtual aliases (container path: /tmp/docker-mailserver/postfix-virtual.cf ). For example, the following docker-data/dms/config/postfix-regexp.cf causes all email sent to \"test\" users to be delivered to qa@example.com instead: /^test[0-9][0-9]*@example.com/ qa@example.com","title":"Configuring RegExp Aliases"},{"location":"config/user-management/aliases/#address-tags-extension-delimiters-an-alternative-to-aliases","text":"Postfix supports so-called address tags, in the form of plus (+) tags - i.e. address+tag@example.com will end up at address@example.com . This is configured by default and the (configurable !) separator is set to + . For more info, see the official documentation . Note If you do decide to change the configurable separator, you must add the same line to both docker-data/dms/config/postfix-main.cf and docker-data/dms/config/dovecot.cf , because Dovecot is acting as the delivery agent. For example, to switch to - , add: recipient_delimiter = -","title":"Address Tags (Extension Delimiters) an Alternative to Aliases"},{"location":"contributing/general/","text":"Coding Style When refactoring, writing or altering scripts or other files, adhere to these rules: Adjust your style of coding to the style that is already present ! Even if you do not like it, this is due to consistency. There was a lot of work involved in making all scripts consistent. Use shellcheck to check your scripts ! Your contributions are checked by GitHub Actions too, so you will need to do this. You can lint your work with make lint to check against all targets. Use the provided .editorconfig file. Use /bin/bash instead of /bin/sh in scripts Documentation You will need to have Docker installed. Navigate into the docs/ directory. Then run: docker run --rm -it -p 8000 :8000 -v \" ${ PWD } :/docs\" squidfunk/mkdocs-material This serves the documentation on your local machine on port 8000 . Each change will be hot-reloaded onto the page you view, just edit, save and look at the result.","title":"General Information"},{"location":"contributing/general/#coding-style","text":"When refactoring, writing or altering scripts or other files, adhere to these rules: Adjust your style of coding to the style that is already present ! Even if you do not like it, this is due to consistency. There was a lot of work involved in making all scripts consistent. Use shellcheck to check your scripts ! Your contributions are checked by GitHub Actions too, so you will need to do this. You can lint your work with make lint to check against all targets. Use the provided .editorconfig file. Use /bin/bash instead of /bin/sh in scripts","title":"Coding Style"},{"location":"contributing/general/#documentation","text":"You will need to have Docker installed. Navigate into the docs/ directory. Then run: docker run --rm -it -p 8000 :8000 -v \" ${ PWD } :/docs\" squidfunk/mkdocs-material This serves the documentation on your local machine on port 8000 . Each change will be hot-reloaded onto the page you view, just edit, save and look at the result.","title":"Documentation"},{"location":"contributing/issues-and-pull-requests/","text":"This project is Open Source. That means that you can contribute on enhancements, bug fixing or improving the documentation. Opening an Issue Attention Before opening an issue , read the README carefully, study the documentation , the Postfix/Dovecot documentation and your search engine you trust. The issue tracker is not meant to be used for unrelated questions! When opening an issue, please provide details use case to let the community reproduce your problem. Please start docker-mailserver with the environment variable LOG_LEVEL set to debug or trace and paste the output into the issue. Attention Use the issue templates to provide the necessary information. Issues which do not use these templates are not worked on and closed. By raising issues, I agree to these terms and I understand, that the rules set for the issue tracker will help both maintainers as well as everyone to find a solution. Maintainers take the time to improve on this project and help by solving issues together. It is therefore expected from others to make an effort and comply with the rules . Pull Requests Motivation You want to add a feature? Feel free to start creating an issue explaining what you want to do and how you're thinking doing it. Other users may have the same need and collaboration may lead to better results. Submit a Pull-Request The development workflow is the following: Fork the project and clone your fork with git clone --recurse-submodules ... or run git submodule update --init --recursive after you cloned your fork Write the code that is needed :D Add integration tests if necessary Prepare your environment and run linting and tests Document your improvements if necessary (e.g. if you introduced new environment variables, describe those in the ENV documentation ) and add your changes the changelog under the \"Unreleased\" section Commit (and sign your commit ), push and create a pull-request to merge into master . Please use the pull-request template to provide a minimum of contextual information and make sure to meet the requirements of the checklist. Pull requests are automatically tested against the CI and will be reviewed when tests pass. When your changes are validated, your branch is merged. CI builds the new :edge image immediately and your changes will be includes in the next version release.","title":"Issues and Pull Requests"},{"location":"contributing/issues-and-pull-requests/#opening-an-issue","text":"Attention Before opening an issue , read the README carefully, study the documentation , the Postfix/Dovecot documentation and your search engine you trust. The issue tracker is not meant to be used for unrelated questions! When opening an issue, please provide details use case to let the community reproduce your problem. Please start docker-mailserver with the environment variable LOG_LEVEL set to debug or trace and paste the output into the issue. Attention Use the issue templates to provide the necessary information. Issues which do not use these templates are not worked on and closed. By raising issues, I agree to these terms and I understand, that the rules set for the issue tracker will help both maintainers as well as everyone to find a solution. Maintainers take the time to improve on this project and help by solving issues together. It is therefore expected from others to make an effort and comply with the rules .","title":"Opening an Issue"},{"location":"contributing/issues-and-pull-requests/#pull-requests","text":"Motivation You want to add a feature? Feel free to start creating an issue explaining what you want to do and how you're thinking doing it. Other users may have the same need and collaboration may lead to better results.","title":"Pull Requests"},{"location":"contributing/issues-and-pull-requests/#submit-a-pull-request","text":"The development workflow is the following: Fork the project and clone your fork with git clone --recurse-submodules ... or run git submodule update --init --recursive after you cloned your fork Write the code that is needed :D Add integration tests if necessary Prepare your environment and run linting and tests Document your improvements if necessary (e.g. if you introduced new environment variables, describe those in the ENV documentation ) and add your changes the changelog under the \"Unreleased\" section Commit (and sign your commit ), push and create a pull-request to merge into master . Please use the pull-request template to provide a minimum of contextual information and make sure to meet the requirements of the checklist. Pull requests are automatically tested against the CI and will be reviewed when tests pass. When your changes are validated, your branch is merged. CI builds the new :edge image immediately and your changes will be includes in the next version release.","title":"Submit a Pull-Request"},{"location":"contributing/tests/","text":"Program testing can be used to show the presence of bugs, but never to show their absence! \u2013 Edsger Wybe Dijkstra Introduction DMS employs a variety of unit and integration tests. All tests and associated configuration is stored in the test/ directory. If you want to change existing functionality or integrate a new feature into DMS, you will probably need to work with our test suite. Can I use macOS? We do not support running linting, tests, etc. on macOS at this time. Please use a Linux VM, Debian/Ubuntu is recommended. About We use BATS ( Bash Automated Testing System ) and additional support libraries. BATS is very similar to Bash, and one can easily and quickly get an understanding of how tests in a single file are run. A test file template provides a minimal working example for newcomers to look at. Structure The test/ directory contains multiple directories. Among them is the bats/ directory ( which is the BATS git submodule ) and the helper/ directory. The latter is especially interesting because it contains common support functionality used in almost every test. Actual tests are located in test/tests/ . Test suite undergoing refactoring We are currently in the process of restructuring all of our tests. Tests will be moved into test/tests/parallel/ and new tests should be placed there as well. Using Our Helper Functions There are many functions that aid in writing tests. We urge you to use them! They will not only ease writing a test but they will also do their best to ensure there are no race conditions or other unwanted side effects. To learn about the functions we provide, you can: look into existing tests for helper functions we already used look into the test/helper/ directory which contains all files that can (and will) be loaded in test files We encourage you to try both of the approaches mentioned above. To make understanding and using the helper functions easy, every function contains detailed documentation comments. Read them carefully! How Are Tests Run? Tests are split into two categories: test/tests/parallel/ : Multiple test files are run concurrently to reduce the required time to complete the test suite. A test file will presently run it's own defined test-cases in a sequential order. test/tests/serial/ : Each test file is queued up to run sequentially. Tests that are unable to support running concurrently belong here. Parallel tests are further partitioned into smaller sets. If your system has the resources to support running more than one of those sets at a time this is perfectly ok ( our CI runs tests by distributing the sets across multiple test runners ). Care must be taken not to mix running the serial tests while a parallel set is also running; this is handled for you when using make tests . Running Tests Prerequisites To run the test suite, you will need to: Install Docker Install jq and (GNU) parallel (under Ubuntu, use sudo apt-get -y install jq parallel ) Execute git submodule update --init --recursive if you haven't already initialized the git submodules Executing Test(s) We use make to run commands. You will first need to build the container image via make build . You can then: Run all tests: make clean tests Run a single test: make clean generate-accounts test/ Run multiple unrelated tests: make clean generate-accounts test/, (just add a , and then immediately write the new test name) Run a whole set or all serial tests: make clean generate-accounts tests/parallel/setX where X is the number of the set or make clean generate-accounts tests/serial Setting the Degree of Parallelization for Tests If your machine is capable, you can increase the amount of tests that are run simultaneously by prepending the make clean all command with BATS_PARALLEL_JOBS=X (i.e. BATS_PARALLEL_JOBS=X make clean all ). This wil speed up the test procedure. You can also run all tests in serial by setting BATS_PARALLEL_JOBS=1 this way. The default value of BATS_PARALLEL_JOBS is 2. This can be increased if your system has the resources spare to support running more jobs at once to complete the test suite sooner. Test Output when Running in Parallel When running tests in parallel ( with make clean generate-accounts tests/parallel/setX ), BATS will delay outputting the results until completing all test cases within a file. This likewise delays the reporting of test-case failures. When troubleshooting parallel set tests, you may prefer to run specific tests you're working on serially ( as demonstrated in the example below ). When writing tests, ensure that parallel set tests still pass when run in parallel. You need to account for other tests running in parallel that may interfere with your own tests logic. An Example In this example, you've made a change to the Rspamd feature support ( or adjusted it's tests ). First verify no regressions have been introduced by running it's specific test file: $ make clean generate-accounts test/rspamd rspamd.bats \u2713 [Rspamd] Postfix's main.cf was adjusted [12] \u2713 [Rspamd] normal mail passes fine [44] \u2713 [Rspamd] detects and rejects spam [122] \u2713 [Rspamd] detects and rejects virus [189] As your feature work progresses your change for Rspamd also affects ClamAV. As your change now spans more than just the Rspamd test file, you could run multiple test files serially: $ make clean generate-accounts test/rspamd,clamav rspamd.bats \u2713 [Rspamd] Postfix's main.cf was adjusted [12] \u2713 [Rspamd] normal mail passes fine [44] \u2713 [Rspamd] detects and rejects spam [122] \u2713 [Rspamd] detects and rejects virus [189] clamav.bats \u2713 [ClamAV] log files exist at /var/log/mail directory [68] \u2713 [ClamAV] should be identified by Amavis [67] \u2713 [ClamAV] freshclam cron is enabled [76] \u2713 [ClamAV] env CLAMAV_MESSAGE_SIZE_LIMIT is set correctly [63] \u2713 [ClamAV] rejects virus [60] You're almost finished with your change before submitting it as a PR. It's a good idea to run the full parallel set those individual tests belong to ( especially if you've modified any tests ): $ make clean generate-accounts tests/parallel/set1 default_relay_host.bats \u2713 [Relay] (ENV) 'DEFAULT_RELAY_HOST' should configure 'main.cf:relayhost' [88] spam_virus/amavis.bats \u2713 [Amavis] SpamAssassin integration should be active [1165] spam_virus/clamav.bats \u2713 [ClamAV] log files exist at /var/log/mail directory [73] \u2713 [ClamAV] should be identified by Amavis [67] \u2713 [ClamAV] freshclam cron is enabled [76] ... Even better, before opening a PR run the full test suite: $ make clean tests ...","title":"Tests"},{"location":"contributing/tests/#introduction","text":"DMS employs a variety of unit and integration tests. All tests and associated configuration is stored in the test/ directory. If you want to change existing functionality or integrate a new feature into DMS, you will probably need to work with our test suite. Can I use macOS? We do not support running linting, tests, etc. on macOS at this time. Please use a Linux VM, Debian/Ubuntu is recommended.","title":"Introduction"},{"location":"contributing/tests/#about","text":"We use BATS ( Bash Automated Testing System ) and additional support libraries. BATS is very similar to Bash, and one can easily and quickly get an understanding of how tests in a single file are run. A test file template provides a minimal working example for newcomers to look at.","title":"About"},{"location":"contributing/tests/#structure","text":"The test/ directory contains multiple directories. Among them is the bats/ directory ( which is the BATS git submodule ) and the helper/ directory. The latter is especially interesting because it contains common support functionality used in almost every test. Actual tests are located in test/tests/ . Test suite undergoing refactoring We are currently in the process of restructuring all of our tests. Tests will be moved into test/tests/parallel/ and new tests should be placed there as well.","title":"Structure"},{"location":"contributing/tests/#using-our-helper-functions","text":"There are many functions that aid in writing tests. We urge you to use them! They will not only ease writing a test but they will also do their best to ensure there are no race conditions or other unwanted side effects. To learn about the functions we provide, you can: look into existing tests for helper functions we already used look into the test/helper/ directory which contains all files that can (and will) be loaded in test files We encourage you to try both of the approaches mentioned above. To make understanding and using the helper functions easy, every function contains detailed documentation comments. Read them carefully!","title":"Using Our Helper Functions"},{"location":"contributing/tests/#how-are-tests-run","text":"Tests are split into two categories: test/tests/parallel/ : Multiple test files are run concurrently to reduce the required time to complete the test suite. A test file will presently run it's own defined test-cases in a sequential order. test/tests/serial/ : Each test file is queued up to run sequentially. Tests that are unable to support running concurrently belong here. Parallel tests are further partitioned into smaller sets. If your system has the resources to support running more than one of those sets at a time this is perfectly ok ( our CI runs tests by distributing the sets across multiple test runners ). Care must be taken not to mix running the serial tests while a parallel set is also running; this is handled for you when using make tests .","title":"How Are Tests Run?"},{"location":"contributing/tests/#running-tests","text":"","title":"Running Tests"},{"location":"contributing/tests/#prerequisites","text":"To run the test suite, you will need to: Install Docker Install jq and (GNU) parallel (under Ubuntu, use sudo apt-get -y install jq parallel ) Execute git submodule update --init --recursive if you haven't already initialized the git submodules","title":"Prerequisites"},{"location":"contributing/tests/#executing-tests","text":"We use make to run commands. You will first need to build the container image via make build . You can then: Run all tests: make clean tests Run a single test: make clean generate-accounts test/ Run multiple unrelated tests: make clean generate-accounts test/, (just add a , and then immediately write the new test name) Run a whole set or all serial tests: make clean generate-accounts tests/parallel/setX where X is the number of the set or make clean generate-accounts tests/serial Setting the Degree of Parallelization for Tests If your machine is capable, you can increase the amount of tests that are run simultaneously by prepending the make clean all command with BATS_PARALLEL_JOBS=X (i.e. BATS_PARALLEL_JOBS=X make clean all ). This wil speed up the test procedure. You can also run all tests in serial by setting BATS_PARALLEL_JOBS=1 this way. The default value of BATS_PARALLEL_JOBS is 2. This can be increased if your system has the resources spare to support running more jobs at once to complete the test suite sooner. Test Output when Running in Parallel When running tests in parallel ( with make clean generate-accounts tests/parallel/setX ), BATS will delay outputting the results until completing all test cases within a file. This likewise delays the reporting of test-case failures. When troubleshooting parallel set tests, you may prefer to run specific tests you're working on serially ( as demonstrated in the example below ). When writing tests, ensure that parallel set tests still pass when run in parallel. You need to account for other tests running in parallel that may interfere with your own tests logic.","title":"Executing Test(s)"},{"location":"contributing/tests/#an-example","text":"In this example, you've made a change to the Rspamd feature support ( or adjusted it's tests ). First verify no regressions have been introduced by running it's specific test file: $ make clean generate-accounts test/rspamd rspamd.bats \u2713 [Rspamd] Postfix's main.cf was adjusted [12] \u2713 [Rspamd] normal mail passes fine [44] \u2713 [Rspamd] detects and rejects spam [122] \u2713 [Rspamd] detects and rejects virus [189] As your feature work progresses your change for Rspamd also affects ClamAV. As your change now spans more than just the Rspamd test file, you could run multiple test files serially: $ make clean generate-accounts test/rspamd,clamav rspamd.bats \u2713 [Rspamd] Postfix's main.cf was adjusted [12] \u2713 [Rspamd] normal mail passes fine [44] \u2713 [Rspamd] detects and rejects spam [122] \u2713 [Rspamd] detects and rejects virus [189] clamav.bats \u2713 [ClamAV] log files exist at /var/log/mail directory [68] \u2713 [ClamAV] should be identified by Amavis [67] \u2713 [ClamAV] freshclam cron is enabled [76] \u2713 [ClamAV] env CLAMAV_MESSAGE_SIZE_LIMIT is set correctly [63] \u2713 [ClamAV] rejects virus [60] You're almost finished with your change before submitting it as a PR. It's a good idea to run the full parallel set those individual tests belong to ( especially if you've modified any tests ): $ make clean generate-accounts tests/parallel/set1 default_relay_host.bats \u2713 [Relay] (ENV) 'DEFAULT_RELAY_HOST' should configure 'main.cf:relayhost' [88] spam_virus/amavis.bats \u2713 [Amavis] SpamAssassin integration should be active [1165] spam_virus/clamav.bats \u2713 [ClamAV] log files exist at /var/log/mail directory [73] \u2713 [ClamAV] should be identified by Amavis [67] \u2713 [ClamAV] freshclam cron is enabled [76] ... Even better, before opening a PR run the full test suite: $ make clean tests ...","title":"An Example"},{"location":"examples/tutorials/basic-installation/","text":"A Basic Example 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 chapter . services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver # Provide the FQDN of your mail server here (Your DNS MX record should point to this value) hostname : mail.example.com ports : - \"25:25\" - \"587:587\" - \"993:993\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - /etc/localtime:/etc/localtime:ro environment : - ENABLE_SPAMASSASSIN=1 - SPAMASSASSIN_SPAM_TO_INBOX=1 - ENABLE_CLAMAV=1 - ENABLE_FAIL2BAN=1 - ENABLE_POSTGREY=1 cap_add : - NET_ADMIN # For Fail2Ban to work restart : always A Basic LDAP Setup Note There are currently no LDAP maintainers. If you encounter issues, please raise them in the issue tracker, but be aware that the core maintainers team will most likely not be able to help you. We would appreciate and we encourage everyone to actively participate in maintaining LDAP-related code by becoming a maintainer! services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver # Provide the FQDN of your mail server here (Your DNS MX record should point to this value) hostname : mail.example.com ports : - \"25:25\" - \"587:587\" - \"993:993\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - /etc/localtime:/etc/localtime:ro environment : - ENABLE_SPAMASSASSIN=1 - SPAMASSASSIN_SPAM_TO_INBOX=1 - ENABLE_CLAMAV=1 - ENABLE_FAIL2BAN=1 - ENABLE_POSTGREY=1 - ACCOUNT_PROVISIONER=LDAP - 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 cap_add : - NET_ADMIN # For Fail2Ban to work restart : always A Detailed Example Note This is a community contributed guide. Please let us know via a Github Issue if you're having any difficulty following the guide so that we can update it. This guide is focused on only using SMTP ports (not POP3 and IMAP) with the intent to send received mail to another MTA service such as Gmail . It is not intended to have a MUA client ( eg: Thunderbird ) to retrieve mail directly from docker-mailserver via POP3/IMAP. In this setup docker-mailserver is not intended to receive email externally, so no anti-spam or anti-virus software is needed, making the service lighter to run. Open Relays Adding the docker network's gateway to the list of trusted hosts ( eg: using the network or connected-networks option ), can create an open relay . For instance if IPv6 is enabled on the host machine, but not in Docker . If you're running a version of docker-mailserver earlier than v10.2, you'll need to get setup.sh . Otherwise you can substitute ./setup.sh with docker exec mailserver setup . Pull the docker image: docker pull docker.io/mailserver/docker-mailserver:latest . Create the file docker-compose.yml with a content like this: Example services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver # Provide the FQDN of your mail server here (Your DNS MX record should point to this value) hostname : mail.example.com ports : - \"25:25\" - \"587:587\" - \"465:465\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - /etc/localtime:/etc/localtime:ro environment : - ENABLE_FAIL2BAN=1 # Using letsencrypt for SSL/TLS certificates - SSL_TYPE=letsencrypt # Allow sending emails from other docker containers # Beware creating an Open Relay: https://docker-mailserver.github.io/docker-mailserver/edge/config/environment/#permit_docker - PERMIT_DOCKER=network # You may want to enable this: https://docker-mailserver.github.io/docker-mailserver/edge/config/environment/#spoof_protection # See step 8 below, which demonstrates setup with enabled/disabled SPOOF_PROTECTION: - SPOOF_PROTECTION=0 cap_add : - NET_ADMIN # For Fail2Ban to work restart : always The docs have a detailed page on Environment Variables for reference. Firewalled ports You may need to open ports 25 , 587 and 465 on the firewall. For example, with the firewall ufw , run: ufw allow 25 ufw allow 587 ufw allow 465 Configure your DNS service to use an MX record for the hostname (eg: mail ) you configured in the previous step and add the SPF TXT record. If you manually manage the DNS zone file for the domain, it would look something like this: mail IN A 10.11.12.13 ; mail-server for example.com 3600 IN MX 1 mail.example.com. ; Add SPF record IN TXT \"v=spf1 mx ~all\" Then don't forget to change the serial number and to restart the service. Generate DKIM keys for your domain via ./setup.sh config dkim . Copy the content of the file docker-data/dms/config/opendkim/keys/example.com/mail.txt and add it to your DNS records as a TXT like SPF was handled above. I use bind9 for managing my domains, so I just paste it on example.com.db : mail._domainkey IN TXT ( \"v=DKIM1; h=sha256; k=rsa; \" \"p=MIIBIjANBgkqhkiG9w0BAQEFACAQ8AMIIBCgKCAQEAaH5KuPYPSF3Ppkt466BDMAFGOA4mgqn4oPjZ5BbFlYA9l5jU3bgzRj3l6/Q1n5a9lQs5fNZ7A/HtY0aMvs3nGE4oi+LTejt1jblMhV/OfJyRCunQBIGp0s8G9kIUBzyKJpDayk2+KJSJt/lxL9Iiy0DE5hIv62ZPP6AaTdHBAsJosLFeAzuLFHQ6USyQRojefqFQtgYqWQ2JiZQ3\" \"iqq3bD/BVlwKRp5gH6TEYEmx8EBJUuDxrJhkWRUk2VDl1fqhVBy8A9O7Ah+85nMrlOHIFsTaYo9o6+cDJ6t1i6G1gu+bZD0d3/3bqGLPBQV9LyEL1Rona5V7TJBGg099NQkTz1IwIDAQAB\" ) ; ----- DKIM key mail for example.com Get an SSL certificate, we have a guide for you here ( Let's Encrypt is a popular service to get free SSL certificates). Start docker-mailserver and check the terminal output for any errors: docker-compose up . Create email accounts and aliases: With SPOOF_PROTECTION=0 ./setup.sh email add admin@example.com passwd123 ./setup.sh email add info@example.com passwd123 ./setup.sh alias add admin@example.com external-account@gmail.com ./setup.sh alias add info@example.com external-account@gmail.com ./setup.sh email list ./setup.sh alias list Aliases make sure that any email that comes to these accounts is forwarded to your third-party email address ( external-account@gmail.com ), where they are retrieved ( eg: via third-party web or mobile app ), instead of connecting directly to docker-mailserer with POP3 / IMAP. With SPOOF_PROTECTION=1 ./setup.sh email add admin.gmail@example.com passwd123 ./setup.sh email add info.gmail@example.com passwd123 ./setup.sh alias add admin@example.com admin.gmail@example.com ./setup.sh alias add info@example.com info.gmail@example.com ./setup.sh alias add admin.gmail@example.com external-account@gmail.com ./setup.sh alias add info.gmail@example.com external-account@gmail.com ./setup.sh email list ./setup.sh alias list This extra step is required to avoid the 553 5.7.1 Sender address rejected: not owned by user error ( the accounts used for submitting mail to Gmail are admin.gmail@example.com and info.gmail@example.com ) Send some test emails to these addresses and make other tests. Once everything is working well, stop the container with ctrl+c and start it again as a daemon: docker-compose up -d .","title":"Basic Installation"},{"location":"examples/tutorials/basic-installation/#a-basic-example-with-relevant-environmental-variables","text":"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 chapter . services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver # Provide the FQDN of your mail server here (Your DNS MX record should point to this value) hostname : mail.example.com ports : - \"25:25\" - \"587:587\" - \"993:993\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - /etc/localtime:/etc/localtime:ro environment : - ENABLE_SPAMASSASSIN=1 - SPAMASSASSIN_SPAM_TO_INBOX=1 - ENABLE_CLAMAV=1 - ENABLE_FAIL2BAN=1 - ENABLE_POSTGREY=1 cap_add : - NET_ADMIN # For Fail2Ban to work restart : always","title":"A Basic Example With Relevant Environmental Variables"},{"location":"examples/tutorials/basic-installation/#a-basic-ldap-setup","text":"Note There are currently no LDAP maintainers. If you encounter issues, please raise them in the issue tracker, but be aware that the core maintainers team will most likely not be able to help you. We would appreciate and we encourage everyone to actively participate in maintaining LDAP-related code by becoming a maintainer! services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver # Provide the FQDN of your mail server here (Your DNS MX record should point to this value) hostname : mail.example.com ports : - \"25:25\" - \"587:587\" - \"993:993\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - /etc/localtime:/etc/localtime:ro environment : - ENABLE_SPAMASSASSIN=1 - SPAMASSASSIN_SPAM_TO_INBOX=1 - ENABLE_CLAMAV=1 - ENABLE_FAIL2BAN=1 - ENABLE_POSTGREY=1 - ACCOUNT_PROVISIONER=LDAP - 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 cap_add : - NET_ADMIN # For Fail2Ban to work restart : always","title":"A Basic LDAP Setup"},{"location":"examples/tutorials/basic-installation/#a-detailed-example","text":"Note This is a community contributed guide. Please let us know via a Github Issue if you're having any difficulty following the guide so that we can update it. This guide is focused on only using SMTP ports (not POP3 and IMAP) with the intent to send received mail to another MTA service such as Gmail . It is not intended to have a MUA client ( eg: Thunderbird ) to retrieve mail directly from docker-mailserver via POP3/IMAP. In this setup docker-mailserver is not intended to receive email externally, so no anti-spam or anti-virus software is needed, making the service lighter to run. Open Relays Adding the docker network's gateway to the list of trusted hosts ( eg: using the network or connected-networks option ), can create an open relay . For instance if IPv6 is enabled on the host machine, but not in Docker . If you're running a version of docker-mailserver earlier than v10.2, you'll need to get setup.sh . Otherwise you can substitute ./setup.sh with docker exec mailserver setup . Pull the docker image: docker pull docker.io/mailserver/docker-mailserver:latest . Create the file docker-compose.yml with a content like this: Example services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver # Provide the FQDN of your mail server here (Your DNS MX record should point to this value) hostname : mail.example.com ports : - \"25:25\" - \"587:587\" - \"465:465\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - /etc/localtime:/etc/localtime:ro environment : - ENABLE_FAIL2BAN=1 # Using letsencrypt for SSL/TLS certificates - SSL_TYPE=letsencrypt # Allow sending emails from other docker containers # Beware creating an Open Relay: https://docker-mailserver.github.io/docker-mailserver/edge/config/environment/#permit_docker - PERMIT_DOCKER=network # You may want to enable this: https://docker-mailserver.github.io/docker-mailserver/edge/config/environment/#spoof_protection # See step 8 below, which demonstrates setup with enabled/disabled SPOOF_PROTECTION: - SPOOF_PROTECTION=0 cap_add : - NET_ADMIN # For Fail2Ban to work restart : always The docs have a detailed page on Environment Variables for reference. Firewalled ports You may need to open ports 25 , 587 and 465 on the firewall. For example, with the firewall ufw , run: ufw allow 25 ufw allow 587 ufw allow 465 Configure your DNS service to use an MX record for the hostname (eg: mail ) you configured in the previous step and add the SPF TXT record. If you manually manage the DNS zone file for the domain, it would look something like this: mail IN A 10.11.12.13 ; mail-server for example.com 3600 IN MX 1 mail.example.com. ; Add SPF record IN TXT \"v=spf1 mx ~all\" Then don't forget to change the serial number and to restart the service. Generate DKIM keys for your domain via ./setup.sh config dkim . Copy the content of the file docker-data/dms/config/opendkim/keys/example.com/mail.txt and add it to your DNS records as a TXT like SPF was handled above. I use bind9 for managing my domains, so I just paste it on example.com.db : mail._domainkey IN TXT ( \"v=DKIM1; h=sha256; k=rsa; \" \"p=MIIBIjANBgkqhkiG9w0BAQEFACAQ8AMIIBCgKCAQEAaH5KuPYPSF3Ppkt466BDMAFGOA4mgqn4oPjZ5BbFlYA9l5jU3bgzRj3l6/Q1n5a9lQs5fNZ7A/HtY0aMvs3nGE4oi+LTejt1jblMhV/OfJyRCunQBIGp0s8G9kIUBzyKJpDayk2+KJSJt/lxL9Iiy0DE5hIv62ZPP6AaTdHBAsJosLFeAzuLFHQ6USyQRojefqFQtgYqWQ2JiZQ3\" \"iqq3bD/BVlwKRp5gH6TEYEmx8EBJUuDxrJhkWRUk2VDl1fqhVBy8A9O7Ah+85nMrlOHIFsTaYo9o6+cDJ6t1i6G1gu+bZD0d3/3bqGLPBQV9LyEL1Rona5V7TJBGg099NQkTz1IwIDAQAB\" ) ; ----- DKIM key mail for example.com Get an SSL certificate, we have a guide for you here ( Let's Encrypt is a popular service to get free SSL certificates). Start docker-mailserver and check the terminal output for any errors: docker-compose up . Create email accounts and aliases: With SPOOF_PROTECTION=0 ./setup.sh email add admin@example.com passwd123 ./setup.sh email add info@example.com passwd123 ./setup.sh alias add admin@example.com external-account@gmail.com ./setup.sh alias add info@example.com external-account@gmail.com ./setup.sh email list ./setup.sh alias list Aliases make sure that any email that comes to these accounts is forwarded to your third-party email address ( external-account@gmail.com ), where they are retrieved ( eg: via third-party web or mobile app ), instead of connecting directly to docker-mailserer with POP3 / IMAP. With SPOOF_PROTECTION=1 ./setup.sh email add admin.gmail@example.com passwd123 ./setup.sh email add info.gmail@example.com passwd123 ./setup.sh alias add admin@example.com admin.gmail@example.com ./setup.sh alias add info@example.com info.gmail@example.com ./setup.sh alias add admin.gmail@example.com external-account@gmail.com ./setup.sh alias add info.gmail@example.com external-account@gmail.com ./setup.sh email list ./setup.sh alias list This extra step is required to avoid the 553 5.7.1 Sender address rejected: not owned by user error ( the accounts used for submitting mail to Gmail are admin.gmail@example.com and info.gmail@example.com ) Send some test emails to these addresses and make other tests. Once everything is working well, stop the container with ctrl+c and start it again as a daemon: docker-compose up -d .","title":"A Detailed Example"},{"location":"examples/tutorials/blog-posts/","text":"This site lists blog entries that write about the project. If you blogged about docker-mailserver let us know so we can add it here! Installing docker-mailserver by @andrewlow Self hosted mail-server by @matrixes Docker-mailserver on kubernetes by @ErikEngerd","title":"Blog Posts"},{"location":"examples/tutorials/docker-build/","text":"Building your own Docker image Submodules You'll need to retrieve the git submodules prior to building your own Docker image. From within your copy of the git repo run the following to retrieve the submodules and build the Docker image: git submodule update --init --recursive docker build -t mailserver/docker-mailserver . Or, you can clone and retrieve the submodules in one command: git clone --recurse-submodules https://github.com/docker-mailserver/docker-mailserver About Docker Version We make use of build-features that require a recent version of Docker. Depending on your distribution, please have a look at the official installation documentation for Docker to get the latest version. Otherwise, you may encounter issues, for example with the --link flag for a COPY command. Environment If you are not using make to build the image, note that you will need to provide DOCKER_BUILDKIT=1 to the docker build command for the build to succeed. Build Arguments The Dockerfile takes additional, so-called build arguments. These are VCS_VERSION : the image version (default = edge) VCS_REVISION : the image revision (default = unknown) When using make to build the image, these are filled with proper values. You can build the image without supplying these arguments just fine though.","title":"Building your own Docker image"},{"location":"examples/tutorials/docker-build/#building-your-own-docker-image","text":"","title":"Building your own Docker image"},{"location":"examples/tutorials/docker-build/#submodules","text":"You'll need to retrieve the git submodules prior to building your own Docker image. From within your copy of the git repo run the following to retrieve the submodules and build the Docker image: git submodule update --init --recursive docker build -t mailserver/docker-mailserver . Or, you can clone and retrieve the submodules in one command: git clone --recurse-submodules https://github.com/docker-mailserver/docker-mailserver","title":"Submodules"},{"location":"examples/tutorials/docker-build/#about-docker","text":"","title":"About Docker"},{"location":"examples/tutorials/docker-build/#version","text":"We make use of build-features that require a recent version of Docker. Depending on your distribution, please have a look at the official installation documentation for Docker to get the latest version. Otherwise, you may encounter issues, for example with the --link flag for a COPY command.","title":"Version"},{"location":"examples/tutorials/docker-build/#environment","text":"If you are not using make to build the image, note that you will need to provide DOCKER_BUILDKIT=1 to the docker build command for the build to succeed.","title":"Environment"},{"location":"examples/tutorials/docker-build/#build-arguments","text":"The Dockerfile takes additional, so-called build arguments. These are VCS_VERSION : the image version (default = edge) VCS_REVISION : the image revision (default = unknown) When using make to build the image, these are filled with proper values. You can build the image without supplying these arguments just fine though.","title":"Build Arguments"},{"location":"examples/tutorials/mailserver-behind-proxy/","text":"Using docker-mailserver behind a Proxy Information If you are hiding your container behind a proxy service you might have discovered that the proxied requests from now on contain the proxy IP as the request origin. Whilst this behavior is technical correct it produces certain problems on the containers behind the proxy as they cannot distinguish the real origin of the requests anymore. To solve this problem on TCP connections we can make use of the proxy protocol . Compared to other workarounds that exist ( X-Forwarded-For which only works for HTTP requests or Tproxy that requires you to recompile your kernel) the proxy protocol: It is protocol agnostic (can work with any layer 7 protocols, even when encrypted). It does not require any infrastructure changes. NAT-ing firewalls have no impact it. It is scalable. There is only one condition: both endpoints of the connection MUST be compatible with proxy protocol. Luckily dovecot and postfix are both Proxy-Protocol ready softwares so it depends only on your used reverse-proxy / loadbalancer. Configuration of the used Proxy Software The configuration depends on the used proxy system. I will provide the configuration examples of traefik v2 using IMAP and SMTP with implicit TLS. Feel free to add your configuration if you achieved the same goal using different proxy software below: Traefik v2 Truncated configuration of traefik itself: version : '3.8' services : reverse-proxy : image : docker.io/traefik:latest # v2.5 container_name : docker-traefik restart : always command : - \"--providers.docker\" - \"--providers.docker.exposedbydefault=false\" - \"--providers.docker.network=proxy\" - \"--entrypoints.web.address=:80\" - \"--entryPoints.websecure.address=:443\" - \"--entryPoints.smtp.address=:25\" - \"--entryPoints.smtp-ssl.address=:465\" - \"--entryPoints.imap-ssl.address=:993\" - \"--entryPoints.sieve.address=:4190\" ports : - \"25:25\" - \"465:465\" - \"993:993\" - \"4190:4190\" [ ... ] Truncated list of necessary labels on the docker-mailserver container: version : '3.8' services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail domainname : example.com restart : always networks : - proxy labels : - \"traefik.enable=true\" - \"traefik.tcp.routers.smtp.rule=HostSNI(`*`)\" - \"traefik.tcp.routers.smtp.entrypoints=smtp\" - \"traefik.tcp.routers.smtp.service=smtp\" - \"traefik.tcp.services.smtp.loadbalancer.server.port=25\" - \"traefik.tcp.services.smtp.loadbalancer.proxyProtocol.version=1\" - \"traefik.tcp.routers.smtp-ssl.rule=HostSNI(`*`)\" - \"traefik.tcp.routers.smtp-ssl.tls=false\" - \"traefik.tcp.routers.smtp-ssl.entrypoints=smtp-ssl\" - \"traefik.tcp.routers.smtp-ssl.service=smtp-ssl\" - \"traefik.tcp.services.smtp-ssl.loadbalancer.server.port=465\" - \"traefik.tcp.services.smtp-ssl.loadbalancer.proxyProtocol.version=1\" - \"traefik.tcp.routers.imap-ssl.rule=HostSNI(`*`)\" - \"traefik.tcp.routers.imap-ssl.entrypoints=imap-ssl\" - \"traefik.tcp.routers.imap-ssl.service=imap-ssl\" - \"traefik.tcp.services.imap-ssl.loadbalancer.server.port=10993\" - \"traefik.tcp.services.imap-ssl.loadbalancer.proxyProtocol.version=2\" - \"traefik.tcp.routers.sieve.rule=HostSNI(`*`)\" - \"traefik.tcp.routers.sieve.entrypoints=sieve\" - \"traefik.tcp.routers.sieve.service=sieve\" - \"traefik.tcp.services.sieve.loadbalancer.server.port=4190\" [ ... ] Keep in mind that it is necessary to use port 10993 here. More information below at dovecot configuration. Configuration of the Backend ( dovecot and postfix ) The following changes can be achieved completely by adding the content to the appropriate files by using the projects function to overwrite config files . Changes for postfix can be applied by adding the following content to docker-data/dms/config/postfix-main.cf : postscreen_upstream_proxy_protocol = haproxy and to docker-data/dms/config/postfix-master.cf : submission/inet/smtpd_upstream_proxy_protocol = haproxy smtps/inet/smtpd_upstream_proxy_protocol = haproxy Changes for dovecot can be applied by adding the following content to docker-data/dms/config/dovecot.cf : haproxy_trusted_networks = , haproxy_timeout = 3 secs service imap-login { inet_listener imaps { haproxy = yes ssl = yes port = 10993 } } Note Port 10993 is used here to avoid conflicts with internal systems like postscreen and amavis as they will exchange messages on the default port and obviously have a different origin then compared to the proxy.","title":"Mailserver behind Proxy"},{"location":"examples/tutorials/mailserver-behind-proxy/#using-docker-mailserver-behind-a-proxy","text":"","title":"Using docker-mailserver behind a Proxy"},{"location":"examples/tutorials/mailserver-behind-proxy/#information","text":"If you are hiding your container behind a proxy service you might have discovered that the proxied requests from now on contain the proxy IP as the request origin. Whilst this behavior is technical correct it produces certain problems on the containers behind the proxy as they cannot distinguish the real origin of the requests anymore. To solve this problem on TCP connections we can make use of the proxy protocol . Compared to other workarounds that exist ( X-Forwarded-For which only works for HTTP requests or Tproxy that requires you to recompile your kernel) the proxy protocol: It is protocol agnostic (can work with any layer 7 protocols, even when encrypted). It does not require any infrastructure changes. NAT-ing firewalls have no impact it. It is scalable. There is only one condition: both endpoints of the connection MUST be compatible with proxy protocol. Luckily dovecot and postfix are both Proxy-Protocol ready softwares so it depends only on your used reverse-proxy / loadbalancer.","title":"Information"},{"location":"examples/tutorials/mailserver-behind-proxy/#configuration-of-the-used-proxy-software","text":"The configuration depends on the used proxy system. I will provide the configuration examples of traefik v2 using IMAP and SMTP with implicit TLS. Feel free to add your configuration if you achieved the same goal using different proxy software below: Traefik v2 Truncated configuration of traefik itself: version : '3.8' services : reverse-proxy : image : docker.io/traefik:latest # v2.5 container_name : docker-traefik restart : always command : - \"--providers.docker\" - \"--providers.docker.exposedbydefault=false\" - \"--providers.docker.network=proxy\" - \"--entrypoints.web.address=:80\" - \"--entryPoints.websecure.address=:443\" - \"--entryPoints.smtp.address=:25\" - \"--entryPoints.smtp-ssl.address=:465\" - \"--entryPoints.imap-ssl.address=:993\" - \"--entryPoints.sieve.address=:4190\" ports : - \"25:25\" - \"465:465\" - \"993:993\" - \"4190:4190\" [ ... ] Truncated list of necessary labels on the docker-mailserver container: version : '3.8' services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail domainname : example.com restart : always networks : - proxy labels : - \"traefik.enable=true\" - \"traefik.tcp.routers.smtp.rule=HostSNI(`*`)\" - \"traefik.tcp.routers.smtp.entrypoints=smtp\" - \"traefik.tcp.routers.smtp.service=smtp\" - \"traefik.tcp.services.smtp.loadbalancer.server.port=25\" - \"traefik.tcp.services.smtp.loadbalancer.proxyProtocol.version=1\" - \"traefik.tcp.routers.smtp-ssl.rule=HostSNI(`*`)\" - \"traefik.tcp.routers.smtp-ssl.tls=false\" - \"traefik.tcp.routers.smtp-ssl.entrypoints=smtp-ssl\" - \"traefik.tcp.routers.smtp-ssl.service=smtp-ssl\" - \"traefik.tcp.services.smtp-ssl.loadbalancer.server.port=465\" - \"traefik.tcp.services.smtp-ssl.loadbalancer.proxyProtocol.version=1\" - \"traefik.tcp.routers.imap-ssl.rule=HostSNI(`*`)\" - \"traefik.tcp.routers.imap-ssl.entrypoints=imap-ssl\" - \"traefik.tcp.routers.imap-ssl.service=imap-ssl\" - \"traefik.tcp.services.imap-ssl.loadbalancer.server.port=10993\" - \"traefik.tcp.services.imap-ssl.loadbalancer.proxyProtocol.version=2\" - \"traefik.tcp.routers.sieve.rule=HostSNI(`*`)\" - \"traefik.tcp.routers.sieve.entrypoints=sieve\" - \"traefik.tcp.routers.sieve.service=sieve\" - \"traefik.tcp.services.sieve.loadbalancer.server.port=4190\" [ ... ] Keep in mind that it is necessary to use port 10993 here. More information below at dovecot configuration.","title":"Configuration of the used Proxy Software"},{"location":"examples/tutorials/mailserver-behind-proxy/#configuration-of-the-backend-dovecot-and-postfix","text":"The following changes can be achieved completely by adding the content to the appropriate files by using the projects function to overwrite config files . Changes for postfix can be applied by adding the following content to docker-data/dms/config/postfix-main.cf : postscreen_upstream_proxy_protocol = haproxy and to docker-data/dms/config/postfix-master.cf : submission/inet/smtpd_upstream_proxy_protocol = haproxy smtps/inet/smtpd_upstream_proxy_protocol = haproxy Changes for dovecot can be applied by adding the following content to docker-data/dms/config/dovecot.cf : haproxy_trusted_networks = , haproxy_timeout = 3 secs service imap-login { inet_listener imaps { haproxy = yes ssl = yes port = 10993 } } Note Port 10993 is used here to avoid conflicts with internal systems like postscreen and amavis as they will exchange messages on the default port and obviously have a different origin then compared to the proxy.","title":"Configuration of the Backend (dovecot and postfix)"},{"location":"examples/use-cases/forward-only-mailserver-with-ldap-authentication/","text":"Building a Forward-Only Mail-Server A forward-only mail-server does not have any local mailboxes. Instead, it has only aliases that forward emails to external email accounts (for example to a Gmail account). You can also send email from the localhost (the computer where docker-mailserver is installed), using as sender any of the alias addresses. The important settings for this setup (on mailserver.env ) are these: PERMIT_DOCKER = host ENABLE_POP3 = ENABLE_CLAMAV = 0 SMTP_ONLY = 1 ENABLE_SPAMASSASSIN = 0 ENABLE_FETCHMAIL = 0 Since there are no local mailboxes, we use SMTP_ONLY=1 to disable dovecot . We disable as well the other services that are related to local mailboxes ( POP3 , ClamAV , SpamAssassin , etc.) We can create aliases with ./setup.sh , like this: ./setup.sh alias add Authenticating with LDAP If you want to send emails from outside the mail-server you have to authenticate somehow (with a username and password). One way of doing it is described in this discussion . However if there are many user accounts, it is better to use authentication with LDAP. The settings for this on mailserver.env are: ENABLE_LDAP = 1 # with the :edge tag, use ACCOUNT_PROVISIONER ACCOUNT_PROVISIONER = LDAP LDAP_START_TLS = yes LDAP_SERVER_HOST = ldap.example.org LDAP_SEARCH_BASE = ou=users,dc=example,dc=org LDAP_BIND_DN = cn=mailserver,dc=example,dc=org LDAP_BIND_PW = pass1234 ENABLE_SASLAUTHD = 1 SASLAUTHD_MECHANISMS = ldap SASLAUTHD_LDAP_SERVER = ldap.example.org SASLAUTHD_LDAP_START_TLS = yes SASLAUTHD_LDAP_BIND_DN = cn=mailserver,dc=example,dc=org SASLAUTHD_LDAP_PASSWORD = pass1234 SASLAUTHD_LDAP_SEARCH_BASE = ou=users,dc=example,dc=org SASLAUTHD_LDAP_FILTER = (&(uid=%U)(objectClass=inetOrgPerson)) My LDAP data structure is very basic, containing only the username, password, and the external email address where to forward emails for this user. An entry looks like this: add uid = username,ou=users,dc=example,dc=org uid : username objectClass : inetOrgPerson sn : username cn : username userPassword : {SSHA}abcdefghi123456789 email : external-account@gmail.com This structure is different from what is expected/assumed from the configuration scripts of docker-mailserver , so it doesn't work just by using the LDAP_QUERY_FILTER_... settings. Instead, I had to use a custom configuration ( via user-patches.sh ). I created the script docker-data/dms/config/user-patches.sh , with content like this: #!/bin/bash rm -f /etc/postfix/ { ldap-groups.cf,ldap-domains.cf } postconf \\ \"virtual_mailbox_domains = /etc/postfix/vhost\" \\ \"virtual_alias_maps = ldap:/etc/postfix/ldap-aliases.cf texthash:/etc/postfix/virtual\" \\ \"smtpd_sender_login_maps = ldap:/etc/postfix/ldap-users.cf\" sed -i /etc/postfix/ldap-users.cf \\ -e '/query_filter/d' \\ -e '/result_attribute/d' \\ -e '/result_format/d' cat <> /etc/postfix/ldap-users.cf query_filter = (uid=%u) result_attribute = uid result_format = %s@example.org EOF sed -i /etc/postfix/ldap-aliases.cf \\ -e '/domain/d' \\ -e '/query_filter/d' \\ -e '/result_attribute/d' cat <> /etc/postfix/ldap-aliases.cf domain = example.org query_filter = (uid=%u) result_attribute = mail EOF postfix reload You see that besides query_filter , I had to customize as well result_attribute and result_format . See also For more details about using LDAP see: LDAP managed mail-server with Postfix and Dovecot for multiple domains Note Another solution that serves as a forward-only mail-server is this .","title":"Forward-Only Mail-Server with LDAP"},{"location":"examples/use-cases/forward-only-mailserver-with-ldap-authentication/#building-a-forward-only-mail-server","text":"A forward-only mail-server does not have any local mailboxes. Instead, it has only aliases that forward emails to external email accounts (for example to a Gmail account). You can also send email from the localhost (the computer where docker-mailserver is installed), using as sender any of the alias addresses. The important settings for this setup (on mailserver.env ) are these: PERMIT_DOCKER = host ENABLE_POP3 = ENABLE_CLAMAV = 0 SMTP_ONLY = 1 ENABLE_SPAMASSASSIN = 0 ENABLE_FETCHMAIL = 0 Since there are no local mailboxes, we use SMTP_ONLY=1 to disable dovecot . We disable as well the other services that are related to local mailboxes ( POP3 , ClamAV , SpamAssassin , etc.) We can create aliases with ./setup.sh , like this: ./setup.sh alias add ","title":"Building a Forward-Only Mail-Server"},{"location":"examples/use-cases/forward-only-mailserver-with-ldap-authentication/#authenticating-with-ldap","text":"If you want to send emails from outside the mail-server you have to authenticate somehow (with a username and password). One way of doing it is described in this discussion . However if there are many user accounts, it is better to use authentication with LDAP. The settings for this on mailserver.env are: ENABLE_LDAP = 1 # with the :edge tag, use ACCOUNT_PROVISIONER ACCOUNT_PROVISIONER = LDAP LDAP_START_TLS = yes LDAP_SERVER_HOST = ldap.example.org LDAP_SEARCH_BASE = ou=users,dc=example,dc=org LDAP_BIND_DN = cn=mailserver,dc=example,dc=org LDAP_BIND_PW = pass1234 ENABLE_SASLAUTHD = 1 SASLAUTHD_MECHANISMS = ldap SASLAUTHD_LDAP_SERVER = ldap.example.org SASLAUTHD_LDAP_START_TLS = yes SASLAUTHD_LDAP_BIND_DN = cn=mailserver,dc=example,dc=org SASLAUTHD_LDAP_PASSWORD = pass1234 SASLAUTHD_LDAP_SEARCH_BASE = ou=users,dc=example,dc=org SASLAUTHD_LDAP_FILTER = (&(uid=%U)(objectClass=inetOrgPerson)) My LDAP data structure is very basic, containing only the username, password, and the external email address where to forward emails for this user. An entry looks like this: add uid = username,ou=users,dc=example,dc=org uid : username objectClass : inetOrgPerson sn : username cn : username userPassword : {SSHA}abcdefghi123456789 email : external-account@gmail.com This structure is different from what is expected/assumed from the configuration scripts of docker-mailserver , so it doesn't work just by using the LDAP_QUERY_FILTER_... settings. Instead, I had to use a custom configuration ( via user-patches.sh ). I created the script docker-data/dms/config/user-patches.sh , with content like this: #!/bin/bash rm -f /etc/postfix/ { ldap-groups.cf,ldap-domains.cf } postconf \\ \"virtual_mailbox_domains = /etc/postfix/vhost\" \\ \"virtual_alias_maps = ldap:/etc/postfix/ldap-aliases.cf texthash:/etc/postfix/virtual\" \\ \"smtpd_sender_login_maps = ldap:/etc/postfix/ldap-users.cf\" sed -i /etc/postfix/ldap-users.cf \\ -e '/query_filter/d' \\ -e '/result_attribute/d' \\ -e '/result_format/d' cat <> /etc/postfix/ldap-users.cf query_filter = (uid=%u) result_attribute = uid result_format = %s@example.org EOF sed -i /etc/postfix/ldap-aliases.cf \\ -e '/domain/d' \\ -e '/query_filter/d' \\ -e '/result_attribute/d' cat <> /etc/postfix/ldap-aliases.cf domain = example.org query_filter = (uid=%u) result_attribute = mail EOF postfix reload You see that besides query_filter , I had to customize as well result_attribute and result_format . See also For more details about using LDAP see: LDAP managed mail-server with Postfix and Dovecot for multiple domains Note Another solution that serves as a forward-only mail-server is this .","title":"Authenticating with LDAP"},{"location":"examples/use-cases/imap-folders/","text":"Mailboxes ( aka IMAP Folders ) INBOX is setup as the private inbox namespace . By default target/dovecot/15-mailboxes.conf configures the special IMAP folders Drafts , Sent , Junk and Trash to be automatically created and subscribed. They are all assigned to the private inbox namespace ( which implicitly provides the INBOX folder ). These IMAP folders are considered special because they add a \"SPECIAL-USE\" attribute , which is a standardized way to communicate to mail clients that the folder serves a purpose like storing spam/junk mail ( \\Junk ) or deleted mail ( \\Trash ). This differentiates them from regular mail folders that you may use for organizing. Adding a mailbox folder See target/dovecot/15-mailboxes.conf for existing mailbox folders which you can modify or uncomment to enable some other common mailboxes. For more information try the official Dovecot documentation . The Archive special IMAP folder may be useful to enable. To do so, make a copy of target/dovecot/15-mailboxes.conf and uncomment the Archive mailbox definition. Mail clients should understand that this folder is intended for archiving mail due to the \\Archive \"SPECIAL-USE\" attribute . With the provided docker-compose.yml example, a volume bind mounts the host directory docker-data/dms/config/ to the container location /tmp/docker-mailserver/ . Config file overrides should instead be mounted to a different location as described in Overriding Configuration for Dovecot : volumes : - ./docker-data/dms/config/dovecot/15-mailboxes.conf:/etc/dovecot/conf.d/15-mailboxes.conf:ro Caution Adding folders to an existing setup Handling of newly added mailbox folders can be inconsistent across mail clients: Users may experience issues such as archived emails only being available locally. Users may need to migrate emails manually between two folders. Support for SPECIAL-USE attributes Not all mail clients support the SPECIAL-USE attribute for mailboxes ( defined in RFC 6154 ). These clients will treat the mailbox folder as any other, using the name assigned to it instead. Some clients may still know to treat these folders for their intended purpose if the mailbox name matches the common names that the SPECIAL-USE attributes represent ( eg Sent as the mailbox name for \\Sent ). Internationalization (i18n) Usually the mail client will know via context such as the SPECIAL-USE attribute or common English mailbox names, to provide a localized label for the users preferred language. Take care to test localized names work well as well. Email Clients Support If a new mail account is added without the SPECIAL-USE attribute enabled for archives: Thunderbird suggests and may create an Archives folder on the server. Outlook for Android archives to a local folder. Spark for Android archives to server folder named Archive . If a new mail account is added after the SPECIAL-USE attribute is enabled for archives: Thunderbird , Outlook for Android and Spark for Android will use the mailbox folder name assigned. Windows Mail Windows Mail has been said to ignore SPECIAL-USE attribute and look only at the mailbox folder name assigned. Needs citation This information is provided by the community. It presently lacks references to confirm the behaviour. If any information is incorrect please let us know!","title":"Customize IMAP Folders"},{"location":"examples/use-cases/imap-folders/#mailboxes-aka-imap-folders","text":"INBOX is setup as the private inbox namespace . By default target/dovecot/15-mailboxes.conf configures the special IMAP folders Drafts , Sent , Junk and Trash to be automatically created and subscribed. They are all assigned to the private inbox namespace ( which implicitly provides the INBOX folder ). These IMAP folders are considered special because they add a \"SPECIAL-USE\" attribute , which is a standardized way to communicate to mail clients that the folder serves a purpose like storing spam/junk mail ( \\Junk ) or deleted mail ( \\Trash ). This differentiates them from regular mail folders that you may use for organizing.","title":"Mailboxes (aka IMAP Folders)"},{"location":"examples/use-cases/imap-folders/#adding-a-mailbox-folder","text":"See target/dovecot/15-mailboxes.conf for existing mailbox folders which you can modify or uncomment to enable some other common mailboxes. For more information try the official Dovecot documentation . The Archive special IMAP folder may be useful to enable. To do so, make a copy of target/dovecot/15-mailboxes.conf and uncomment the Archive mailbox definition. Mail clients should understand that this folder is intended for archiving mail due to the \\Archive \"SPECIAL-USE\" attribute . With the provided docker-compose.yml example, a volume bind mounts the host directory docker-data/dms/config/ to the container location /tmp/docker-mailserver/ . Config file overrides should instead be mounted to a different location as described in Overriding Configuration for Dovecot : volumes : - ./docker-data/dms/config/dovecot/15-mailboxes.conf:/etc/dovecot/conf.d/15-mailboxes.conf:ro","title":"Adding a mailbox folder"},{"location":"examples/use-cases/imap-folders/#caution","text":"","title":"Caution"},{"location":"examples/use-cases/imap-folders/#adding-folders-to-an-existing-setup","text":"Handling of newly added mailbox folders can be inconsistent across mail clients: Users may experience issues such as archived emails only being available locally. Users may need to migrate emails manually between two folders.","title":"Adding folders to an existing setup"},{"location":"examples/use-cases/imap-folders/#support-for-special-use-attributes","text":"Not all mail clients support the SPECIAL-USE attribute for mailboxes ( defined in RFC 6154 ). These clients will treat the mailbox folder as any other, using the name assigned to it instead. Some clients may still know to treat these folders for their intended purpose if the mailbox name matches the common names that the SPECIAL-USE attributes represent ( eg Sent as the mailbox name for \\Sent ).","title":"Support for SPECIAL-USE attributes"},{"location":"examples/use-cases/imap-folders/#internationalization-i18n","text":"Usually the mail client will know via context such as the SPECIAL-USE attribute or common English mailbox names, to provide a localized label for the users preferred language. Take care to test localized names work well as well.","title":"Internationalization (i18n)"},{"location":"examples/use-cases/imap-folders/#email-clients-support","text":"If a new mail account is added without the SPECIAL-USE attribute enabled for archives: Thunderbird suggests and may create an Archives folder on the server. Outlook for Android archives to a local folder. Spark for Android archives to server folder named Archive . If a new mail account is added after the SPECIAL-USE attribute is enabled for archives: Thunderbird , Outlook for Android and Spark for Android will use the mailbox folder name assigned. Windows Mail Windows Mail has been said to ignore SPECIAL-USE attribute and look only at the mailbox folder name assigned. Needs citation This information is provided by the community. It presently lacks references to confirm the behaviour. If any information is incorrect please let us know!","title":"Email Clients Support"}]} \ No newline at end of file +{"config":{"indexing":"full","lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"Welcome to the Documentation for docker-mailserver ! This Documentation is Versioned Make sure to select the correct version of this documentation! It should match the version of the image you are using. The default version corresponds to the :edge image tag - the most recent build, not the most recent stable release . This documentation provides you not only with the basic setup and configuration of DMS but also with advanced configuration, elaborate usage scenarios, detailed examples, hints and more. About docker-mailserver , or DMS for short, is a production-ready fullstack but simple mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.). It employs only configuration files, no SQL database. The image is focused around the slogan \"Keep it simple and versioned\". Contents Getting Started If you're completely new to mail servers or you want to read up on them, check out our Introduction page . If you're new to DMS as a mail server appliance, make sure to read the Usage chapter first. If you want to look at examples for Docker Compose, we have an Examples page . There is also a script - setup.sh - supplied with this project. It supports you in configuring and administrating your server. Information on how to get it and how to use it is available on a dedicated page . Configuration We have a dedicated configuration page . It contains most of the configuration and explanation you need to setup your mail server properly. Be aware that advanced tasks may still require reading through all parts of this documentation; it may also involve inspecting your running container for debugging purposes. After all, a mail-server is a complex arrangement of various programs. Important If you'd like to change, patch or alter files or behavior of docker-mailserver , you can use a script. Just place a script called user-patches.sh in your ./docker-data/dms/config/ folder volume (which is mounted to /tmp/docker-mailserver/ inside the container) and it will be run on container startup. See the 'Modifications via Script' page for additional documentation and an example. You might also want to check out: A list of all configuration options via ENV A list of all optional and automatically created configuration files and directories Tip Definitely check out the FAQ for more information and tips! Please do not open an issue before you have checked our documentation for answers, including the FAQ ! Tests DMS employs a variety of tests. If you want to know more about our test suite, view our testing docs . Contributing We are always happy to welcome new contributors. For guidelines and entrypoints please have a look at the Contributing section .","title":"Home"},{"location":"#welcome-to-the-documentation-for-docker-mailserver","text":"This Documentation is Versioned Make sure to select the correct version of this documentation! It should match the version of the image you are using. The default version corresponds to the :edge image tag - the most recent build, not the most recent stable release . This documentation provides you not only with the basic setup and configuration of DMS but also with advanced configuration, elaborate usage scenarios, detailed examples, hints and more.","title":"Welcome to the Documentation for docker-mailserver!"},{"location":"#about","text":"docker-mailserver , or DMS for short, is a production-ready fullstack but simple mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.). It employs only configuration files, no SQL database. The image is focused around the slogan \"Keep it simple and versioned\".","title":"About"},{"location":"#contents","text":"","title":"Contents"},{"location":"#getting-started","text":"If you're completely new to mail servers or you want to read up on them, check out our Introduction page . If you're new to DMS as a mail server appliance, make sure to read the Usage chapter first. If you want to look at examples for Docker Compose, we have an Examples page . There is also a script - setup.sh - supplied with this project. It supports you in configuring and administrating your server. Information on how to get it and how to use it is available on a dedicated page .","title":"Getting Started"},{"location":"#configuration","text":"We have a dedicated configuration page . It contains most of the configuration and explanation you need to setup your mail server properly. Be aware that advanced tasks may still require reading through all parts of this documentation; it may also involve inspecting your running container for debugging purposes. After all, a mail-server is a complex arrangement of various programs. Important If you'd like to change, patch or alter files or behavior of docker-mailserver , you can use a script. Just place a script called user-patches.sh in your ./docker-data/dms/config/ folder volume (which is mounted to /tmp/docker-mailserver/ inside the container) and it will be run on container startup. See the 'Modifications via Script' page for additional documentation and an example. You might also want to check out: A list of all configuration options via ENV A list of all optional and automatically created configuration files and directories Tip Definitely check out the FAQ for more information and tips! Please do not open an issue before you have checked our documentation for answers, including the FAQ !","title":"Configuration"},{"location":"#tests","text":"DMS employs a variety of tests. If you want to know more about our test suite, view our testing docs .","title":"Tests"},{"location":"#contributing","text":"We are always happy to welcome new contributors. For guidelines and entrypoints please have a look at the Contributing section .","title":"Contributing"},{"location":"faq/","text":"What kind of database are you using? None! No database is required. The filesystem is the database. This image is based on config files that can be persisted using bind mounts (default) or Docker volumes, and as such versioned, backed up and so forth. Where are emails stored? Mails are stored in /var/mail/${domain}/${username} . Since v9.0.0 it is possible to add custom user_attributes for each accounts to have a different mailbox configuration (See #1792 ). How are IMAP mailboxes ( aka IMAP Folders ) set up? INBOX is setup by default with the special IMAP folders Drafts , Sent , Junk and Trash . You can learn how to modify or add your own folders ( including additional special folders like Archive ) by visiting our docs page Customizing IMAP Folders for more information. How do I update DMS? Make sure to read the CHANGELOG before updating to new versions, to be prepared for possible breaking changes. Then, run the following commands: docker-compose pull docker-compose down docker-compose up -d You should see the new version number on startup, for example: [ INF ] Welcome to docker-mailserver 11.3.1 . And you're done! Don't forget to have a look at the remaining functions of the setup.sh script with ./setup.sh help . Which operating systems are supported? Linux is officially supported. Windows and macOS are not supported and users and have reported various issues running the image on these hosts. As you'll realistically be deploying to production on a Linux host, if you are on Windows or macOS and want to run the image locally first, it's advised to do so via a VM guest running Linux if you have issues running DMS on your host system. What are the system requirements? Recommended 1 vCore 2GB RAM Swap enabled for the container Minimum 1 vCore 512MB RAM You'll need to avoid running some services like ClamAV ( disabled by default ) to be able to run on a host with 512MB of RAM. Warning ClamAV can consume a lot of memory, as it reads the entire signature database into RAM. Current figure is about 850M and growing. If you get errors about ClamAV or amavis failing to allocate memory you need more RAM or more swap and of course docker must be allowed to use swap (not always the case). If you can't use swap at all you may need 3G RAM. How to alter a running DMS instance without relaunching the container? docker-mailserver aggregates multiple \"sub-services\", such as Postfix, Dovecot, Fail2ban, SpamAssassin, etc. In many cases, one may edit a sub-service's config and reload that very sub-service, without stopping and relaunching the whole mail-server. In order to do so, you'll probably want to push your config updates to your server through a Docker volume (these docs use: ./docker-data/dms/config/:/tmp/docker-mailserver/ ), then restart the sub-service to apply your changes, using supervisorctl . For instance, after editing fail2ban's config: supervisorctl restart fail2ban . See the documentation for supervisorctl . Tip To add, update or delete an email account; there is no need to restart postfix / dovecot service inside the container after using setup.sh script. For more information, see #1639 . How can I sync the container and host date/time? Share the host's /etc/localtime with the container, e.g. by using a bind mount: volumes : - /etc/localtime:/etc/localtime:ro Optionally, you can set the TZ ENV variable; e.g. TZ=Europe/Berlin . Check this list for which values are allowed. What is the file format? All files are using the Unix format with LF line endings. Please do not use CRLF . Do you support multiple domains? DMS supports multiple domains out of the box, so you can do this: ./setup.sh email add user1@example.com ./setup.sh email add user1@example.de ./setup.sh email add user1@server.example.org What about backups? Bind mounts (default) From the location of your docker-compose.yml , create a compressed archive of your docker-data/dms/config/ and docker-data/dms/mail-* folders: tar --gzip -cf \"backup- $( date +%F ) .tar.gz\" ./docker-data/dms Then to restore docker-data/dms/config/ and docker-data/dms/mail-* folders from your backup file: tar --gzip -xf backup-date.tar.gz Volumes Assuming that you use docker-compose and data volumes, you can backup the configuration, emails and logs like this: # create backup docker run --rm -it \\ -v \" ${ PWD } /docker-data/dms/config/:/tmp/docker-mailserver/\" \\ -v \" ${ PWD } /docker-data/dms-backups/:/backup/\" \\ --volumes-from mailserver \\ alpine:latest \\ tar czf \"/backup/mail- $( date +%F ) .tar.gz\" /var/mail /var/mail-state /var/log/mail /tmp/docker-mailserver # delete backups older than 30 days find \" ${ PWD } /docker-data/dms-backups/\" -type f -mtime +30 -delete What about the ./docker-data/dms/mail-state folder? When you run DMS with the ENV variable ONE_DIR=1 (default), this folder will: Provide support to persist Fail2Ban blocks, ClamAV signature updates, and the like when the container is restarted or recreated. To persist that container state properly this folder should be volume mounted to /var/mail-state/ internally . Service data is relocated to the mail-state folder for the following services: Postfix, Dovecot, Fail2Ban, Amavis, PostGrey, ClamAV, SpamAssassin. I Want to Know More About the Ports See this part of the documentation for further details and best practice advice, especially regarding security concerns . How can I configure my email client? Login is full email address ( @ ). # IMAP username : password : server : imap port : 143 or 993 with STARTTLS/SSL (recommended) imap path prefix : INBOX # SMTP smtp port : 25 or 587/465 with STARTTLS/SSL (recommended) username : password : DMS is properly configured for port 587, if possible, we recommend using port 465 for SMTP though. See this section to learn more about ports . Can I use a naked/bare domain (i.e. no hostname)? Yes, but not without some configuration changes. Normally it is assumed that DMS runs on a host with a name, so the fully qualified host name might be mail.example.com with the domain example.com . The MX records point to mail.example.com . To use a bare domain ( where the host name is example.com and the domain is also example.com ), change mydestination : From: mydestination = $myhostname, localhost.$mydomain, localhost To: mydestination = localhost.$mydomain, localhost Add the latter line to docker-data/dms/config/postfix-main.cf . If that doesn't work, make sure that OVERRIDE_HOSTNAME is blank in your mailserver.env file (see #1731 ). Without these changes there will be warnings in the logs like: warning: do not list domain example.com in BOTH mydestination and virtual_mailbox_domains Plus of course mail delivery fails. Also you need to define hostname: example.com in your docker-compose.yml and don't sepecify the domainname: at all. How can I configure a catch-all? Considering you want to redirect all incoming e-mails for the domain example.com to user1@example.com , add the following line to docker-data/dms/config/postfix-virtual.cf : @example.com user1@example.com How can I delete all the emails for a specific user? First of all, create a special alias named devnull by editing docker-data/dms/config/postfix-aliases.cf : devnull: /dev/null Considering you want to delete all the e-mails received for baduser@example.com , add the following line to docker-data/dms/config/postfix-virtual.cf : baduser@example.com devnull Important If you use a catch-all rule for the main/sub domain, you need another entry in docker-data/dms/config/postfix-virtual.cf : @mail.example.com hello@example.com baduser@example.com devnull devnull@mail.example.com devnull What kind of SSL certificates can I use? Both RSA and ECDSA certs are supported. You can provide your own cert files manually, or mount a letsencrypt generated directory ( with alternative support for Traefik's acme.json ). Check out the SSL_TYPE documentation for more details. I just moved from my old mail server to DMS, but \"it doesn't work\"? If this migration implies a DNS modification, be sure to wait for DNS propagation before opening an issue. Few examples of symptoms can be found here or here . This could be related to a modification of your MX record, or the IP mapped to mail.example.com . Additionally, validate your DNS configuration . If everything is OK regarding DNS, please provide formatted logs and config files. This will allow us to help you. If we're blind, we won't be able to do anything. Can DMS run in a Rancher environment? Yes, by adding the environment variable PERMIT_DOCKER: network . Warning Adding the Docker network's gateway to the list of trusted hosts, e.g. using the network or connected-networks option, can create an open relay , for instance if IPv6 is enabled on the host machine but not in Docker . How can I authenticate users with SMTP_ONLY=1 ? See #1247 for an example. Todo Write a How-to / Use-Case / Tutorial about authentication with SMTP_ONLY . Common Errors warning: connect to Milter service inet:localhost:8893: Connection refused # DMARC not running # = > /etc/init.d/opendmarc restart warning: connect to Milter service inet:localhost:8891: Connection refused # DKIM not running # = > /etc/init.d/opendkim restart mail amavis[1459]: (01459-01) (!)connect to /var/run/clamav/clamd.ctl failed, attempt #1: Can't connect to a UNIX socket /var/run/clamav/clamd.ctl: No such file or directory mail amavis[1459]: (01459-01) (!)ClamAV-clamd: All attempts (1) failed connecting to /var/run/clamav/clamd.ctl, retrying (2) mail amavis[1459]: (01459-01) (!)ClamAV-clamscan av-scanner FAILED: /usr/bin/clamscan KILLED, signal 9 (0009) at (eval 100) line 905. mail amavis[1459]: (01459-01) (!!)AV: ALL VIRUS SCANNERS FAILED # Clamav is not running ( not started or because you don ' t have enough memory ) # = > check requirements and/or start Clamav How to use DMS behind a proxy Using user-patches.sh , update the container file /etc/postfix/main.cf to include: proxy_interfaces = X.X.X.X (your public IP) How to adjust settings with the user-patches.sh script Suppose you want to change a number of settings that are not listed as variables or add things to the server that are not included? docker-mailserver has a built-in way to do post-install processes. If you place a script called user-patches.sh in the config directory it will be run after all configuration files are set up, but before the postfix, amavis and other daemons are started. It is common to use a local directory for config added to docker-mailsever via a volume mount in your docker-compose.yml (eg: ./docker-data/dms/config/:/tmp/docker-mailserver/ ). Add or create the script file to your config directory: cd ./docker-data/dms/config touch user-patches.sh chmod +x user-patches.sh Then fill user-patches.sh with suitable code. If you want to test it you can move into the running container, run it and see if it does what you want. For instance: # start shell in container ./setup.sh debug login # check the file cat /tmp/docker-mailserver/user-patches.sh # run the script /tmp/docker-mailserver/user-patches.sh # exit the container shell back to the host shell exit You can do a lot of things with such a script. You can find an example user-patches.sh script here: example user-patches.sh script . We also have a very similar docs page specifically about this feature! Special use-case - patching the supervisord configuration It seems worth noting, that the user-patches.sh gets executed through supervisord . If you need to patch some supervisord config (e.g. /etc/supervisor/conf.d/saslauth.conf ), the patching happens too late. An easy workaround is to make the user-patches.sh reload the supervisord config after patching it: #!/bin/bash sed -i 's/rimap -r/rimap/' /etc/supervisor/conf.d/saslauth.conf supervisorctl update How to ban custom IP addresses with Fail2ban Use the following command: ./setup.sh fail2ban ban The default bantime is 180 days. This value can be customized . What to do in case of 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. SpamAssasin How can I manage my custom SpamAssassin rules? Antispam rules are managed in docker-data/dms/config/spamassassin-rules.cf . What are acceptable SA_SPAM_SUBJECT values? For no subject set SA_SPAM_SUBJECT=undef . For a trailing white-space subject one can define the whole variable with quotes in docker-compose.yml : environment : - \"SA_SPAM_SUBJECT=[SPAM] \" Why are SpamAssassin x-headers not inserted into my subdomain.example.com subdomain emails? In the default setup, amavis only applies SpamAssassin x-headers into domains matching the template listed in the config file ( 05-domain_id in the amavis defaults). The default setup @local_domains_acl = ( \".$mydomain\" ); does not match subdomains. To match subdomains, you can override the @local_domains_acl directive in the amavis user config file 50-user with @local_domains_maps = (\".\"); to match any sort of domain template. How can I make SpamAssassin better recognize spam? Put received spams in .Junk/ imap folder using SPAMASSASSIN_SPAM_TO_INBOX=1 and MOVE_SPAM_TO_JUNK=1 and add a user cron like the following: # This assumes you're having `environment: ONE_DIR=1` in the `mailserver.env`, # with a consolidated config in `/var/mail-state` # # m h dom mon dow command # Everyday 2:00AM, learn spam from a specific user 0 2 * * * docker exec mailserver sa-learn --spam /var/mail/example.com/username/.Junk --dbpath /var/mail-state/lib-amavis/.spamassassin With docker-compose you can more easily use the internal instance of cron within docker-mailserver . This is less problematic than the simple solution shown above, because it decouples the learning from the host on which docker-mailserver is running, and avoids errors if the mail-server is not running. The following configuration works nicely: Example Create a system cron file: # in the docker-compose.yml root directory mkdir -p ./docker-data/dms/cron touch ./docker-data/dms/cron/sa-learn chown root:root ./docker-data/dms/cron/sa-learn chmod 0644 ./docker-data/dms/cron/sa-learn Edit the system cron file nano ./docker-data/dms/cron/sa-learn , and set an appropriate configuration: # This assumes you're having `environment: ONE_DIR=1` in the env-mailserver, # with a consolidated config in `/var/mail-state` # # '> /dev/null' to send error notifications from 'stderr' to 'postmaster@example.com' # # m h dom mon dow user command # # Everyday 2:00AM, learn spam from a specific user # spam: junk directory 0 2 * * * root sa-learn --spam /var/mail/example.com/username/.Junk --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # ham: archive directories 15 2 * * * root sa-learn --ham /var/mail/example.com/username/.Archive* --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # ham: inbox subdirectories 30 2 * * * root sa-learn --ham /var/mail/example.com/username/cur* --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # # Everyday 3:00AM, learn spam from all users of a domain # spam: junk directory 0 3 * * * root sa-learn --spam /var/mail/not-example.com/*/.Junk --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # ham: archive directories 15 3 * * * root sa-learn --ham /var/mail/not-example.com/*/.Archive* --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # ham: inbox subdirectories 30 3 * * * root sa-learn --ham /var/mail/not-example.com/*/cur* --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null Then with docker-compose.yml : services : mailserver : image : docker.io/mailserver/docker-mailserver:latest volumes : - ./docker-data/dms/cron/sa-learn:/etc/cron.d/sa-learn Or with Docker Swarm : version : '3.8' services : mailserver : image : docker.io/mailserver/docker-mailserver:latest # ... configs : - source : my_sa_crontab target : /etc/cron.d/sa-learn configs : my_sa_crontab : file : ./docker-data/dms/cron/sa-learn With the default settings, SpamAssassin will require 200 mails trained for spam (for example with the method explained above) and 200 mails trained for ham (using the same command as above but using --ham and providing it with some ham mails). Until you provided these 200+200 mails, SpamAssassin will not take the learned mails into account. For further reference, see the SpamAssassin Wiki . How do I have more control about what SpamAssassin is filtering? By default, SPAM and INFECTED emails are put to a quarantine which is not very straight forward to access. Several config settings are affecting this behavior: First, make sure you have the proper thresholds set: SA_TAG = -100000.0 SA_TAG2 = 3.75 SA_KILL = 100000.0 The very negative vaue in SA_TAG makes sure, that all emails have the SpamAssassin headers included. SA_TAG2 is the actual threshold to set the YES/NO flag for spam detection. SA_KILL needs to be very high, to make sure nothing is bounced at all ( SA_KILL superseeds SPAMASSASSIN_SPAM_TO_INBOX ) Make sure everything (including SPAM) is delivered to the inbox and not quarantined: SPAMASSASSIN_SPAM_TO_INBOX = 1 Use MOVE_SPAM_TO_JUNK=1 or create a sieve script which puts spam to the Junk folder: require [ \"comparator-i;ascii-numeric\" , \"relational\" , \"fileinto\" ]; if header :contains \"X-Spam-Flag\" \"YES\" { fileinto \"Junk\" ; } elsif allof ( not header :matches \"x-spam-score\" \"-*\" , header :value \"ge\" :comparator \"i;ascii-numeric\" \"x-spam-score\" \"3.75\" ) { fileinto \"Junk\" ; } Create a dedicated mailbox for emails which are infected/bad header and everything amavis is blocking by default and put its address into docker-data/dms/config/amavis.cf $clean_quarantine_to = \"amavis\\@example.com\"; $virus_quarantine_to = \"amavis\\@example.com\"; $banned_quarantine_to = \"amavis\\@example.com\"; $bad_header_quarantine_to = \"amavis\\@example.com\"; $spam_quarantine_to = \"amavis\\@example.com\";","title":"FAQ"},{"location":"faq/#what-kind-of-database-are-you-using","text":"None! No database is required. The filesystem is the database. This image is based on config files that can be persisted using bind mounts (default) or Docker volumes, and as such versioned, backed up and so forth.","title":"What kind of database are you using?"},{"location":"faq/#where-are-emails-stored","text":"Mails are stored in /var/mail/${domain}/${username} . Since v9.0.0 it is possible to add custom user_attributes for each accounts to have a different mailbox configuration (See #1792 ).","title":"Where are emails stored?"},{"location":"faq/#how-are-imap-mailboxes-aka-imap-folders-set-up","text":"INBOX is setup by default with the special IMAP folders Drafts , Sent , Junk and Trash . You can learn how to modify or add your own folders ( including additional special folders like Archive ) by visiting our docs page Customizing IMAP Folders for more information.","title":"How are IMAP mailboxes (aka IMAP Folders) set up?"},{"location":"faq/#how-do-i-update-dms","text":"Make sure to read the CHANGELOG before updating to new versions, to be prepared for possible breaking changes. Then, run the following commands: docker-compose pull docker-compose down docker-compose up -d You should see the new version number on startup, for example: [ INF ] Welcome to docker-mailserver 11.3.1 . And you're done! Don't forget to have a look at the remaining functions of the setup.sh script with ./setup.sh help .","title":"How do I update DMS?"},{"location":"faq/#which-operating-systems-are-supported","text":"Linux is officially supported. Windows and macOS are not supported and users and have reported various issues running the image on these hosts. As you'll realistically be deploying to production on a Linux host, if you are on Windows or macOS and want to run the image locally first, it's advised to do so via a VM guest running Linux if you have issues running DMS on your host system.","title":"Which operating systems are supported?"},{"location":"faq/#what-are-the-system-requirements","text":"","title":"What are the system requirements?"},{"location":"faq/#recommended","text":"1 vCore 2GB RAM Swap enabled for the container","title":"Recommended"},{"location":"faq/#minimum","text":"1 vCore 512MB RAM You'll need to avoid running some services like ClamAV ( disabled by default ) to be able to run on a host with 512MB of RAM. Warning ClamAV can consume a lot of memory, as it reads the entire signature database into RAM. Current figure is about 850M and growing. If you get errors about ClamAV or amavis failing to allocate memory you need more RAM or more swap and of course docker must be allowed to use swap (not always the case). If you can't use swap at all you may need 3G RAM.","title":"Minimum"},{"location":"faq/#how-to-alter-a-running-dms-instance-without-relaunching-the-container","text":"docker-mailserver aggregates multiple \"sub-services\", such as Postfix, Dovecot, Fail2ban, SpamAssassin, etc. In many cases, one may edit a sub-service's config and reload that very sub-service, without stopping and relaunching the whole mail-server. In order to do so, you'll probably want to push your config updates to your server through a Docker volume (these docs use: ./docker-data/dms/config/:/tmp/docker-mailserver/ ), then restart the sub-service to apply your changes, using supervisorctl . For instance, after editing fail2ban's config: supervisorctl restart fail2ban . See the documentation for supervisorctl . Tip To add, update or delete an email account; there is no need to restart postfix / dovecot service inside the container after using setup.sh script. For more information, see #1639 .","title":"How to alter a running DMS instance without relaunching the container?"},{"location":"faq/#how-can-i-sync-the-container-and-host-datetime","text":"Share the host's /etc/localtime with the container, e.g. by using a bind mount: volumes : - /etc/localtime:/etc/localtime:ro Optionally, you can set the TZ ENV variable; e.g. TZ=Europe/Berlin . Check this list for which values are allowed.","title":"How can I sync the container and host date/time?"},{"location":"faq/#what-is-the-file-format","text":"All files are using the Unix format with LF line endings. Please do not use CRLF .","title":"What is the file format?"},{"location":"faq/#do-you-support-multiple-domains","text":"DMS supports multiple domains out of the box, so you can do this: ./setup.sh email add user1@example.com ./setup.sh email add user1@example.de ./setup.sh email add user1@server.example.org","title":"Do you support multiple domains?"},{"location":"faq/#what-about-backups","text":"","title":"What about backups?"},{"location":"faq/#bind-mounts-default","text":"From the location of your docker-compose.yml , create a compressed archive of your docker-data/dms/config/ and docker-data/dms/mail-* folders: tar --gzip -cf \"backup- $( date +%F ) .tar.gz\" ./docker-data/dms Then to restore docker-data/dms/config/ and docker-data/dms/mail-* folders from your backup file: tar --gzip -xf backup-date.tar.gz","title":"Bind mounts (default)"},{"location":"faq/#volumes","text":"Assuming that you use docker-compose and data volumes, you can backup the configuration, emails and logs like this: # create backup docker run --rm -it \\ -v \" ${ PWD } /docker-data/dms/config/:/tmp/docker-mailserver/\" \\ -v \" ${ PWD } /docker-data/dms-backups/:/backup/\" \\ --volumes-from mailserver \\ alpine:latest \\ tar czf \"/backup/mail- $( date +%F ) .tar.gz\" /var/mail /var/mail-state /var/log/mail /tmp/docker-mailserver # delete backups older than 30 days find \" ${ PWD } /docker-data/dms-backups/\" -type f -mtime +30 -delete","title":"Volumes"},{"location":"faq/#what-about-the-docker-datadmsmail-state-folder","text":"When you run DMS with the ENV variable ONE_DIR=1 (default), this folder will: Provide support to persist Fail2Ban blocks, ClamAV signature updates, and the like when the container is restarted or recreated. To persist that container state properly this folder should be volume mounted to /var/mail-state/ internally . Service data is relocated to the mail-state folder for the following services: Postfix, Dovecot, Fail2Ban, Amavis, PostGrey, ClamAV, SpamAssassin.","title":"What about the ./docker-data/dms/mail-state folder?"},{"location":"faq/#i-want-to-know-more-about-the-ports","text":"See this part of the documentation for further details and best practice advice, especially regarding security concerns .","title":"I Want to Know More About the Ports"},{"location":"faq/#how-can-i-configure-my-email-client","text":"Login is full email address ( @ ). # IMAP username : password : server : imap port : 143 or 993 with STARTTLS/SSL (recommended) imap path prefix : INBOX # SMTP smtp port : 25 or 587/465 with STARTTLS/SSL (recommended) username : password : DMS is properly configured for port 587, if possible, we recommend using port 465 for SMTP though. See this section to learn more about ports .","title":"How can I configure my email client?"},{"location":"faq/#can-i-use-a-nakedbare-domain-ie-no-hostname","text":"Yes, but not without some configuration changes. Normally it is assumed that DMS runs on a host with a name, so the fully qualified host name might be mail.example.com with the domain example.com . The MX records point to mail.example.com . To use a bare domain ( where the host name is example.com and the domain is also example.com ), change mydestination : From: mydestination = $myhostname, localhost.$mydomain, localhost To: mydestination = localhost.$mydomain, localhost Add the latter line to docker-data/dms/config/postfix-main.cf . If that doesn't work, make sure that OVERRIDE_HOSTNAME is blank in your mailserver.env file (see #1731 ). Without these changes there will be warnings in the logs like: warning: do not list domain example.com in BOTH mydestination and virtual_mailbox_domains Plus of course mail delivery fails. Also you need to define hostname: example.com in your docker-compose.yml and don't sepecify the domainname: at all.","title":"Can I use a naked/bare domain (i.e. no hostname)?"},{"location":"faq/#how-can-i-configure-a-catch-all","text":"Considering you want to redirect all incoming e-mails for the domain example.com to user1@example.com , add the following line to docker-data/dms/config/postfix-virtual.cf : @example.com user1@example.com","title":"How can I configure a catch-all?"},{"location":"faq/#how-can-i-delete-all-the-emails-for-a-specific-user","text":"First of all, create a special alias named devnull by editing docker-data/dms/config/postfix-aliases.cf : devnull: /dev/null Considering you want to delete all the e-mails received for baduser@example.com , add the following line to docker-data/dms/config/postfix-virtual.cf : baduser@example.com devnull Important If you use a catch-all rule for the main/sub domain, you need another entry in docker-data/dms/config/postfix-virtual.cf : @mail.example.com hello@example.com baduser@example.com devnull devnull@mail.example.com devnull","title":"How can I delete all the emails for a specific user?"},{"location":"faq/#what-kind-of-ssl-certificates-can-i-use","text":"Both RSA and ECDSA certs are supported. You can provide your own cert files manually, or mount a letsencrypt generated directory ( with alternative support for Traefik's acme.json ). Check out the SSL_TYPE documentation for more details.","title":"What kind of SSL certificates can I use?"},{"location":"faq/#i-just-moved-from-my-old-mail-server-to-dms-but-it-doesnt-work","text":"If this migration implies a DNS modification, be sure to wait for DNS propagation before opening an issue. Few examples of symptoms can be found here or here . This could be related to a modification of your MX record, or the IP mapped to mail.example.com . Additionally, validate your DNS configuration . If everything is OK regarding DNS, please provide formatted logs and config files. This will allow us to help you. If we're blind, we won't be able to do anything.","title":"I just moved from my old mail server to DMS, but \"it doesn't work\"?"},{"location":"faq/#can-dms-run-in-a-rancher-environment","text":"Yes, by adding the environment variable PERMIT_DOCKER: network . Warning Adding the Docker network's gateway to the list of trusted hosts, e.g. using the network or connected-networks option, can create an open relay , for instance if IPv6 is enabled on the host machine but not in Docker .","title":"Can DMS run in a Rancher environment?"},{"location":"faq/#how-can-i-authenticate-users-with-smtp_only1","text":"See #1247 for an example. Todo Write a How-to / Use-Case / Tutorial about authentication with SMTP_ONLY .","title":"How can I authenticate users with SMTP_ONLY=1?"},{"location":"faq/#common-errors","text":"warning: connect to Milter service inet:localhost:8893: Connection refused # DMARC not running # = > /etc/init.d/opendmarc restart warning: connect to Milter service inet:localhost:8891: Connection refused # DKIM not running # = > /etc/init.d/opendkim restart mail amavis[1459]: (01459-01) (!)connect to /var/run/clamav/clamd.ctl failed, attempt #1: Can't connect to a UNIX socket /var/run/clamav/clamd.ctl: No such file or directory mail amavis[1459]: (01459-01) (!)ClamAV-clamd: All attempts (1) failed connecting to /var/run/clamav/clamd.ctl, retrying (2) mail amavis[1459]: (01459-01) (!)ClamAV-clamscan av-scanner FAILED: /usr/bin/clamscan KILLED, signal 9 (0009) at (eval 100) line 905. mail amavis[1459]: (01459-01) (!!)AV: ALL VIRUS SCANNERS FAILED # Clamav is not running ( not started or because you don ' t have enough memory ) # = > check requirements and/or start Clamav","title":"Common Errors"},{"location":"faq/#how-to-use-dms-behind-a-proxy","text":"Using user-patches.sh , update the container file /etc/postfix/main.cf to include: proxy_interfaces = X.X.X.X (your public IP)","title":"How to use DMS behind a proxy"},{"location":"faq/#how-to-adjust-settings-with-the-user-patchessh-script","text":"Suppose you want to change a number of settings that are not listed as variables or add things to the server that are not included? docker-mailserver has a built-in way to do post-install processes. If you place a script called user-patches.sh in the config directory it will be run after all configuration files are set up, but before the postfix, amavis and other daemons are started. It is common to use a local directory for config added to docker-mailsever via a volume mount in your docker-compose.yml (eg: ./docker-data/dms/config/:/tmp/docker-mailserver/ ). Add or create the script file to your config directory: cd ./docker-data/dms/config touch user-patches.sh chmod +x user-patches.sh Then fill user-patches.sh with suitable code. If you want to test it you can move into the running container, run it and see if it does what you want. For instance: # start shell in container ./setup.sh debug login # check the file cat /tmp/docker-mailserver/user-patches.sh # run the script /tmp/docker-mailserver/user-patches.sh # exit the container shell back to the host shell exit You can do a lot of things with such a script. You can find an example user-patches.sh script here: example user-patches.sh script . We also have a very similar docs page specifically about this feature! Special use-case - patching the supervisord configuration It seems worth noting, that the user-patches.sh gets executed through supervisord . If you need to patch some supervisord config (e.g. /etc/supervisor/conf.d/saslauth.conf ), the patching happens too late. An easy workaround is to make the user-patches.sh reload the supervisord config after patching it: #!/bin/bash sed -i 's/rimap -r/rimap/' /etc/supervisor/conf.d/saslauth.conf supervisorctl update","title":"How to adjust settings with the user-patches.sh script"},{"location":"faq/#how-to-ban-custom-ip-addresses-with-fail2ban","text":"Use the following command: ./setup.sh fail2ban ban The default bantime is 180 days. This value can be customized .","title":"How to ban custom IP addresses with Fail2ban"},{"location":"faq/#what-to-do-in-case-of-spfforwarding-problems","text":"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.","title":"What to do in case of SPF/Forwarding problems"},{"location":"faq/#spamassasin","text":"","title":"SpamAssasin"},{"location":"faq/#how-can-i-manage-my-custom-spamassassin-rules","text":"Antispam rules are managed in docker-data/dms/config/spamassassin-rules.cf .","title":"How can I manage my custom SpamAssassin rules?"},{"location":"faq/#what-are-acceptable-sa_spam_subject-values","text":"For no subject set SA_SPAM_SUBJECT=undef . For a trailing white-space subject one can define the whole variable with quotes in docker-compose.yml : environment : - \"SA_SPAM_SUBJECT=[SPAM] \"","title":"What are acceptable SA_SPAM_SUBJECT values?"},{"location":"faq/#why-are-spamassassin-x-headers-not-inserted-into-my-subdomainexamplecom-subdomain-emails","text":"In the default setup, amavis only applies SpamAssassin x-headers into domains matching the template listed in the config file ( 05-domain_id in the amavis defaults). The default setup @local_domains_acl = ( \".$mydomain\" ); does not match subdomains. To match subdomains, you can override the @local_domains_acl directive in the amavis user config file 50-user with @local_domains_maps = (\".\"); to match any sort of domain template.","title":"Why are SpamAssassin x-headers not inserted into my subdomain.example.com subdomain emails?"},{"location":"faq/#how-can-i-make-spamassassin-better-recognize-spam","text":"Put received spams in .Junk/ imap folder using SPAMASSASSIN_SPAM_TO_INBOX=1 and MOVE_SPAM_TO_JUNK=1 and add a user cron like the following: # This assumes you're having `environment: ONE_DIR=1` in the `mailserver.env`, # with a consolidated config in `/var/mail-state` # # m h dom mon dow command # Everyday 2:00AM, learn spam from a specific user 0 2 * * * docker exec mailserver sa-learn --spam /var/mail/example.com/username/.Junk --dbpath /var/mail-state/lib-amavis/.spamassassin With docker-compose you can more easily use the internal instance of cron within docker-mailserver . This is less problematic than the simple solution shown above, because it decouples the learning from the host on which docker-mailserver is running, and avoids errors if the mail-server is not running. The following configuration works nicely: Example Create a system cron file: # in the docker-compose.yml root directory mkdir -p ./docker-data/dms/cron touch ./docker-data/dms/cron/sa-learn chown root:root ./docker-data/dms/cron/sa-learn chmod 0644 ./docker-data/dms/cron/sa-learn Edit the system cron file nano ./docker-data/dms/cron/sa-learn , and set an appropriate configuration: # This assumes you're having `environment: ONE_DIR=1` in the env-mailserver, # with a consolidated config in `/var/mail-state` # # '> /dev/null' to send error notifications from 'stderr' to 'postmaster@example.com' # # m h dom mon dow user command # # Everyday 2:00AM, learn spam from a specific user # spam: junk directory 0 2 * * * root sa-learn --spam /var/mail/example.com/username/.Junk --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # ham: archive directories 15 2 * * * root sa-learn --ham /var/mail/example.com/username/.Archive* --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # ham: inbox subdirectories 30 2 * * * root sa-learn --ham /var/mail/example.com/username/cur* --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # # Everyday 3:00AM, learn spam from all users of a domain # spam: junk directory 0 3 * * * root sa-learn --spam /var/mail/not-example.com/*/.Junk --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # ham: archive directories 15 3 * * * root sa-learn --ham /var/mail/not-example.com/*/.Archive* --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null # ham: inbox subdirectories 30 3 * * * root sa-learn --ham /var/mail/not-example.com/*/cur* --dbpath /var/mail-state/lib-amavis/.spamassassin > /dev/null Then with docker-compose.yml : services : mailserver : image : docker.io/mailserver/docker-mailserver:latest volumes : - ./docker-data/dms/cron/sa-learn:/etc/cron.d/sa-learn Or with Docker Swarm : version : '3.8' services : mailserver : image : docker.io/mailserver/docker-mailserver:latest # ... configs : - source : my_sa_crontab target : /etc/cron.d/sa-learn configs : my_sa_crontab : file : ./docker-data/dms/cron/sa-learn With the default settings, SpamAssassin will require 200 mails trained for spam (for example with the method explained above) and 200 mails trained for ham (using the same command as above but using --ham and providing it with some ham mails). Until you provided these 200+200 mails, SpamAssassin will not take the learned mails into account. For further reference, see the SpamAssassin Wiki .","title":"How can I make SpamAssassin better recognize spam?"},{"location":"faq/#how-do-i-have-more-control-about-what-spamassassin-is-filtering","text":"By default, SPAM and INFECTED emails are put to a quarantine which is not very straight forward to access. Several config settings are affecting this behavior: First, make sure you have the proper thresholds set: SA_TAG = -100000.0 SA_TAG2 = 3.75 SA_KILL = 100000.0 The very negative vaue in SA_TAG makes sure, that all emails have the SpamAssassin headers included. SA_TAG2 is the actual threshold to set the YES/NO flag for spam detection. SA_KILL needs to be very high, to make sure nothing is bounced at all ( SA_KILL superseeds SPAMASSASSIN_SPAM_TO_INBOX ) Make sure everything (including SPAM) is delivered to the inbox and not quarantined: SPAMASSASSIN_SPAM_TO_INBOX = 1 Use MOVE_SPAM_TO_JUNK=1 or create a sieve script which puts spam to the Junk folder: require [ \"comparator-i;ascii-numeric\" , \"relational\" , \"fileinto\" ]; if header :contains \"X-Spam-Flag\" \"YES\" { fileinto \"Junk\" ; } elsif allof ( not header :matches \"x-spam-score\" \"-*\" , header :value \"ge\" :comparator \"i;ascii-numeric\" \"x-spam-score\" \"3.75\" ) { fileinto \"Junk\" ; } Create a dedicated mailbox for emails which are infected/bad header and everything amavis is blocking by default and put its address into docker-data/dms/config/amavis.cf $clean_quarantine_to = \"amavis\\@example.com\"; $virus_quarantine_to = \"amavis\\@example.com\"; $banned_quarantine_to = \"amavis\\@example.com\"; $bad_header_quarantine_to = \"amavis\\@example.com\"; $spam_quarantine_to = \"amavis\\@example.com\";","title":"How do I have more control about what SpamAssassin is filtering?"},{"location":"introduction/","text":"What is a mail-server, and how does it perform its duty? Here's an introduction to the field that covers everything you need to know to get started with docker-mailserver . Anatomy of a Mail-Server A mail-server is only a part of a client-server relationship aimed at exchanging information in the form of emails . Exchanging emails requires using specific means (programs and protocols). docker-mailserver provides you with the server portion, whereas the client can be anything from a terminal via text-based software (eg. Mutt ) to a fully-fledged desktop application (eg. Mozilla Thunderbird , Microsoft Outlook \u2026), to a web interface, etc. Unlike the client-side where usually a single program is used to perform retrieval and viewing of emails, the server-side is composed of many specialized components. The mail-server is capable of accepting, forwarding, delivering, storing and overall exchanging messages, but each one of those tasks is actually handled by a specific piece of software. All of these \"agents\" must be integrated with one another for the exchange to take place. docker-mailserver has made informed choices about those components and their (default) configuration. It offers a comprehensive platform to run a fully featured mail-server in no time! Components The following components are required to create a complete delivery chain : MUA: a Mail User Agent is basically any client/program capable of sending emails to a mail-server; while also capable of fetching emails from a mail-server for presenting them to the end users. MTA: a Mail Transfer Agent is the so-called \"mail-server\" as seen from the MUA's perspective. It's a piece of software dedicated to accepting submitted emails, then forwarding them-where exactly will depend on an email's final destination. If the receiving MTA is responsible for the FQDN the email is sent to, then an MTA is to forward that email to an MDA (see below). Otherwise, it is to transfer (ie. forward, relay) to another MTA, \"closer\" to the email's final destination. MDA: a Mail Delivery Agent is responsible for accepting emails from an MTA and dropping them into their recipients' mailboxes, whichever the form. Here's a schematic view of mail delivery: Sending an email: MUA ----> MTA ----> (MTA relays) ----> MDA Fetching an email: MUA <--------------------------------- MDA There may be other moving parts or sub-divisions (for instance, at several points along the chain, specialized programs may be analyzing, filtering, bouncing, editing\u2026 the exchanged emails). In a nutshell, docker-mailserver provides you with the following components: A MTA: Postfix A MDA: Dovecot A bunch of additional programs to improve security and emails processing Here's where docker-mailserver 's toochain fits within the delivery chain: docker-mailserver is here: \u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513 Sending an email: MUA ---> MTA ---> (MTA relays) ---> \u252b MTA \u256e \u2503 Fetching an email: MUA <------------------------------ \u252b MDA \u256f \u2503 \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251b Example Let's say Alice owns a Gmail account, alice@gmail.com ; and Bob owns an account on a docker-mailserver 's instance, bob@dms.io . Make sure not to conflate these two very different scenarios: A) Alice sends an email to bob@dms.io => the email is first submitted to MTA smtp.gmail.com , then relayed to MTA smtp.dms.io where it is then delivered into Bob's mailbox. B) Bob sends an email to alice@gmail.com => the email is first submitted to MTA smtp.dms.io , then relayed to MTA smtp.gmail.com and eventually delivered into Alice's mailbox. In scenario A the email leaves Gmail's premises, that email's initial submission is not handled by your docker-mailserver instance(MTA); it merely receives the email after it has been relayed by Gmail's MTA. In scenario B , the docker-mailserver instance(MTA) handles the submission, prior to relaying. The main takeaway is that when a third-party sends an email to a docker-mailserver instance(MTA) (or any MTA for that matter), it does not establish a direct connection with that MTA. Email submission first goes through the sender's MTA, then some relaying between at least two MTAs is required to deliver the email. That will prove very important when it comes to security management. One important thing to note is that MTA and MDA programs may actually handle multiple tasks (which is the case with docker-mailserver 's Postfix and Dovecot). For instance, Postfix is both an SMTP server (accepting emails) and a relaying MTA (transferring, ie. sending emails to other MTA/MDA); Dovecot is both an MDA (delivering emails in mailboxes) and an IMAP server (allowing MUAs to fetch emails from the mail-server ). On top of that, Postfix may rely on Dovecot's authentication capabilities. The exact relationship between all the components and their respective (sometimes shared) responsibilities is beyond the scope of this document. Please explore this wiki & the web to get more insights about docker-mailserver 's toolchain. About Security & Ports In the previous section, different components were outlined. Each one of those is responsible for a specific task, it has a specific purpose. Three main purposes exist when it comes to exchanging emails: Submission : for a MUA (client), the act of sending actual email data over the network, toward an MTA (server). Transfer (aka. Relay ): for an MTA, the act of sending actual email data over the network, toward another MTA (server) closer to the final destination (where an MTA will forward data to an MDA). Retrieval : for a MUA (client), the act of fetching actual email data over the network, from an MDA. Postfix handles Submission (and might handle Relay), whereas Dovecot handles Retrieval. They both need to be accessible by MUAs in order to act as servers, therefore they expose public endpoints on specific TCP ports (see. Understanding the ports for more details). Those endpoints may be secured, using an encryption scheme and TLS certificates. When it comes to the specifics of email exchange, we have to look at protocols and ports enabled to support all the identified purposes. There are several valid options and they've been evolving overtime. Here's docker-mailserver 's default configuration: Purpose Protocol TCP port / encryption Transfer/Relay SMTP 25 (unencrypted) Submission ESMTP 587 (encrypted using STARTTLS) Retrieval IMAP4 143 (encrypted using STARTTLS) + 993 (TLS) Retrieval POP3 Not activated \u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Submission \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Transfer/Relay \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2510 MUA ----- STARTTLS ---> \u2524(587) MTA \u256e (25)\u251c <-- cleartext ---> \u250a Third-party MTA \u250a ---- cleartext ---> \u2524(25) \u2502 | \u2514\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2518 |\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504| MUA <---- STARTTLS ---- \u2524(143) MDA \u256f | <-- enforced TLS -- \u2524(993) | \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Retrieval \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251b If you're new to email infrastructure, both that table and the schema may be confusing. Read on to expand your understanding and learn about docker-mailserver 's configuration, including how you can customize it. Submission - SMTP For a MUA to send an email to an MTA, it needs to establish a connection with that server, then push data packets over a network that both the MUA (client) and the MTA (server) are connected to. The server implements the SMTP protocol, which makes it capable of handling Submission . In the case of docker-mailserver , the MTA (SMTP server) is Postfix. The MUA (client) may vary, yet its Submission request is performed as TCP packets sent over the public internet. This exchange of information may be secured in order to counter eavesdropping. Two kinds of Submission Let's say I own an account on a docker-mailserver instance, me@dms.io . There are two very different use-cases for Submission: I want to send an email to someone Someone wants to send you an email In the first scenario, I will be submitting my email directly to my docker-mailserver instance/MTA (Postfix), which will then relay the email to its recipient's MTA for final delivery. In this case, Submission is first handled by establishing a direct connection to my own MTA-so at least for this portion of the delivery chain, I'll be able to ensure security/confidentiality. Not so much for what comes next, ie. relaying between MTAs and final delivery. In the second scenario, a third-party email account owner will be first submitting an email to some third-party MTA. I have no control over this initial portion of the delivery chain, nor do I have control over the relaying that comes next. My MTA will merely accept a relayed email coming \"out of the blue\". My MTA will thus have to support two kinds of Submission: Outward Submission (self-owned email is submitted directly to the MTA, then is relayed \"outside\") Inward Submission (third-party email has been submitted & relayed, then is accepted \"inside\" by the MTA) \u250f\u2501\u2501\u2501\u2501 Outward Submission \u2501\u2501\u2501\u2501\u2513 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2510 Me ---------------> \u2524 \u251c -----------------> \u250a \u250a \u2502 My MTA \u2502 \u250a Third-party MTA \u250a \u2502 \u251c <----------------- \u250a \u250a \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2518 \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Inward Submission \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251b Outward Submission The best practice as of 2020 when it comes to securing Outward Submission is to use Implicit TLS connection via ESMTP on port 465 (see RFC 8314 ). Let's break it down. Implicit TLS means the server enforces the client into using an encrypted TCP connection, using TLS . With this kind of connection, the MUA has to establish a TLS-encrypted connection from the get go (TLS is implied, hence the name \"Implicit\"). Any client attempting to either submit email in cleartext (unencrypted, not secure), or requesting a cleartext connection to be upgraded to a TLS-encrypted one using STARTTLS , is to be denied. Implicit TLS is sometimes called Enforced TLS for that reason. ESMTP is SMTP + extensions. It's the version of the SMTP protocol that a mail-server commonly communicates with today. For the purpose of this documentation, ESMTP and SMTP are synonymous. Port 465 is the reserved TCP port for Implicit TLS Submission (since 2018). There is actually a boisterous history to that ports usage, but let's keep it simple. Warning This Submission setup is sometimes referred to as SMTPS . Long story short: this is incorrect and should be avoided. Although a very satisfactory setup, Implicit TLS on port 465 is somewhat \"cutting edge\". There exists another well established mail Submission setup that must be supported as well, SMTP+STARTTLS on port 587. It uses Explicit TLS: the client starts with a cleartext connection, then the server informs a TLS-encrypted \"upgraded\" connection may be established, and the client may eventually decide to establish it prior to the Submission. Basically it's an opportunistic, opt-in TLS upgrade of the connection between the client and the server, at the client's discretion, using a mechanism known as STARTTLS that both ends need to implement. In many implementations, the mail-server doesn't enforce TLS encryption, for backwards compatibility. Clients are thus free to deny the TLS-upgrade proposal (or misled by a hacker about STARTTLS not being available), and the server accepts unencrypted (cleartext) mail exchange, which poses a confidentiality threat and, to some extent, spam issues. RFC 8314 (section 3.3) recommends for a mail-server to support both Implicit and Explicit TLS for Submission, and to enforce TLS-encryption on ports 587 (Explicit TLS) and 465 (Implicit TLS). That's exactly docker-mailserver 's default configuration: abiding by RFC 8314, it enforces a strict ( encrypt ) STARTTLS policy , where a denied TLS upgrade terminates the connection thus (hopefully but at the client's discretion) preventing unencrypted (cleartext) Submission. docker-mailserver 's default configuration enables and requires Explicit TLS (STARTTLS) on port 587 for Outward Submission. It does not enable Implicit TLS Outward Submission on port 465 by default. One may enable it through simple custom configuration, either as a replacement or (better!) supplementary mean of secure Submission. It does not support old MUAs (clients) not supporting TLS encryption on ports 587/465 (those should perform Submission on port 25, more details below). One may relax that constraint through advanced custom configuration, for backwards compatibility. A final Outward Submission setup exists and is akin SMTP+STARTTLS on port 587, but on port 25. That port has historically been reserved specifically for unencrypted (cleartext) mail exchange though, making STARTTLS a bit wrong to use. As is expected by RFC 5321 , docker-mailserver uses port 25 for unencrypted Submission in order to support older clients, but most importantly for unencrypted Transfer/Relay between MTAs. docker-mailserver 's default configuration also enables unencrypted (cleartext) on port 25 for Outward Submission. It does not enable Explicit TLS (STARTTLS) on port 25 by default. One may enable it through advanced custom configuration, either as a replacement (bad!) or as a supplementary mean of secure Outward Submission. One may also secure Outward Submission using advanced encryption scheme, such as DANE/DNSSEC and/or MTA-STS. Inward Submission Granted it's still very difficult enforcing encryption between MTAs (Transfer/Relay) without risking dropping emails (when relayed by MTAs not supporting TLS-encryption), Inward Submission is to be handled in cleartext on port 25 by default. docker-mailserver 's default configuration enables unencrypted (cleartext) on port 25 for Inward Submission. It does not enable Explicit TLS (STARTTLS) on port 25 by default. One may enable it through advanced custom configuration, either as a replacement (bad!) or as a supplementary mean of secure Inward Submission. One may also secure Inward Submission using advanced encryption scheme, such as DANE/DNSSEC and/or MTA-STS. Overall, docker-mailserver 's default configuration for SMTP looks like this: \u250f\u2501\u2501\u2501\u2501 Outward Submission \u2501\u2501\u2501\u2501\u2513 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2510 Me -- cleartext --> \u2524(25) (25)\u251c --- cleartext ---> \u250a \u250a Me -- STARTTLS ---> \u2524(587) My MTA \u2502 \u250a Third-party MTA \u250a \u2502 (25)\u251c <---cleartext ---- \u250a \u250a \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2518 \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Inward Submission \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251b Retrieval - IMAP A MUA willing to fetch an email from a mail-server will most likely communicate with its IMAP server. As with SMTP described earlier, communication will take place in the form of data packets exchanged over a network that both the client and the server are connected to. The IMAP protocol makes the server capable of handling Retrieval . In the case of docker-mailserver , the IMAP server is Dovecot. The MUA (client) may vary, yet its Retrieval request is performed as TCP packets sent over the public internet. This exchange of information may be secured in order to counter eavesdropping. Again, as with SMTP described earlier, the IMAP protocol may be secured with either Implicit TLS (aka. IMAPS / IMAP4S) or Explicit TLS (using STARTTLS). The best practice as of 2020 is to enforce IMAPS on port 993, rather than IMAP+STARTTLS on port 143 (see RFC 8314 ); yet the latter is usually provided for backwards compatibility. docker-mailserver 's default configuration enables both Implicit and Explicit TLS for Retrievial, on ports 993 and 143 respectively. Retrieval - POP3 Similarly to IMAP, the older POP3 protocol may be secured with either Implicit or Explicit TLS. The best practice as of 2020 would be POP3S on port 995, rather than POP3 +STARTTLS on port 110 (see RFC 8314 ). docker-mailserver 's default configuration disables POP3 altogether. One should expect MUAs to use TLS-encrypted IMAP for Retrieval. How does docker-mailserver help with setting everything up? As a batteries included Docker image, docker-mailserver provides you with all the required components and a default configuration, to run a decent and secure mail-server. One may then customize all aspects of its internal components. Simple customization is supported through docker-compose configuration and the env-mailserver configuration file. Advanced customization is supported through providing \"monkey-patching\" configuration files and/or deriving your own image from docker-mailserver 's upstream, for a complete control over how things run. On the subject of security, one might consider docker-mailserver 's default configuration to not be 100% secure: it enables unencrypted traffic on port 25 it enables Explicit TLS (STARTTLS) on port 587, instead of Implicit TLS on port 465 We believe docker-mailserver 's default configuration to be a good middle ground: it goes slightly beyond \"old\" (1999) RFC 2487 ; and with developer friendly configuration settings, it makes it pretty easy to abide by the \"newest\" (2018) RFC 8314 . Eventually, it is up to you deciding exactly what kind of transportation/encryption to use and/or enforce, and to customize your instance accordingly (with looser or stricter security). Be also aware that protocols and ports on your server can only go so far with security; third-party MTAs might relay your emails on insecure connections, man-in-the-middle attacks might still prove effective, etc. Advanced counter-measure such as DANE, MTA-STS and/or full body encryption (eg. PGP) should be considered as well for increased confidentiality, but ideally without compromising backwards compatibility so as to not block emails. The README is the best starting point in configuring and running your mail-server. You may then explore this wiki to cover additional topics, including but not limited to, security.","title":"Introduction"},{"location":"introduction/#anatomy-of-a-mail-server","text":"A mail-server is only a part of a client-server relationship aimed at exchanging information in the form of emails . Exchanging emails requires using specific means (programs and protocols). docker-mailserver provides you with the server portion, whereas the client can be anything from a terminal via text-based software (eg. Mutt ) to a fully-fledged desktop application (eg. Mozilla Thunderbird , Microsoft Outlook \u2026), to a web interface, etc. Unlike the client-side where usually a single program is used to perform retrieval and viewing of emails, the server-side is composed of many specialized components. The mail-server is capable of accepting, forwarding, delivering, storing and overall exchanging messages, but each one of those tasks is actually handled by a specific piece of software. All of these \"agents\" must be integrated with one another for the exchange to take place. docker-mailserver has made informed choices about those components and their (default) configuration. It offers a comprehensive platform to run a fully featured mail-server in no time!","title":"Anatomy of a Mail-Server"},{"location":"introduction/#components","text":"The following components are required to create a complete delivery chain : MUA: a Mail User Agent is basically any client/program capable of sending emails to a mail-server; while also capable of fetching emails from a mail-server for presenting them to the end users. MTA: a Mail Transfer Agent is the so-called \"mail-server\" as seen from the MUA's perspective. It's a piece of software dedicated to accepting submitted emails, then forwarding them-where exactly will depend on an email's final destination. If the receiving MTA is responsible for the FQDN the email is sent to, then an MTA is to forward that email to an MDA (see below). Otherwise, it is to transfer (ie. forward, relay) to another MTA, \"closer\" to the email's final destination. MDA: a Mail Delivery Agent is responsible for accepting emails from an MTA and dropping them into their recipients' mailboxes, whichever the form. Here's a schematic view of mail delivery: Sending an email: MUA ----> MTA ----> (MTA relays) ----> MDA Fetching an email: MUA <--------------------------------- MDA There may be other moving parts or sub-divisions (for instance, at several points along the chain, specialized programs may be analyzing, filtering, bouncing, editing\u2026 the exchanged emails). In a nutshell, docker-mailserver provides you with the following components: A MTA: Postfix A MDA: Dovecot A bunch of additional programs to improve security and emails processing Here's where docker-mailserver 's toochain fits within the delivery chain: docker-mailserver is here: \u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513 Sending an email: MUA ---> MTA ---> (MTA relays) ---> \u252b MTA \u256e \u2503 Fetching an email: MUA <------------------------------ \u252b MDA \u256f \u2503 \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251b Example Let's say Alice owns a Gmail account, alice@gmail.com ; and Bob owns an account on a docker-mailserver 's instance, bob@dms.io . Make sure not to conflate these two very different scenarios: A) Alice sends an email to bob@dms.io => the email is first submitted to MTA smtp.gmail.com , then relayed to MTA smtp.dms.io where it is then delivered into Bob's mailbox. B) Bob sends an email to alice@gmail.com => the email is first submitted to MTA smtp.dms.io , then relayed to MTA smtp.gmail.com and eventually delivered into Alice's mailbox. In scenario A the email leaves Gmail's premises, that email's initial submission is not handled by your docker-mailserver instance(MTA); it merely receives the email after it has been relayed by Gmail's MTA. In scenario B , the docker-mailserver instance(MTA) handles the submission, prior to relaying. The main takeaway is that when a third-party sends an email to a docker-mailserver instance(MTA) (or any MTA for that matter), it does not establish a direct connection with that MTA. Email submission first goes through the sender's MTA, then some relaying between at least two MTAs is required to deliver the email. That will prove very important when it comes to security management. One important thing to note is that MTA and MDA programs may actually handle multiple tasks (which is the case with docker-mailserver 's Postfix and Dovecot). For instance, Postfix is both an SMTP server (accepting emails) and a relaying MTA (transferring, ie. sending emails to other MTA/MDA); Dovecot is both an MDA (delivering emails in mailboxes) and an IMAP server (allowing MUAs to fetch emails from the mail-server ). On top of that, Postfix may rely on Dovecot's authentication capabilities. The exact relationship between all the components and their respective (sometimes shared) responsibilities is beyond the scope of this document. Please explore this wiki & the web to get more insights about docker-mailserver 's toolchain.","title":"Components"},{"location":"introduction/#about-security-ports","text":"In the previous section, different components were outlined. Each one of those is responsible for a specific task, it has a specific purpose. Three main purposes exist when it comes to exchanging emails: Submission : for a MUA (client), the act of sending actual email data over the network, toward an MTA (server). Transfer (aka. Relay ): for an MTA, the act of sending actual email data over the network, toward another MTA (server) closer to the final destination (where an MTA will forward data to an MDA). Retrieval : for a MUA (client), the act of fetching actual email data over the network, from an MDA. Postfix handles Submission (and might handle Relay), whereas Dovecot handles Retrieval. They both need to be accessible by MUAs in order to act as servers, therefore they expose public endpoints on specific TCP ports (see. Understanding the ports for more details). Those endpoints may be secured, using an encryption scheme and TLS certificates. When it comes to the specifics of email exchange, we have to look at protocols and ports enabled to support all the identified purposes. There are several valid options and they've been evolving overtime. Here's docker-mailserver 's default configuration: Purpose Protocol TCP port / encryption Transfer/Relay SMTP 25 (unencrypted) Submission ESMTP 587 (encrypted using STARTTLS) Retrieval IMAP4 143 (encrypted using STARTTLS) + 993 (TLS) Retrieval POP3 Not activated \u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Submission \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Transfer/Relay \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2510 MUA ----- STARTTLS ---> \u2524(587) MTA \u256e (25)\u251c <-- cleartext ---> \u250a Third-party MTA \u250a ---- cleartext ---> \u2524(25) \u2502 | \u2514\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2518 |\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504| MUA <---- STARTTLS ---- \u2524(143) MDA \u256f | <-- enforced TLS -- \u2524(993) | \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Retrieval \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251b If you're new to email infrastructure, both that table and the schema may be confusing. Read on to expand your understanding and learn about docker-mailserver 's configuration, including how you can customize it.","title":"About Security & Ports"},{"location":"introduction/#submission-smtp","text":"For a MUA to send an email to an MTA, it needs to establish a connection with that server, then push data packets over a network that both the MUA (client) and the MTA (server) are connected to. The server implements the SMTP protocol, which makes it capable of handling Submission . In the case of docker-mailserver , the MTA (SMTP server) is Postfix. The MUA (client) may vary, yet its Submission request is performed as TCP packets sent over the public internet. This exchange of information may be secured in order to counter eavesdropping.","title":"Submission - SMTP"},{"location":"introduction/#two-kinds-of-submission","text":"Let's say I own an account on a docker-mailserver instance, me@dms.io . There are two very different use-cases for Submission: I want to send an email to someone Someone wants to send you an email In the first scenario, I will be submitting my email directly to my docker-mailserver instance/MTA (Postfix), which will then relay the email to its recipient's MTA for final delivery. In this case, Submission is first handled by establishing a direct connection to my own MTA-so at least for this portion of the delivery chain, I'll be able to ensure security/confidentiality. Not so much for what comes next, ie. relaying between MTAs and final delivery. In the second scenario, a third-party email account owner will be first submitting an email to some third-party MTA. I have no control over this initial portion of the delivery chain, nor do I have control over the relaying that comes next. My MTA will merely accept a relayed email coming \"out of the blue\". My MTA will thus have to support two kinds of Submission: Outward Submission (self-owned email is submitted directly to the MTA, then is relayed \"outside\") Inward Submission (third-party email has been submitted & relayed, then is accepted \"inside\" by the MTA) \u250f\u2501\u2501\u2501\u2501 Outward Submission \u2501\u2501\u2501\u2501\u2513 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2510 Me ---------------> \u2524 \u251c -----------------> \u250a \u250a \u2502 My MTA \u2502 \u250a Third-party MTA \u250a \u2502 \u251c <----------------- \u250a \u250a \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2518 \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Inward Submission \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251b","title":"Two kinds of Submission"},{"location":"introduction/#outward-submission","text":"The best practice as of 2020 when it comes to securing Outward Submission is to use Implicit TLS connection via ESMTP on port 465 (see RFC 8314 ). Let's break it down. Implicit TLS means the server enforces the client into using an encrypted TCP connection, using TLS . With this kind of connection, the MUA has to establish a TLS-encrypted connection from the get go (TLS is implied, hence the name \"Implicit\"). Any client attempting to either submit email in cleartext (unencrypted, not secure), or requesting a cleartext connection to be upgraded to a TLS-encrypted one using STARTTLS , is to be denied. Implicit TLS is sometimes called Enforced TLS for that reason. ESMTP is SMTP + extensions. It's the version of the SMTP protocol that a mail-server commonly communicates with today. For the purpose of this documentation, ESMTP and SMTP are synonymous. Port 465 is the reserved TCP port for Implicit TLS Submission (since 2018). There is actually a boisterous history to that ports usage, but let's keep it simple. Warning This Submission setup is sometimes referred to as SMTPS . Long story short: this is incorrect and should be avoided. Although a very satisfactory setup, Implicit TLS on port 465 is somewhat \"cutting edge\". There exists another well established mail Submission setup that must be supported as well, SMTP+STARTTLS on port 587. It uses Explicit TLS: the client starts with a cleartext connection, then the server informs a TLS-encrypted \"upgraded\" connection may be established, and the client may eventually decide to establish it prior to the Submission. Basically it's an opportunistic, opt-in TLS upgrade of the connection between the client and the server, at the client's discretion, using a mechanism known as STARTTLS that both ends need to implement. In many implementations, the mail-server doesn't enforce TLS encryption, for backwards compatibility. Clients are thus free to deny the TLS-upgrade proposal (or misled by a hacker about STARTTLS not being available), and the server accepts unencrypted (cleartext) mail exchange, which poses a confidentiality threat and, to some extent, spam issues. RFC 8314 (section 3.3) recommends for a mail-server to support both Implicit and Explicit TLS for Submission, and to enforce TLS-encryption on ports 587 (Explicit TLS) and 465 (Implicit TLS). That's exactly docker-mailserver 's default configuration: abiding by RFC 8314, it enforces a strict ( encrypt ) STARTTLS policy , where a denied TLS upgrade terminates the connection thus (hopefully but at the client's discretion) preventing unencrypted (cleartext) Submission. docker-mailserver 's default configuration enables and requires Explicit TLS (STARTTLS) on port 587 for Outward Submission. It does not enable Implicit TLS Outward Submission on port 465 by default. One may enable it through simple custom configuration, either as a replacement or (better!) supplementary mean of secure Submission. It does not support old MUAs (clients) not supporting TLS encryption on ports 587/465 (those should perform Submission on port 25, more details below). One may relax that constraint through advanced custom configuration, for backwards compatibility. A final Outward Submission setup exists and is akin SMTP+STARTTLS on port 587, but on port 25. That port has historically been reserved specifically for unencrypted (cleartext) mail exchange though, making STARTTLS a bit wrong to use. As is expected by RFC 5321 , docker-mailserver uses port 25 for unencrypted Submission in order to support older clients, but most importantly for unencrypted Transfer/Relay between MTAs. docker-mailserver 's default configuration also enables unencrypted (cleartext) on port 25 for Outward Submission. It does not enable Explicit TLS (STARTTLS) on port 25 by default. One may enable it through advanced custom configuration, either as a replacement (bad!) or as a supplementary mean of secure Outward Submission. One may also secure Outward Submission using advanced encryption scheme, such as DANE/DNSSEC and/or MTA-STS.","title":"Outward Submission"},{"location":"introduction/#inward-submission","text":"Granted it's still very difficult enforcing encryption between MTAs (Transfer/Relay) without risking dropping emails (when relayed by MTAs not supporting TLS-encryption), Inward Submission is to be handled in cleartext on port 25 by default. docker-mailserver 's default configuration enables unencrypted (cleartext) on port 25 for Inward Submission. It does not enable Explicit TLS (STARTTLS) on port 25 by default. One may enable it through advanced custom configuration, either as a replacement (bad!) or as a supplementary mean of secure Inward Submission. One may also secure Inward Submission using advanced encryption scheme, such as DANE/DNSSEC and/or MTA-STS. Overall, docker-mailserver 's default configuration for SMTP looks like this: \u250f\u2501\u2501\u2501\u2501 Outward Submission \u2501\u2501\u2501\u2501\u2513 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2510 Me -- cleartext --> \u2524(25) (25)\u251c --- cleartext ---> \u250a \u250a Me -- STARTTLS ---> \u2524(587) My MTA \u2502 \u250a Third-party MTA \u250a \u2502 (25)\u251c <---cleartext ---- \u250a \u250a \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2504\u2518 \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 Inward Submission \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251b","title":"Inward Submission"},{"location":"introduction/#retrieval-imap","text":"A MUA willing to fetch an email from a mail-server will most likely communicate with its IMAP server. As with SMTP described earlier, communication will take place in the form of data packets exchanged over a network that both the client and the server are connected to. The IMAP protocol makes the server capable of handling Retrieval . In the case of docker-mailserver , the IMAP server is Dovecot. The MUA (client) may vary, yet its Retrieval request is performed as TCP packets sent over the public internet. This exchange of information may be secured in order to counter eavesdropping. Again, as with SMTP described earlier, the IMAP protocol may be secured with either Implicit TLS (aka. IMAPS / IMAP4S) or Explicit TLS (using STARTTLS). The best practice as of 2020 is to enforce IMAPS on port 993, rather than IMAP+STARTTLS on port 143 (see RFC 8314 ); yet the latter is usually provided for backwards compatibility. docker-mailserver 's default configuration enables both Implicit and Explicit TLS for Retrievial, on ports 993 and 143 respectively.","title":"Retrieval - IMAP"},{"location":"introduction/#retrieval-pop3","text":"Similarly to IMAP, the older POP3 protocol may be secured with either Implicit or Explicit TLS. The best practice as of 2020 would be POP3S on port 995, rather than POP3 +STARTTLS on port 110 (see RFC 8314 ). docker-mailserver 's default configuration disables POP3 altogether. One should expect MUAs to use TLS-encrypted IMAP for Retrieval.","title":"Retrieval - POP3"},{"location":"introduction/#how-does-docker-mailserver-help-with-setting-everything-up","text":"As a batteries included Docker image, docker-mailserver provides you with all the required components and a default configuration, to run a decent and secure mail-server. One may then customize all aspects of its internal components. Simple customization is supported through docker-compose configuration and the env-mailserver configuration file. Advanced customization is supported through providing \"monkey-patching\" configuration files and/or deriving your own image from docker-mailserver 's upstream, for a complete control over how things run. On the subject of security, one might consider docker-mailserver 's default configuration to not be 100% secure: it enables unencrypted traffic on port 25 it enables Explicit TLS (STARTTLS) on port 587, instead of Implicit TLS on port 465 We believe docker-mailserver 's default configuration to be a good middle ground: it goes slightly beyond \"old\" (1999) RFC 2487 ; and with developer friendly configuration settings, it makes it pretty easy to abide by the \"newest\" (2018) RFC 8314 . Eventually, it is up to you deciding exactly what kind of transportation/encryption to use and/or enforce, and to customize your instance accordingly (with looser or stricter security). Be also aware that protocols and ports on your server can only go so far with security; third-party MTAs might relay your emails on insecure connections, man-in-the-middle attacks might still prove effective, etc. Advanced counter-measure such as DANE, MTA-STS and/or full body encryption (eg. PGP) should be considered as well for increased confidentiality, but ideally without compromising backwards compatibility so as to not block emails. The README is the best starting point in configuring and running your mail-server. You may then explore this wiki to cover additional topics, including but not limited to, security.","title":"How does docker-mailserver help with setting everything up?"},{"location":"usage/","text":"This pages explains how to get started with DMS, basically explaining how you can use it. The procedure uses Docker Compose as a reference. In our examples, /docker-data/dms/config/ on the host is mounted to /tmp/docker-mailserver/ inside the container. Available Images / Tags - Tagging Convention CI/CD will automatically build, test and push new images to container registries. Currently, the following registries are supported: DockerHub ( docker.io/mailserver/docker-mailserver ) GitHub Container Registry ( ghcr.io/docker-mailserver/docker-mailserver ) All workflows are using the tagging convention listed below. It is subsequently applied to all images. Event Image Tags push on master edge push a tag ( v1.2.3 ) 1.2.3 , 1.2 , 1 , latest Get the Tools setup.sh Not Required Anymore Since DMS v10.2.0 , setup.sh functionality is included within the container image. The external convenience script is no longer required if you prefer using docker exec setup instead. Issue the following commands to acquire the necessary files: DMS_GITHUB_URL = 'https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master' wget \" ${ DMS_GITHUB_URL } /docker-compose.yml\" wget \" ${ DMS_GITHUB_URL } /mailserver.env\" # Optional wget \" ${ DMS_GITHUB_URL } /setup.sh\" chmod a+x ./setup.sh Create a docker-compose Environment Install the latest Docker Compose Edit docker-compose.yml to your liking substitute mail.example.com according to your FQDN if you want to use SELinux for the ./docker-data/dms/config/:/tmp/docker-mailserver/ mount, append -z or -Z Configure the mailserver container to your liking by editing mailserver.env ( Documentation ), but keep in mind this .env file: only basic VAR=VAL is supported ( do not quote your values) variable substitution is not supported (e.g. OVERRIDE_HOSTNAME=$HOSTNAME.$DOMAINNAME ) Podman Support If you're using podman, make sure to read the related documentation Get up and running First Things First Using the Correct Commands For Stopping and Starting DMS Use docker compose up / down , not docker compose start / stop . Otherwise, the container is not properly destroyed and you may experience problems during startup because of inconsistent state. Using Ctrl+C is not supported either ! You are able to get a full overview of how the configuration works by either running: ./setup.sh help which includes the options of setup.sh . docker run --rm docker.io/mailserver/docker-mailserver:latest setup help which provides you with all the information on configuration provided \"inside\" the container itself. Usage of setup.sh when no DMS Container Is Running If no DMS container is running, any ./setup.sh command will check online for the :latest image tag (the current stable release), performing a docker pull ... if necessary followed by running the command in a temporary container: $ ./setup.sh help Image 'docker.io/mailserver/docker-mailserver:latest' not found. Pulling ... SETUP(1) NAME setup - 'docker-mailserver' Administration & Configuration script ... $ docker run --rm docker.io/mailserver/docker-mailserver:latest setup help SETUP(1) NAME setup - 'docker-mailserver' Administration & Configuration script ... Starting for the first time On first start, you will need to add at least one email account (unless you're using LDAP). You have two minutes to do so, otherwise DMS will shutdown and restart. You can add accounts with the following two methods: Use setup.sh : ./setup.sh email add Run the command directly in the container: docker exec -ti setup email add You can then proceed by creating the postmaster alias and by creating DKIM keys. docker-compose up -d mailserver # you may add some more users # for SELinux, use -Z ./setup.sh [ -Z ] email add [ ] # and configure aliases, DKIM and more ./setup.sh [ -Z ] alias add postmaster@ Further Miscellaneous Steps DNS - DKIM You can (and you should) generate DKIM keys by running ./setup.sh [ -Z ] config dkim If you want to see detailed usage information, run ./setup.sh config dkim help In case you're using LDAP, the setup looks a bit different as you do not add user accounts directly. Postfix doesn't know your domain(s) and you need to provide it when configuring DKIM: ./setup.sh config dkim domain '[,]' 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 . See the documentation for more details. Custom User Changes & Patches If you'd like to change, patch or alter files or behavior of docker-mailserver , you can use a script. See this part of our documentation for a detailed explanation.","title":"Usage"},{"location":"usage/#available-images-tags-tagging-convention","text":"CI/CD will automatically build, test and push new images to container registries. Currently, the following registries are supported: DockerHub ( docker.io/mailserver/docker-mailserver ) GitHub Container Registry ( ghcr.io/docker-mailserver/docker-mailserver ) All workflows are using the tagging convention listed below. It is subsequently applied to all images. Event Image Tags push on master edge push a tag ( v1.2.3 ) 1.2.3 , 1.2 , 1 , latest","title":"Available Images / Tags - Tagging Convention"},{"location":"usage/#get-the-tools","text":"setup.sh Not Required Anymore Since DMS v10.2.0 , setup.sh functionality is included within the container image. The external convenience script is no longer required if you prefer using docker exec setup instead. Issue the following commands to acquire the necessary files: DMS_GITHUB_URL = 'https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master' wget \" ${ DMS_GITHUB_URL } /docker-compose.yml\" wget \" ${ DMS_GITHUB_URL } /mailserver.env\" # Optional wget \" ${ DMS_GITHUB_URL } /setup.sh\" chmod a+x ./setup.sh","title":"Get the Tools"},{"location":"usage/#create-a-docker-compose-environment","text":"Install the latest Docker Compose Edit docker-compose.yml to your liking substitute mail.example.com according to your FQDN if you want to use SELinux for the ./docker-data/dms/config/:/tmp/docker-mailserver/ mount, append -z or -Z Configure the mailserver container to your liking by editing mailserver.env ( Documentation ), but keep in mind this .env file: only basic VAR=VAL is supported ( do not quote your values) variable substitution is not supported (e.g. OVERRIDE_HOSTNAME=$HOSTNAME.$DOMAINNAME ) Podman Support If you're using podman, make sure to read the related documentation","title":"Create a docker-compose Environment"},{"location":"usage/#get-up-and-running","text":"","title":"Get up and running"},{"location":"usage/#first-things-first","text":"Using the Correct Commands For Stopping and Starting DMS Use docker compose up / down , not docker compose start / stop . Otherwise, the container is not properly destroyed and you may experience problems during startup because of inconsistent state. Using Ctrl+C is not supported either ! You are able to get a full overview of how the configuration works by either running: ./setup.sh help which includes the options of setup.sh . docker run --rm docker.io/mailserver/docker-mailserver:latest setup help which provides you with all the information on configuration provided \"inside\" the container itself. Usage of setup.sh when no DMS Container Is Running If no DMS container is running, any ./setup.sh command will check online for the :latest image tag (the current stable release), performing a docker pull ... if necessary followed by running the command in a temporary container: $ ./setup.sh help Image 'docker.io/mailserver/docker-mailserver:latest' not found. Pulling ... SETUP(1) NAME setup - 'docker-mailserver' Administration & Configuration script ... $ docker run --rm docker.io/mailserver/docker-mailserver:latest setup help SETUP(1) NAME setup - 'docker-mailserver' Administration & Configuration script ...","title":"First Things First"},{"location":"usage/#starting-for-the-first-time","text":"On first start, you will need to add at least one email account (unless you're using LDAP). You have two minutes to do so, otherwise DMS will shutdown and restart. You can add accounts with the following two methods: Use setup.sh : ./setup.sh email add Run the command directly in the container: docker exec -ti setup email add You can then proceed by creating the postmaster alias and by creating DKIM keys. docker-compose up -d mailserver # you may add some more users # for SELinux, use -Z ./setup.sh [ -Z ] email add [ ] # and configure aliases, DKIM and more ./setup.sh [ -Z ] alias add postmaster@ ","title":"Starting for the first time"},{"location":"usage/#further-miscellaneous-steps","text":"","title":"Further Miscellaneous Steps"},{"location":"usage/#dns-dkim","text":"You can (and you should) generate DKIM keys by running ./setup.sh [ -Z ] config dkim If you want to see detailed usage information, run ./setup.sh config dkim help In case you're using LDAP, the setup looks a bit different as you do not add user accounts directly. Postfix doesn't know your domain(s) and you need to provide it when configuring DKIM: ./setup.sh config dkim domain '[,]' 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 . See the documentation for more details.","title":"DNS - DKIM"},{"location":"usage/#custom-user-changes-patches","text":"If you'd like to change, patch or alter files or behavior of docker-mailserver , you can use a script. See this part of our documentation for a detailed explanation.","title":"Custom User Changes & Patches"},{"location":"config/environment/","text":"Info Values in bold are the default values. If an option doesn't work as documented here, check if you are running the latest image. The current master branch corresponds to the image mailserver/docker-mailserver:edge . General OVERRIDE_HOSTNAME empty => uses the hostname command to get canonical hostname for docker-mailserver to use. => Specify a fully-qualified domainname to serve mail for. This is used for many of the config features so if you can't set your hostname ( eg: you're in a container platform that doesn't let you ) specify it via this environment variable. It will take priority over docker run options: --hostname and --domainname , or docker-compose.yml config equivalents: hostname: and domainname: . DMS_DEBUG This environment variable was removed in v11.0.0 ! Use LOG_LEVEL instead. LOG_LEVEL Set the log level for DMS. This is mostly relevant for container startup scripts and change detection event feedback. Valid values (in order of increasing verbosity) are: error , warn , info , debug and trace . The default log level is info . SUPERVISOR_LOGLEVEL Here you can adjust the log-level for Supervisor . Possible values are critical => Only show critical messages error => Only show erroneous output warn => Show warnings info => Normal informational output debug => Also show debug messages The log-level will show everything in its class and above. ONE_DIR 0 => state in default directories. 1 => consolidate all states into a single directory ( /var/mail-state ) to allow persistence using docker volumes. See the related FAQ entry for more information. ACCOUNT_PROVISIONER Configures the provisioning source of user accounts (including aliases) for user queries and authentication by services managed by DMS ( Postfix and Dovecot ). User provisioning via OIDC is planned for the future, see this tracking issue . empty => use FILE LDAP => use LDAP authentication OIDC => use OIDC authentication ( not yet implemented ) FILE => use local files (this is used as the default) A second container for the ldap service is necessary (e.g. docker-openldap ) PERMIT_DOCKER Set different options for mynetworks option (can be overwrite in postfix-main.cf) WARNING : Adding the docker network's gateway to the list of trusted hosts, e.g. using the network or connected-networks option, can create an open relay , for instance if IPv6 is enabled on the host machine but not in Docker. none => Explicitly force authentication container => Container IP address only. host => Add docker host (ipv4 only). network => Add the docker default bridge network (172.16.0.0/12); WARNING : docker-compose might use others (e.g. 192.168.0.0/16) use PERMIT_DOCKER=connected-networks in this case. connected-networks => Add all connected docker networks (ipv4 only). Note: you probably want to set POSTFIX_INET_PROTOCOLS=ipv4 to make it work fine with Docker. TZ Set the timezone. If this variable is unset, the container runtime will try to detect the time using /etc/localtime , which you can alternatively mount into the container. The value of this variable must follow the pattern AREA/ZONE , i.e. of you want to use Germany's time zone, use Europe/Berlin . You can lookup all available timezones here . ENABLE_RSPAMD Enable or disable Rspamd. Current State Rspamd-support is under active development. Be aware that breaking changes can happen at any time. To get more information, see the detailed documentation page for Rspamd . 0 => disabled 1 => enabled ENABLE_REDIS Explicit control over running a Redis instance within the container. By default, this value will match what is set for ENABLE_RSPAMD . The purpose of this setting is to opt-out of starting an internal Redis instance when enabling Rspamd, replacing it with your own external instance. Configuring rspamd for an external Redis instance You will need to provide configuration at /etc/rspamd/local.d/redis.conf similar to: servers = \"redis.example.test:6379\"; expand_keys = true; 0 => Disabled 1 => Enabled ENABLE_AMAVIS Amavis content filter (used for ClamAV & SpamAssassin) 0 => Amavis is disabled 1 => Amavis is enabled AMAVIS_LOGLEVEL This page provides information on Amavis' logging statistics. -1/-2/-3 => Only show errors 0 => Show warnings 1/2 => Show default informational output 3/4/5 => log debug information (very verbose) ENABLE_DNSBL This enables DNS block lists in Postscreen . If you want to know which lists we are using, have a look at the default main.cf for Postfix we provide and search for postscreen_dnsbl_sites . A Warning On DNS Block Lists Make sure your DNS queries are properly resolved, i.e. you will most likely not want to use a public DNS resolver as these queries do not return meaningful results. We try our best to only evaluate proper return codes - this is not a guarantee that all codes are handled fine though. Note that emails will be rejected if they don't pass the block list checks! 0 => DNS block lists are disabled 1 => DNS block lists are enabled ENABLE_OPENDKIM Enables the OpenDKIM service. 1 => Enabled 0 => Disabled ENABLE_OPENDMARC Enables the OpenDMARC service. 1 => Enabled 0 => Disabled ENABLE_POP3 empty => POP3 service disabled 1 => Enables POP3 service ENABLE_CLAMAV 0 => ClamAV is disabled 1 => ClamAV is enabled ENABLE_FAIL2BAN 0 => fail2ban service disabled 1 => Enables fail2ban service If you enable Fail2Ban, don't forget to add the following lines to your docker-compose.yml : cap_add: - NET_ADMIN Otherwise, nftables won't be able to ban IPs. FAIL2BAN_BLOCKTYPE drop => drop packet (send NO reply) reject => reject packet (send ICMP unreachable) FAIL2BAN_BLOCKTYPE=drop SMTP_ONLY empty => all daemons start 1 => only launch postfix smtp SSL_TYPE In the majority of cases, you want letsencrypt or manual . self-signed can be used for testing SSL until you provide a valid certificate, note that third-parties cannot trust self-signed certificates, do not use this type in production. custom is a temporary workaround that is not officially supported. empty => SSL disabled. letsencrypt => Support for using certificates with Let's Encrypt provisioners. (Docs: Let's Encrypt Setup ) manual => Provide your own certificate via separate key and cert files. (Docs: Bring Your Own Certificates ) Requires: SSL_CERT_PATH and SSL_KEY_PATH ENV vars to be set to the location of the files within the container. Optional: SSL_ALT_CERT_PATH and SSL_ALT_KEY_PATH allow providing a 2nd certificate as a fallback for dual (aka hybrid) certificate support. Useful for ECDSA with an RSA fallback. Presently only manual mode supports this feature . custom => Provide your own certificate as a single file containing both the private key and full certificate chain. (Docs: None ) self-signed => Provide your own self-signed certificate files. Expects a self-signed CA cert for verification. Use only for local testing of your setup . (Docs: Self-Signed Certificates ) Please read the SSL page in the documentation for more information. TLS_LEVEL empty => modern modern => Enables TLSv1.2 and modern ciphers only. (default) intermediate => Enables TLSv1, TLSv1.1 and TLSv1.2 and broad compatibility ciphers. SPOOF_PROTECTION Configures the handling of creating mails with forged sender addresses. empty => Mail address spoofing allowed. Any logged in user may create email messages with a forged sender address. See also Wikipedia (not recommended, but default for backwards compatibility reasons) 1 => (recommended) Mail spoofing denied. Each user may only send with his own or his alias addresses. Addresses with extension delimiters are not able to send messages. ENABLE_SRS Enables the Sender Rewriting Scheme. SRS is needed if docker-mailserver acts as forwarder. See postsrsd for further explanation. 0 => Disabled 1 => Enabled NETWORK_INTERFACE In case your network interface differs from eth0 , e.g. when you are using HostNetworking in Kubernetes, you can set this to whatever interface you want. This interface will then be used. empty => eth0 VIRUSMAILS_DELETE_DELAY Set how many days a virusmail will stay on the server before being deleted empty => 7 days POSTFIX_DAGENT Configure Postfix virtual_transport to deliver mail to a different LMTP client ( default is a unix socket to dovecot ). Provide any valid URI. Examples: empty => lmtp:unix:/var/run/dovecot/lmtp (default, configured in Postfix main.cf ) lmtp:unix:private/dovecot-lmtp (use socket) lmtps:inet:: (secure lmtp with starttls) lmtp::2003 (use kopano as mailstore) POSTFIX_MAILBOX_SIZE_LIMIT Set the mailbox size limit for all users. If set to zero, the size will be unlimited (default). empty => 0 (no limit) ENABLE_QUOTAS 1 => Dovecot quota is enabled 0 => Dovecot quota is disabled See mailbox quota . POSTFIX_MESSAGE_SIZE_LIMIT Set the message size limit for all users. If set to zero, the size will be unlimited (not recommended!) empty => 10240000 (~10 MB) CLAMAV_MESSAGE_SIZE_LIMIT Mails larger than this limit won't be scanned. ClamAV must be enabled (ENABLE_CLAMAV=1) for this. empty => 25M (25 MB) ENABLE_MANAGESIEVE empty => Managesieve service disabled 1 => Enables Managesieve on port 4190 POSTMASTER_ADDRESS empty => postmaster@example.com => Specify the postmaster address ENABLE_UPDATE_CHECK Check for updates on container start and then once a day. If an update is available, a mail is send to POSTMASTER_ADDRESS. 0 => Update check disabled 1 => Update check enabled UPDATE_CHECK_INTERVAL Customize the update check interval. Number + Suffix. Suffix must be 's' for seconds, 'm' for minutes, 'h' for hours or 'd' for days. 1d => Check for updates once a day POSTSCREEN_ACTION enforce => Allow other tests to complete. Reject attempts to deliver mail with a 550 SMTP reply, and log the helo/sender/recipient information. Repeat this test the next time the client connects. drop => Drop the connection immediately with a 521 SMTP reply. Repeat this test the next time the client connects. ignore => Ignore the failure of this test. Allow other tests to complete. Repeat this test the next time the client connects. This option is useful for testing and collecting statistics without blocking mail. DOVECOT_MAILBOX_FORMAT maildir => uses very common Maildir format, one file contains one message sdbox => (experimental) uses Dovecot high-performance mailbox format, one file contains one message mdbox ==> (experimental) uses Dovecot high-performance mailbox format, multiple messages per file and multiple files per box This option has been added in November 2019. Using other format than Maildir is considered as experimental in docker-mailserver and should only be used for testing purpose. For more details, please refer to Dovecot Documentation . POSTFIX_INET_PROTOCOLS all => Listen on all interfaces. ipv4 => Listen only on IPv4 interfaces. Most likely you want this behind Docker. ipv6 => Listen only on IPv6 interfaces. Note: More details at http://www.postfix.org/postconf.5.html#inet_protocols DOVECOT_INET_PROTOCOLS all => Listen on all interfaces ipv4 => Listen only on IPv4 interfaces. Most likely you want this behind Docker. ipv6 => Listen only on IPv6 interfaces. Note: More information at https://dovecot.org/doc/dovecot-example.conf Reports PFLOGSUMM_TRIGGER Enables regular Postfix log summary (\"pflogsumm\") mail reports. not set => No report daily_cron => Daily report for the previous day logrotate => Full report based on the mail log when it is rotated This is a new option. The old REPORT options are still supported for backwards compatibility. If this is not set and reports are enabled with the old options, logrotate will be used. PFLOGSUMM_RECIPIENT Recipient address for Postfix log summary reports. not set => Use POSTMASTER_ADDRESS => Specify the recipient address(es) PFLOGSUMM_SENDER Sender address ( FROM ) for pflogsumm reports (if Postfix log summary reports are enabled). not set => Use REPORT_SENDER => Specify the sender address LOGWATCH_INTERVAL Interval for logwatch report. none => No report is generated daily => Send a daily report weekly => Send a report every week LOGWATCH_RECIPIENT Recipient address for logwatch reports if they are enabled. not set => Use REPORT_RECIPIENT or POSTMASTER_ADDRESS => Specify the recipient address(es) LOGWATCH_SENDER Sender address ( FROM ) for logwatch reports if logwatch reports are enabled. not set => Use REPORT_SENDER => Specify the sender address REPORT_RECIPIENT Defines who receives reports (if they are enabled). empty => Use POSTMASTER_ADDRESS => Specify the recipient address REPORT_SENDER Defines who sends reports (if they are enabled). empty => mailserver-report@ => Specify the sender address LOGROTATE_INTERVAL Changes the interval in which log files are rotated. weekly => Rotate log files weekly daily => Rotate log files daily monthly => Rotate log files monthly Note LOGROTATE_INTERVAL only manages logrotate within the container for services we manage internally. The entire log output for the container is still available via docker logs mailserver (or your respective container name). If you want to configure external log rotation for that container output as well, : Docker Logging Drivers . By default, the logs are lost when the container is destroyed (eg: re-creating via docker-compose down && docker-compose up -d ). To keep the logs, mount a volume (to /var/log/mail/ ). Note This variable can also determine the interval for Postfix's log summary reports, see PFLOGSUMM_TRIGGER . SpamAssassin ENABLE_SPAMASSASSIN 0 => SpamAssassin is disabled 1 => SpamAssassin is enabled SPAMASSASSIN_SPAM_TO_INBOX 0 => Spam messages will be bounced ( rejected ) without any notification ( dangerous ). 1 => Spam messages will be delivered to the inbox and tagged as spam using SA_SPAM_SUBJECT . ENABLE_SPAMASSASSIN_KAM KAM is a 3rd party SpamAssassin ruleset, provided by the McGrail Foundation. If SpamAssassin is enabled, KAM can be used in addition to the default ruleset. 0 => KAM disabled 1 => KAM enabled MOVE_SPAM_TO_JUNK Spam messages can be moved in the Junk folder. Note: this setting needs SPAMASSASSIN_SPAM_TO_INBOX=1 0 => Spam messages will be delivered in the mailbox. 1 => Spam messages will be delivered in the Junk folder. SA_TAG 2.0 => add spam info headers if at, or above that level Note: this SpamAssassin setting needs ENABLE_SPAMASSASSIN=1 SA_TAG2 6.31 => add 'spam detected' headers at that level Note: this SpamAssassin setting needs ENABLE_SPAMASSASSIN=1 SA_KILL 6.31 => triggers spam evasive actions This SpamAssassin setting needs ENABLE_SPAMASSASSIN=1 By default, docker-mailserver is configured to quarantine spam emails. If emails are quarantined, they are compressed and stored in a location dependent on the ONE_DIR setting above. To inhibit this behaviour and deliver spam emails, set this to a very high value e.g. 100.0 . If ONE_DIR=1 (default) the location is /var/mail-state/lib-amavis/virusmails/ , or if ONE_DIR=0 : /var/lib/amavis/virusmails/ . These paths are inside the docker container. SA_SPAM_SUBJECT ***SPAM*** => add tag to subject if spam detected Note: this SpamAssassin setting needs ENABLE_SPAMASSASSIN=1 . Add the SpamAssassin score to the subject line by inserting the keyword _SCORE_: ***SPAM(_SCORE_)*** . SA_SHORTCIRCUIT_BAYES_SPAM 1 => will activate SpamAssassin short circuiting for bayes spam detection. This will uncomment the respective line in /etc/spamassasin/local.cf Note: activate this only if you are confident in your bayes database for identifying spam. SA_SHORTCIRCUIT_BAYES_HAM 1 => will activate SpamAssassin short circuiting for bayes ham detection This will uncomment the respective line in /etc/spamassasin/local.cf Note: activate this only if you are confident in your bayes database for identifying ham. Fetchmail ENABLE_FETCHMAIL 0 => fetchmail disabled 1 => fetchmail enabled FETCHMAIL_POLL 300 => fetchmail The number of seconds for the interval FETCHMAIL_PARALLEL 0 => fetchmail runs with a single config file /etc/fetchmailrc 1 => /etc/fetchmailrc is split per poll entry. For every poll entry a separate fetchmail instance is started to allow having multiple imap idle configurations defined. Note: The defaults of your fetchmailrc file need to be at the top of the file. Otherwise it won't be added correctly to all separate fetchmail instances. LDAP ENABLE_LDAP Deprecated. See ACCOUNT_PROVISIONER . LDAP_START_TLS empty => no yes => LDAP over TLS enabled for Postfix LDAP_SERVER_HOST empty => mail.example.com => Specify the dns-name/ip-address where the ldap-server is listening, or an URI like ldaps://mail.example.com NOTE: If you going to use docker-mailserver in combination with docker-compose.yml you can set the service name here LDAP_SEARCH_BASE empty => ou=people,dc=domain,dc=com => e.g. LDAP_SEARCH_BASE=dc=mydomain,dc=local LDAP_BIND_DN empty => cn=admin,dc=domain,dc=com => take a look at examples of SASL_LDAP_BIND_DN LDAP_BIND_PW empty => admin => Specify the password to bind against ldap LDAP_QUERY_FILTER_USER e.g. (&(mail=%s)(mailEnabled=TRUE)) => Specify how ldap should be asked for users LDAP_QUERY_FILTER_GROUP e.g. (&(mailGroupMember=%s)(mailEnabled=TRUE)) => Specify how ldap should be asked for groups LDAP_QUERY_FILTER_ALIAS e.g. (&(mailAlias=%s)(mailEnabled=TRUE)) => Specify how ldap should be asked for aliases LDAP_QUERY_FILTER_DOMAIN e.g. (&(|(mail=*@%s)(mailalias=*@%s)(mailGroupMember=*@%s))(mailEnabled=TRUE)) => Specify how ldap should be asked for domains LDAP_QUERY_FILTER_SENDERS empty => use user/alias/group maps directly, equivalent to (|($LDAP_QUERY_FILTER_USER)($LDAP_QUERY_FILTER_ALIAS)($LDAP_QUERY_FILTER_GROUP)) => Override how ldap should be asked if a sender address is allowed for a user DOVECOT_TLS empty => no yes => LDAP over TLS enabled for Dovecot Dovecot The following variables overwrite the default values for /etc/dovecot/dovecot-ldap.conf.ext . DOVECOT_BASE empty => same as LDAP_SEARCH_BASE => Tell Dovecot to search only below this base entry. (e.g. ou=people,dc=domain,dc=com ) DOVECOT_DEFAULT_PASS_SCHEME empty => SSHA => Select one crypt scheme for password hashing from this list of password schemes . DOVECOT_DN empty => same as LDAP_BIND_DN => Bind dn for LDAP connection. (e.g. cn=admin,dc=domain,dc=com ) DOVECOT_DNPASS empty => same as LDAP_BIND_PW => Password for LDAP dn sepecifified in DOVECOT_DN . DOVECOT_URIS empty => same as LDAP_SERVER_HOST => Specify a space separated list of LDAP uris. Note: If the protocol is missing, ldap:// will be used. Note: This deprecates DOVECOT_HOSTS (as it didn't allow to use LDAPS), which is currently still supported for backwards compatibility. DOVECOT_LDAP_VERSION empty => 3 2 => LDAP version 2 is used 3 => LDAP version 3 is used DOVECOT_AUTH_BIND empty => no yes => Enable LDAP authentication binds DOVECOT_USER_FILTER e.g. (&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n)) DOVECOT_USER_ATTRS e.g. homeDirectory=home,qmailUID=uid,qmailGID=gid,mailMessageStore=mail => Specify the directory to dovecot attribute mapping that fits your directory structure. Note: This is necessary for directories that do not use the Postfix Book Schema. Note: The left-hand value is the directory attribute, the right hand value is the dovecot variable. More details on the Dovecot Wiki DOVECOT_PASS_FILTER e.g. (&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n)) empty => same as DOVECOT_USER_FILTER DOVECOT_PASS_ATTRS e.g. uid=user,userPassword=password => Specify the directory to dovecot variable mapping that fits your directory structure. Note: This is necessary for directories that do not use the Postfix Book Schema. Note: The left-hand value is the directory attribute, the right hand value is the dovecot variable. More details on the Dovecot Wiki Postgrey ENABLE_POSTGREY 0 => postgrey is disabled 1 => postgrey is enabled POSTGREY_DELAY 300 => greylist for N seconds Note: This postgrey setting needs ENABLE_POSTGREY=1 POSTGREY_MAX_AGE 35 => delete entries older than N days since the last time that they have been seen Note: This postgrey setting needs ENABLE_POSTGREY=1 POSTGREY_AUTO_WHITELIST_CLIENTS 5 => whitelist host after N successful deliveries (N=0 to disable whitelisting) Note: This postgrey setting needs ENABLE_POSTGREY=1 POSTGREY_TEXT Delayed by Postgrey => response when a mail is greylisted Note: This postgrey setting needs ENABLE_POSTGREY=1 SASL Auth ENABLE_SASLAUTHD 0 => saslauthd is disabled 1 => saslauthd is enabled SASLAUTHD_MECHANISMS empty => pam ldap => authenticate against ldap server shadow => authenticate against local user db mysql => authenticate against mysql db rimap => authenticate against imap server NOTE: can be a list of mechanisms like pam ldap shadow SASLAUTHD_MECH_OPTIONS empty => None e.g. with SASLAUTHD_MECHANISMS rimap you need to specify the ip-address/servername of the imap server ==> xxx.xxx.xxx.xxx SASLAUTHD_LDAP_SERVER empty => same as LDAP_SERVER_HOST Note: since version 10.0.0, you can specify a protocol here (like ldaps://); this deprecates SASLAUTHD_LDAP_SSL. SASLAUTHD_LDAP_START_TLS empty => no yes => Enable ldap_start_tls option SASLAUTHD_LDAP_TLS_CHECK_PEER empty => no yes => Enable ldap_tls_check_peer option SASLAUTHD_LDAP_TLS_CACERT_DIR Path to directory with CA (Certificate Authority) certificates. empty => Nothing is added to the configuration Any value => Fills the ldap_tls_cacert_dir option SASLAUTHD_LDAP_TLS_CACERT_FILE File containing CA (Certificate Authority) certificate(s). empty => Nothing is added to the configuration Any value => Fills the ldap_tls_cacert_file option SASLAUTHD_LDAP_BIND_DN empty => same as LDAP_BIND_DN specify an object with privileges to search the directory tree e.g. active directory: SASLAUTHD_LDAP_BIND_DN=cn=Administrator,cn=Users,dc=mydomain,dc=net e.g. openldap: SASLAUTHD_LDAP_BIND_DN=cn=admin,dc=mydomain,dc=net SASLAUTHD_LDAP_PASSWORD empty => same as LDAP_BIND_PW SASLAUTHD_LDAP_SEARCH_BASE empty => same as LDAP_SEARCH_BASE specify the search base SASLAUTHD_LDAP_FILTER empty => default filter (&(uniqueIdentifier=%u)(mailEnabled=TRUE)) e.g. for active directory: (&(sAMAccountName=%U)(objectClass=person)) e.g. for openldap: (&(uid=%U)(objectClass=person)) SASLAUTHD_LDAP_PASSWORD_ATTR Specify what password attribute to use for password verification. empty => Nothing is added to the configuration but the documentation says it is userPassword by default. Any value => Fills the ldap_password_attr option SASLAUTHD_LDAP_AUTH_METHOD empty => bind will be used as a default value fastbind => The fastbind method is used custom => The custom method uses userPassword attribute to verify the password SASLAUTHD_LDAP_MECH Specify the authentication mechanism for SASL bind. empty => Nothing is added to the configuration Any value => Fills the ldap_mech option SRS (Sender Rewriting Scheme) SRS_SENDER_CLASSES An email has an \"envelope\" sender (indicating the sending server) and a \"header\" sender (indicating who sent it). More strict SPF policies may require you to replace both instead of just the envelope sender. More info . envelope_sender => Rewrite only envelope sender address header_sender => Rewrite only header sender (not recommended) envelope_sender,header_sender => Rewrite both senders SRS_EXCLUDE_DOMAINS empty => Envelope sender will be rewritten for all domains provide comma separated list of domains to exclude from rewriting SRS_SECRET empty => generated when the container is started for the first time provide a secret to use in base64 you may specify multiple keys, comma separated. the first one is used for signing and the remaining will be used for verification. this is how you rotate and expire keys if you have a cluster/swarm make sure the same keys are on all nodes example command to generate a key: dd if=/dev/urandom bs=24 count=1 2>/dev/null | base64 SRS_DOMAINNAME empty => Derived from OVERRIDE_HOSTNAME , $DOMAINNAME (internal), or the container's hostname Set this if auto-detection fails, isn't what you want, or you wish to have a separate container handle DSNs Default Relay Host DEFAULT_RELAY_HOST empty => don't set default relayhost setting in main.cf default host and port to relay all mail through. Format: [example.com]:587 (don't forget the brackets if you need this to be compatible with $RELAY_USER and $RELAY_PASSWORD , explained below). Multi-domain Relay Hosts RELAY_HOST empty => don't configure relay host default host to relay mail through RELAY_PORT empty => 25 default port to relay mail through RELAY_USER empty => no default default relay username (if no specific entry exists in postfix-sasl-password.cf) RELAY_PASSWORD empty => no default password for default relay user","title":"Environment Variables"},{"location":"config/environment/#general","text":"","title":"General"},{"location":"config/environment/#override_hostname","text":"empty => uses the hostname command to get canonical hostname for docker-mailserver to use. => Specify a fully-qualified domainname to serve mail for. This is used for many of the config features so if you can't set your hostname ( eg: you're in a container platform that doesn't let you ) specify it via this environment variable. It will take priority over docker run options: --hostname and --domainname , or docker-compose.yml config equivalents: hostname: and domainname: .","title":"OVERRIDE_HOSTNAME"},{"location":"config/environment/#dms_debug","text":"This environment variable was removed in v11.0.0 ! Use LOG_LEVEL instead.","title":"DMS_DEBUG"},{"location":"config/environment/#log_level","text":"Set the log level for DMS. This is mostly relevant for container startup scripts and change detection event feedback. Valid values (in order of increasing verbosity) are: error , warn , info , debug and trace . The default log level is info .","title":"LOG_LEVEL"},{"location":"config/environment/#supervisor_loglevel","text":"Here you can adjust the log-level for Supervisor . Possible values are critical => Only show critical messages error => Only show erroneous output warn => Show warnings info => Normal informational output debug => Also show debug messages The log-level will show everything in its class and above.","title":"SUPERVISOR_LOGLEVEL"},{"location":"config/environment/#one_dir","text":"0 => state in default directories. 1 => consolidate all states into a single directory ( /var/mail-state ) to allow persistence using docker volumes. See the related FAQ entry for more information.","title":"ONE_DIR"},{"location":"config/environment/#account_provisioner","text":"Configures the provisioning source of user accounts (including aliases) for user queries and authentication by services managed by DMS ( Postfix and Dovecot ). User provisioning via OIDC is planned for the future, see this tracking issue . empty => use FILE LDAP => use LDAP authentication OIDC => use OIDC authentication ( not yet implemented ) FILE => use local files (this is used as the default) A second container for the ldap service is necessary (e.g. docker-openldap )","title":"ACCOUNT_PROVISIONER"},{"location":"config/environment/#permit_docker","text":"Set different options for mynetworks option (can be overwrite in postfix-main.cf) WARNING : Adding the docker network's gateway to the list of trusted hosts, e.g. using the network or connected-networks option, can create an open relay , for instance if IPv6 is enabled on the host machine but not in Docker. none => Explicitly force authentication container => Container IP address only. host => Add docker host (ipv4 only). network => Add the docker default bridge network (172.16.0.0/12); WARNING : docker-compose might use others (e.g. 192.168.0.0/16) use PERMIT_DOCKER=connected-networks in this case. connected-networks => Add all connected docker networks (ipv4 only). Note: you probably want to set POSTFIX_INET_PROTOCOLS=ipv4 to make it work fine with Docker.","title":"PERMIT_DOCKER"},{"location":"config/environment/#tz","text":"Set the timezone. If this variable is unset, the container runtime will try to detect the time using /etc/localtime , which you can alternatively mount into the container. The value of this variable must follow the pattern AREA/ZONE , i.e. of you want to use Germany's time zone, use Europe/Berlin . You can lookup all available timezones here .","title":"TZ"},{"location":"config/environment/#enable_rspamd","text":"Enable or disable Rspamd. Current State Rspamd-support is under active development. Be aware that breaking changes can happen at any time. To get more information, see the detailed documentation page for Rspamd . 0 => disabled 1 => enabled","title":"ENABLE_RSPAMD"},{"location":"config/environment/#enable_redis","text":"Explicit control over running a Redis instance within the container. By default, this value will match what is set for ENABLE_RSPAMD . The purpose of this setting is to opt-out of starting an internal Redis instance when enabling Rspamd, replacing it with your own external instance. Configuring rspamd for an external Redis instance You will need to provide configuration at /etc/rspamd/local.d/redis.conf similar to: servers = \"redis.example.test:6379\"; expand_keys = true; 0 => Disabled 1 => Enabled","title":"ENABLE_REDIS"},{"location":"config/environment/#enable_amavis","text":"Amavis content filter (used for ClamAV & SpamAssassin) 0 => Amavis is disabled 1 => Amavis is enabled","title":"ENABLE_AMAVIS"},{"location":"config/environment/#amavis_loglevel","text":"This page provides information on Amavis' logging statistics. -1/-2/-3 => Only show errors 0 => Show warnings 1/2 => Show default informational output 3/4/5 => log debug information (very verbose)","title":"AMAVIS_LOGLEVEL"},{"location":"config/environment/#enable_dnsbl","text":"This enables DNS block lists in Postscreen . If you want to know which lists we are using, have a look at the default main.cf for Postfix we provide and search for postscreen_dnsbl_sites . A Warning On DNS Block Lists Make sure your DNS queries are properly resolved, i.e. you will most likely not want to use a public DNS resolver as these queries do not return meaningful results. We try our best to only evaluate proper return codes - this is not a guarantee that all codes are handled fine though. Note that emails will be rejected if they don't pass the block list checks! 0 => DNS block lists are disabled 1 => DNS block lists are enabled","title":"ENABLE_DNSBL"},{"location":"config/environment/#enable_opendkim","text":"Enables the OpenDKIM service. 1 => Enabled 0 => Disabled","title":"ENABLE_OPENDKIM"},{"location":"config/environment/#enable_opendmarc","text":"Enables the OpenDMARC service. 1 => Enabled 0 => Disabled","title":"ENABLE_OPENDMARC"},{"location":"config/environment/#enable_pop3","text":"empty => POP3 service disabled 1 => Enables POP3 service","title":"ENABLE_POP3"},{"location":"config/environment/#enable_clamav","text":"0 => ClamAV is disabled 1 => ClamAV is enabled","title":"ENABLE_CLAMAV"},{"location":"config/environment/#enable_fail2ban","text":"0 => fail2ban service disabled 1 => Enables fail2ban service If you enable Fail2Ban, don't forget to add the following lines to your docker-compose.yml : cap_add: - NET_ADMIN Otherwise, nftables won't be able to ban IPs.","title":"ENABLE_FAIL2BAN"},{"location":"config/environment/#fail2ban_blocktype","text":"drop => drop packet (send NO reply) reject => reject packet (send ICMP unreachable) FAIL2BAN_BLOCKTYPE=drop","title":"FAIL2BAN_BLOCKTYPE"},{"location":"config/environment/#smtp_only","text":"empty => all daemons start 1 => only launch postfix smtp","title":"SMTP_ONLY"},{"location":"config/environment/#ssl_type","text":"In the majority of cases, you want letsencrypt or manual . self-signed can be used for testing SSL until you provide a valid certificate, note that third-parties cannot trust self-signed certificates, do not use this type in production. custom is a temporary workaround that is not officially supported. empty => SSL disabled. letsencrypt => Support for using certificates with Let's Encrypt provisioners. (Docs: Let's Encrypt Setup ) manual => Provide your own certificate via separate key and cert files. (Docs: Bring Your Own Certificates ) Requires: SSL_CERT_PATH and SSL_KEY_PATH ENV vars to be set to the location of the files within the container. Optional: SSL_ALT_CERT_PATH and SSL_ALT_KEY_PATH allow providing a 2nd certificate as a fallback for dual (aka hybrid) certificate support. Useful for ECDSA with an RSA fallback. Presently only manual mode supports this feature . custom => Provide your own certificate as a single file containing both the private key and full certificate chain. (Docs: None ) self-signed => Provide your own self-signed certificate files. Expects a self-signed CA cert for verification. Use only for local testing of your setup . (Docs: Self-Signed Certificates ) Please read the SSL page in the documentation for more information.","title":"SSL_TYPE"},{"location":"config/environment/#tls_level","text":"empty => modern modern => Enables TLSv1.2 and modern ciphers only. (default) intermediate => Enables TLSv1, TLSv1.1 and TLSv1.2 and broad compatibility ciphers.","title":"TLS_LEVEL"},{"location":"config/environment/#spoof_protection","text":"Configures the handling of creating mails with forged sender addresses. empty => Mail address spoofing allowed. Any logged in user may create email messages with a forged sender address. See also Wikipedia (not recommended, but default for backwards compatibility reasons) 1 => (recommended) Mail spoofing denied. Each user may only send with his own or his alias addresses. Addresses with extension delimiters are not able to send messages.","title":"SPOOF_PROTECTION"},{"location":"config/environment/#enable_srs","text":"Enables the Sender Rewriting Scheme. SRS is needed if docker-mailserver acts as forwarder. See postsrsd for further explanation. 0 => Disabled 1 => Enabled","title":"ENABLE_SRS"},{"location":"config/environment/#network_interface","text":"In case your network interface differs from eth0 , e.g. when you are using HostNetworking in Kubernetes, you can set this to whatever interface you want. This interface will then be used. empty => eth0","title":"NETWORK_INTERFACE"},{"location":"config/environment/#virusmails_delete_delay","text":"Set how many days a virusmail will stay on the server before being deleted empty => 7 days","title":"VIRUSMAILS_DELETE_DELAY"},{"location":"config/environment/#postfix_dagent","text":"Configure Postfix virtual_transport to deliver mail to a different LMTP client ( default is a unix socket to dovecot ). Provide any valid URI. Examples: empty => lmtp:unix:/var/run/dovecot/lmtp (default, configured in Postfix main.cf ) lmtp:unix:private/dovecot-lmtp (use socket) lmtps:inet:: (secure lmtp with starttls) lmtp::2003 (use kopano as mailstore)","title":"POSTFIX_DAGENT"},{"location":"config/environment/#postfix_mailbox_size_limit","text":"Set the mailbox size limit for all users. If set to zero, the size will be unlimited (default). empty => 0 (no limit)","title":"POSTFIX_MAILBOX_SIZE_LIMIT"},{"location":"config/environment/#enable_quotas","text":"1 => Dovecot quota is enabled 0 => Dovecot quota is disabled See mailbox quota .","title":"ENABLE_QUOTAS"},{"location":"config/environment/#postfix_message_size_limit","text":"Set the message size limit for all users. If set to zero, the size will be unlimited (not recommended!) empty => 10240000 (~10 MB)","title":"POSTFIX_MESSAGE_SIZE_LIMIT"},{"location":"config/environment/#clamav_message_size_limit","text":"Mails larger than this limit won't be scanned. ClamAV must be enabled (ENABLE_CLAMAV=1) for this. empty => 25M (25 MB)","title":"CLAMAV_MESSAGE_SIZE_LIMIT"},{"location":"config/environment/#enable_managesieve","text":"empty => Managesieve service disabled 1 => Enables Managesieve on port 4190","title":"ENABLE_MANAGESIEVE"},{"location":"config/environment/#postmaster_address","text":"empty => postmaster@example.com => Specify the postmaster address","title":"POSTMASTER_ADDRESS"},{"location":"config/environment/#enable_update_check","text":"Check for updates on container start and then once a day. If an update is available, a mail is send to POSTMASTER_ADDRESS. 0 => Update check disabled 1 => Update check enabled","title":"ENABLE_UPDATE_CHECK"},{"location":"config/environment/#update_check_interval","text":"Customize the update check interval. Number + Suffix. Suffix must be 's' for seconds, 'm' for minutes, 'h' for hours or 'd' for days. 1d => Check for updates once a day","title":"UPDATE_CHECK_INTERVAL"},{"location":"config/environment/#postscreen_action","text":"enforce => Allow other tests to complete. Reject attempts to deliver mail with a 550 SMTP reply, and log the helo/sender/recipient information. Repeat this test the next time the client connects. drop => Drop the connection immediately with a 521 SMTP reply. Repeat this test the next time the client connects. ignore => Ignore the failure of this test. Allow other tests to complete. Repeat this test the next time the client connects. This option is useful for testing and collecting statistics without blocking mail.","title":"POSTSCREEN_ACTION"},{"location":"config/environment/#dovecot_mailbox_format","text":"maildir => uses very common Maildir format, one file contains one message sdbox => (experimental) uses Dovecot high-performance mailbox format, one file contains one message mdbox ==> (experimental) uses Dovecot high-performance mailbox format, multiple messages per file and multiple files per box This option has been added in November 2019. Using other format than Maildir is considered as experimental in docker-mailserver and should only be used for testing purpose. For more details, please refer to Dovecot Documentation .","title":"DOVECOT_MAILBOX_FORMAT"},{"location":"config/environment/#postfix_inet_protocols","text":"all => Listen on all interfaces. ipv4 => Listen only on IPv4 interfaces. Most likely you want this behind Docker. ipv6 => Listen only on IPv6 interfaces. Note: More details at http://www.postfix.org/postconf.5.html#inet_protocols","title":"POSTFIX_INET_PROTOCOLS"},{"location":"config/environment/#dovecot_inet_protocols","text":"all => Listen on all interfaces ipv4 => Listen only on IPv4 interfaces. Most likely you want this behind Docker. ipv6 => Listen only on IPv6 interfaces. Note: More information at https://dovecot.org/doc/dovecot-example.conf","title":"DOVECOT_INET_PROTOCOLS"},{"location":"config/environment/#reports","text":"","title":"Reports"},{"location":"config/environment/#pflogsumm_trigger","text":"Enables regular Postfix log summary (\"pflogsumm\") mail reports. not set => No report daily_cron => Daily report for the previous day logrotate => Full report based on the mail log when it is rotated This is a new option. The old REPORT options are still supported for backwards compatibility. If this is not set and reports are enabled with the old options, logrotate will be used.","title":"PFLOGSUMM_TRIGGER"},{"location":"config/environment/#pflogsumm_recipient","text":"Recipient address for Postfix log summary reports. not set => Use POSTMASTER_ADDRESS => Specify the recipient address(es)","title":"PFLOGSUMM_RECIPIENT"},{"location":"config/environment/#pflogsumm_sender","text":"Sender address ( FROM ) for pflogsumm reports (if Postfix log summary reports are enabled). not set => Use REPORT_SENDER => Specify the sender address","title":"PFLOGSUMM_SENDER"},{"location":"config/environment/#logwatch_interval","text":"Interval for logwatch report. none => No report is generated daily => Send a daily report weekly => Send a report every week","title":"LOGWATCH_INTERVAL"},{"location":"config/environment/#logwatch_recipient","text":"Recipient address for logwatch reports if they are enabled. not set => Use REPORT_RECIPIENT or POSTMASTER_ADDRESS => Specify the recipient address(es)","title":"LOGWATCH_RECIPIENT"},{"location":"config/environment/#logwatch_sender","text":"Sender address ( FROM ) for logwatch reports if logwatch reports are enabled. not set => Use REPORT_SENDER => Specify the sender address","title":"LOGWATCH_SENDER"},{"location":"config/environment/#report_recipient","text":"Defines who receives reports (if they are enabled). empty => Use POSTMASTER_ADDRESS => Specify the recipient address","title":"REPORT_RECIPIENT"},{"location":"config/environment/#report_sender","text":"Defines who sends reports (if they are enabled). empty => mailserver-report@ => Specify the sender address","title":"REPORT_SENDER"},{"location":"config/environment/#logrotate_interval","text":"Changes the interval in which log files are rotated. weekly => Rotate log files weekly daily => Rotate log files daily monthly => Rotate log files monthly Note LOGROTATE_INTERVAL only manages logrotate within the container for services we manage internally. The entire log output for the container is still available via docker logs mailserver (or your respective container name). If you want to configure external log rotation for that container output as well, : Docker Logging Drivers . By default, the logs are lost when the container is destroyed (eg: re-creating via docker-compose down && docker-compose up -d ). To keep the logs, mount a volume (to /var/log/mail/ ). Note This variable can also determine the interval for Postfix's log summary reports, see PFLOGSUMM_TRIGGER .","title":"LOGROTATE_INTERVAL"},{"location":"config/environment/#spamassassin","text":"","title":"SpamAssassin"},{"location":"config/environment/#enable_spamassassin","text":"0 => SpamAssassin is disabled 1 => SpamAssassin is enabled","title":"ENABLE_SPAMASSASSIN"},{"location":"config/environment/#spamassassin_spam_to_inbox","text":"0 => Spam messages will be bounced ( rejected ) without any notification ( dangerous ). 1 => Spam messages will be delivered to the inbox and tagged as spam using SA_SPAM_SUBJECT .","title":"SPAMASSASSIN_SPAM_TO_INBOX"},{"location":"config/environment/#enable_spamassassin_kam","text":"KAM is a 3rd party SpamAssassin ruleset, provided by the McGrail Foundation. If SpamAssassin is enabled, KAM can be used in addition to the default ruleset. 0 => KAM disabled 1 => KAM enabled","title":"ENABLE_SPAMASSASSIN_KAM"},{"location":"config/environment/#move_spam_to_junk","text":"Spam messages can be moved in the Junk folder. Note: this setting needs SPAMASSASSIN_SPAM_TO_INBOX=1 0 => Spam messages will be delivered in the mailbox. 1 => Spam messages will be delivered in the Junk folder.","title":"MOVE_SPAM_TO_JUNK"},{"location":"config/environment/#sa_tag","text":"2.0 => add spam info headers if at, or above that level Note: this SpamAssassin setting needs ENABLE_SPAMASSASSIN=1","title":"SA_TAG"},{"location":"config/environment/#sa_tag2","text":"6.31 => add 'spam detected' headers at that level Note: this SpamAssassin setting needs ENABLE_SPAMASSASSIN=1","title":"SA_TAG2"},{"location":"config/environment/#sa_kill","text":"6.31 => triggers spam evasive actions This SpamAssassin setting needs ENABLE_SPAMASSASSIN=1 By default, docker-mailserver is configured to quarantine spam emails. If emails are quarantined, they are compressed and stored in a location dependent on the ONE_DIR setting above. To inhibit this behaviour and deliver spam emails, set this to a very high value e.g. 100.0 . If ONE_DIR=1 (default) the location is /var/mail-state/lib-amavis/virusmails/ , or if ONE_DIR=0 : /var/lib/amavis/virusmails/ . These paths are inside the docker container.","title":"SA_KILL"},{"location":"config/environment/#sa_spam_subject","text":"***SPAM*** => add tag to subject if spam detected Note: this SpamAssassin setting needs ENABLE_SPAMASSASSIN=1 . Add the SpamAssassin score to the subject line by inserting the keyword _SCORE_: ***SPAM(_SCORE_)*** .","title":"SA_SPAM_SUBJECT"},{"location":"config/environment/#sa_shortcircuit_bayes_spam","text":"1 => will activate SpamAssassin short circuiting for bayes spam detection. This will uncomment the respective line in /etc/spamassasin/local.cf Note: activate this only if you are confident in your bayes database for identifying spam.","title":"SA_SHORTCIRCUIT_BAYES_SPAM"},{"location":"config/environment/#sa_shortcircuit_bayes_ham","text":"1 => will activate SpamAssassin short circuiting for bayes ham detection This will uncomment the respective line in /etc/spamassasin/local.cf Note: activate this only if you are confident in your bayes database for identifying ham.","title":"SA_SHORTCIRCUIT_BAYES_HAM"},{"location":"config/environment/#fetchmail","text":"","title":"Fetchmail"},{"location":"config/environment/#enable_fetchmail","text":"0 => fetchmail disabled 1 => fetchmail enabled","title":"ENABLE_FETCHMAIL"},{"location":"config/environment/#fetchmail_poll","text":"300 => fetchmail The number of seconds for the interval","title":"FETCHMAIL_POLL"},{"location":"config/environment/#fetchmail_parallel","text":"0 => fetchmail runs with a single config file /etc/fetchmailrc 1 => /etc/fetchmailrc is split per poll entry. For every poll entry a separate fetchmail instance is started to allow having multiple imap idle configurations defined. Note: The defaults of your fetchmailrc file need to be at the top of the file. Otherwise it won't be added correctly to all separate fetchmail instances.","title":"FETCHMAIL_PARALLEL"},{"location":"config/environment/#ldap","text":"","title":"LDAP"},{"location":"config/environment/#enable_ldap","text":"Deprecated. See ACCOUNT_PROVISIONER .","title":"ENABLE_LDAP"},{"location":"config/environment/#ldap_start_tls","text":"empty => no yes => LDAP over TLS enabled for Postfix","title":"LDAP_START_TLS"},{"location":"config/environment/#ldap_server_host","text":"empty => mail.example.com => Specify the dns-name/ip-address where the ldap-server is listening, or an URI like ldaps://mail.example.com NOTE: If you going to use docker-mailserver in combination with docker-compose.yml you can set the service name here","title":"LDAP_SERVER_HOST"},{"location":"config/environment/#ldap_search_base","text":"empty => ou=people,dc=domain,dc=com => e.g. LDAP_SEARCH_BASE=dc=mydomain,dc=local","title":"LDAP_SEARCH_BASE"},{"location":"config/environment/#ldap_bind_dn","text":"empty => cn=admin,dc=domain,dc=com => take a look at examples of SASL_LDAP_BIND_DN","title":"LDAP_BIND_DN"},{"location":"config/environment/#ldap_bind_pw","text":"empty => admin => Specify the password to bind against ldap","title":"LDAP_BIND_PW"},{"location":"config/environment/#ldap_query_filter_user","text":"e.g. (&(mail=%s)(mailEnabled=TRUE)) => Specify how ldap should be asked for users","title":"LDAP_QUERY_FILTER_USER"},{"location":"config/environment/#ldap_query_filter_group","text":"e.g. (&(mailGroupMember=%s)(mailEnabled=TRUE)) => Specify how ldap should be asked for groups","title":"LDAP_QUERY_FILTER_GROUP"},{"location":"config/environment/#ldap_query_filter_alias","text":"e.g. (&(mailAlias=%s)(mailEnabled=TRUE)) => Specify how ldap should be asked for aliases","title":"LDAP_QUERY_FILTER_ALIAS"},{"location":"config/environment/#ldap_query_filter_domain","text":"e.g. (&(|(mail=*@%s)(mailalias=*@%s)(mailGroupMember=*@%s))(mailEnabled=TRUE)) => Specify how ldap should be asked for domains","title":"LDAP_QUERY_FILTER_DOMAIN"},{"location":"config/environment/#ldap_query_filter_senders","text":"empty => use user/alias/group maps directly, equivalent to (|($LDAP_QUERY_FILTER_USER)($LDAP_QUERY_FILTER_ALIAS)($LDAP_QUERY_FILTER_GROUP)) => Override how ldap should be asked if a sender address is allowed for a user","title":"LDAP_QUERY_FILTER_SENDERS"},{"location":"config/environment/#dovecot_tls","text":"empty => no yes => LDAP over TLS enabled for Dovecot","title":"DOVECOT_TLS"},{"location":"config/environment/#dovecot","text":"The following variables overwrite the default values for /etc/dovecot/dovecot-ldap.conf.ext .","title":"Dovecot"},{"location":"config/environment/#dovecot_base","text":"empty => same as LDAP_SEARCH_BASE => Tell Dovecot to search only below this base entry. (e.g. ou=people,dc=domain,dc=com )","title":"DOVECOT_BASE"},{"location":"config/environment/#dovecot_default_pass_scheme","text":"empty => SSHA => Select one crypt scheme for password hashing from this list of password schemes .","title":"DOVECOT_DEFAULT_PASS_SCHEME"},{"location":"config/environment/#dovecot_dn","text":"empty => same as LDAP_BIND_DN => Bind dn for LDAP connection. (e.g. cn=admin,dc=domain,dc=com )","title":"DOVECOT_DN"},{"location":"config/environment/#dovecot_dnpass","text":"empty => same as LDAP_BIND_PW => Password for LDAP dn sepecifified in DOVECOT_DN .","title":"DOVECOT_DNPASS"},{"location":"config/environment/#dovecot_uris","text":"empty => same as LDAP_SERVER_HOST => Specify a space separated list of LDAP uris. Note: If the protocol is missing, ldap:// will be used. Note: This deprecates DOVECOT_HOSTS (as it didn't allow to use LDAPS), which is currently still supported for backwards compatibility.","title":"DOVECOT_URIS"},{"location":"config/environment/#dovecot_ldap_version","text":"empty => 3 2 => LDAP version 2 is used 3 => LDAP version 3 is used","title":"DOVECOT_LDAP_VERSION"},{"location":"config/environment/#dovecot_auth_bind","text":"empty => no yes => Enable LDAP authentication binds","title":"DOVECOT_AUTH_BIND"},{"location":"config/environment/#dovecot_user_filter","text":"e.g. (&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n))","title":"DOVECOT_USER_FILTER"},{"location":"config/environment/#dovecot_user_attrs","text":"e.g. homeDirectory=home,qmailUID=uid,qmailGID=gid,mailMessageStore=mail => Specify the directory to dovecot attribute mapping that fits your directory structure. Note: This is necessary for directories that do not use the Postfix Book Schema. Note: The left-hand value is the directory attribute, the right hand value is the dovecot variable. More details on the Dovecot Wiki","title":"DOVECOT_USER_ATTRS"},{"location":"config/environment/#dovecot_pass_filter","text":"e.g. (&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n)) empty => same as DOVECOT_USER_FILTER","title":"DOVECOT_PASS_FILTER"},{"location":"config/environment/#dovecot_pass_attrs","text":"e.g. uid=user,userPassword=password => Specify the directory to dovecot variable mapping that fits your directory structure. Note: This is necessary for directories that do not use the Postfix Book Schema. Note: The left-hand value is the directory attribute, the right hand value is the dovecot variable. More details on the Dovecot Wiki","title":"DOVECOT_PASS_ATTRS"},{"location":"config/environment/#postgrey","text":"","title":"Postgrey"},{"location":"config/environment/#enable_postgrey","text":"0 => postgrey is disabled 1 => postgrey is enabled","title":"ENABLE_POSTGREY"},{"location":"config/environment/#postgrey_delay","text":"300 => greylist for N seconds Note: This postgrey setting needs ENABLE_POSTGREY=1","title":"POSTGREY_DELAY"},{"location":"config/environment/#postgrey_max_age","text":"35 => delete entries older than N days since the last time that they have been seen Note: This postgrey setting needs ENABLE_POSTGREY=1","title":"POSTGREY_MAX_AGE"},{"location":"config/environment/#postgrey_auto_whitelist_clients","text":"5 => whitelist host after N successful deliveries (N=0 to disable whitelisting) Note: This postgrey setting needs ENABLE_POSTGREY=1","title":"POSTGREY_AUTO_WHITELIST_CLIENTS"},{"location":"config/environment/#postgrey_text","text":"Delayed by Postgrey => response when a mail is greylisted Note: This postgrey setting needs ENABLE_POSTGREY=1","title":"POSTGREY_TEXT"},{"location":"config/environment/#sasl-auth","text":"","title":"SASL Auth"},{"location":"config/environment/#enable_saslauthd","text":"0 => saslauthd is disabled 1 => saslauthd is enabled","title":"ENABLE_SASLAUTHD"},{"location":"config/environment/#saslauthd_mechanisms","text":"empty => pam ldap => authenticate against ldap server shadow => authenticate against local user db mysql => authenticate against mysql db rimap => authenticate against imap server NOTE: can be a list of mechanisms like pam ldap shadow","title":"SASLAUTHD_MECHANISMS"},{"location":"config/environment/#saslauthd_mech_options","text":"empty => None e.g. with SASLAUTHD_MECHANISMS rimap you need to specify the ip-address/servername of the imap server ==> xxx.xxx.xxx.xxx","title":"SASLAUTHD_MECH_OPTIONS"},{"location":"config/environment/#saslauthd_ldap_server","text":"empty => same as LDAP_SERVER_HOST Note: since version 10.0.0, you can specify a protocol here (like ldaps://); this deprecates SASLAUTHD_LDAP_SSL.","title":"SASLAUTHD_LDAP_SERVER"},{"location":"config/environment/#saslauthd_ldap_start_tls","text":"empty => no yes => Enable ldap_start_tls option","title":"SASLAUTHD_LDAP_START_TLS"},{"location":"config/environment/#saslauthd_ldap_tls_check_peer","text":"empty => no yes => Enable ldap_tls_check_peer option","title":"SASLAUTHD_LDAP_TLS_CHECK_PEER"},{"location":"config/environment/#saslauthd_ldap_tls_cacert_dir","text":"Path to directory with CA (Certificate Authority) certificates. empty => Nothing is added to the configuration Any value => Fills the ldap_tls_cacert_dir option","title":"SASLAUTHD_LDAP_TLS_CACERT_DIR"},{"location":"config/environment/#saslauthd_ldap_tls_cacert_file","text":"File containing CA (Certificate Authority) certificate(s). empty => Nothing is added to the configuration Any value => Fills the ldap_tls_cacert_file option","title":"SASLAUTHD_LDAP_TLS_CACERT_FILE"},{"location":"config/environment/#saslauthd_ldap_bind_dn","text":"empty => same as LDAP_BIND_DN specify an object with privileges to search the directory tree e.g. active directory: SASLAUTHD_LDAP_BIND_DN=cn=Administrator,cn=Users,dc=mydomain,dc=net e.g. openldap: SASLAUTHD_LDAP_BIND_DN=cn=admin,dc=mydomain,dc=net","title":"SASLAUTHD_LDAP_BIND_DN"},{"location":"config/environment/#saslauthd_ldap_password","text":"empty => same as LDAP_BIND_PW","title":"SASLAUTHD_LDAP_PASSWORD"},{"location":"config/environment/#saslauthd_ldap_search_base","text":"empty => same as LDAP_SEARCH_BASE specify the search base","title":"SASLAUTHD_LDAP_SEARCH_BASE"},{"location":"config/environment/#saslauthd_ldap_filter","text":"empty => default filter (&(uniqueIdentifier=%u)(mailEnabled=TRUE)) e.g. for active directory: (&(sAMAccountName=%U)(objectClass=person)) e.g. for openldap: (&(uid=%U)(objectClass=person))","title":"SASLAUTHD_LDAP_FILTER"},{"location":"config/environment/#saslauthd_ldap_password_attr","text":"Specify what password attribute to use for password verification. empty => Nothing is added to the configuration but the documentation says it is userPassword by default. Any value => Fills the ldap_password_attr option","title":"SASLAUTHD_LDAP_PASSWORD_ATTR"},{"location":"config/environment/#saslauthd_ldap_auth_method","text":"empty => bind will be used as a default value fastbind => The fastbind method is used custom => The custom method uses userPassword attribute to verify the password","title":"SASLAUTHD_LDAP_AUTH_METHOD"},{"location":"config/environment/#saslauthd_ldap_mech","text":"Specify the authentication mechanism for SASL bind. empty => Nothing is added to the configuration Any value => Fills the ldap_mech option","title":"SASLAUTHD_LDAP_MECH"},{"location":"config/environment/#srs-sender-rewriting-scheme","text":"","title":"SRS (Sender Rewriting Scheme)"},{"location":"config/environment/#srs_sender_classes","text":"An email has an \"envelope\" sender (indicating the sending server) and a \"header\" sender (indicating who sent it). More strict SPF policies may require you to replace both instead of just the envelope sender. More info . envelope_sender => Rewrite only envelope sender address header_sender => Rewrite only header sender (not recommended) envelope_sender,header_sender => Rewrite both senders","title":"SRS_SENDER_CLASSES"},{"location":"config/environment/#srs_exclude_domains","text":"empty => Envelope sender will be rewritten for all domains provide comma separated list of domains to exclude from rewriting","title":"SRS_EXCLUDE_DOMAINS"},{"location":"config/environment/#srs_secret","text":"empty => generated when the container is started for the first time provide a secret to use in base64 you may specify multiple keys, comma separated. the first one is used for signing and the remaining will be used for verification. this is how you rotate and expire keys if you have a cluster/swarm make sure the same keys are on all nodes example command to generate a key: dd if=/dev/urandom bs=24 count=1 2>/dev/null | base64","title":"SRS_SECRET"},{"location":"config/environment/#srs_domainname","text":"empty => Derived from OVERRIDE_HOSTNAME , $DOMAINNAME (internal), or the container's hostname Set this if auto-detection fails, isn't what you want, or you wish to have a separate container handle DSNs","title":"SRS_DOMAINNAME"},{"location":"config/environment/#default-relay-host","text":"","title":"Default Relay Host"},{"location":"config/environment/#default_relay_host","text":"empty => don't set default relayhost setting in main.cf default host and port to relay all mail through. Format: [example.com]:587 (don't forget the brackets if you need this to be compatible with $RELAY_USER and $RELAY_PASSWORD , explained below).","title":"DEFAULT_RELAY_HOST"},{"location":"config/environment/#multi-domain-relay-hosts","text":"","title":"Multi-domain Relay Hosts"},{"location":"config/environment/#relay_host","text":"empty => don't configure relay host default host to relay mail through","title":"RELAY_HOST"},{"location":"config/environment/#relay_port","text":"empty => 25 default port to relay mail through","title":"RELAY_PORT"},{"location":"config/environment/#relay_user","text":"empty => no default default relay username (if no specific entry exists in postfix-sasl-password.cf)","title":"RELAY_USER"},{"location":"config/environment/#relay_password","text":"empty => no default password for default relay user","title":"RELAY_PASSWORD"},{"location":"config/pop3/","text":"If you want to use POP3(S), you have to add the ports 110 and/or 995 (TLS secured) and the environment variable ENABLE_POP3 to your docker-compose.yml : mailserver : ports : - \"25:25\" # SMTP (explicit TLS => STARTTLS) - \"143:143\" # IMAP4 (explicit TLS => STARTTLS) - \"465:465\" # ESMTP (implicit TLS) - \"587:587\" # ESMTP (explicit TLS => STARTTLS) - \"993:993\" # IMAP4 (implicit TLS) - \"110:110\" # POP3 - \"995:995\" # POP3 (with TLS) environment : - ENABLE_POP3=1","title":"Mail Delivery with POP3"},{"location":"config/setup.sh/","text":"setup.sh is an administration script that helps with the most common tasks, including initial configuration. It is intended to be run from the host machine, not from inside your running container. The latest version of the script is included in the docker-mailserver repository. You may retrieve it at any time by running this command in your console: wget https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/setup.sh chmod a+x ./setup.sh setup.sh for docker-mailserver version v10.1.x and below If you're using docker-mailserver version v10.1.x or below, you will need to get setup.sh with a specific version. Substitute with the tagged release version that you're using: wget https://raw.githubusercontent.com/docker-mailserver/docker-mailserver//setup.sh . Usage Run ./setup.sh help and you'll get all you have ever wanted some usage information: SETUP(1) NAME setup.sh - docker-mailserver administration script SYNOPSIS ./setup.sh [ OPTIONS... ] COMMAND [ help | ARGUMENTS... ] COMMAND := { email | alias | quota | config | relay | debug } SUBCOMMAND DESCRIPTION This is the main administration script that you use for all your interactions with 'docker-mailserver'. Setup, configuration and much more is done with this script. Please note that the script executes most of the commands inside the container itself. If the image was not found, this script will pull the ':latest' tag of 'mailserver/docker-mailserver'. This tag refers to the latest release, see the tagging convention in the README under https://github.com/docker-mailserver/docker-mailserver/blob/master/README.md You will be able to see detailed information about the script you're invoking and its arguments by appending help after your command. Currently, this does not work with all scripts. [SUB]COMMANDS COMMAND email := ./setup.sh email add [] ./setup.sh email update [] ./setup.sh email del [ OPTIONS... ] [ ... ] ./setup.sh email restrict [] ./setup.sh email list COMMAND alias := ./setup.sh alias add ./setup.sh alias del ./setup.sh alias list COMMAND quota := ./setup.sh quota set [] ./setup.sh quota del COMMAND config := ./setup.sh config dkim [ ARGUMENTS... ] COMMAND relay := ./setup.sh relay add-auth [] ./setup.sh relay add-domain [] ./setup.sh relay exclude-domain COMMAND fail2ban = ./setup.sh fail2ban ./setup.sh fail2ban ban ./setup.sh fail2ban unban COMMAND debug := ./setup.sh debug fetchmail ./setup.sh debug login ./setup.sh debug show-mail-logs EXAMPLES ./setup.sh email add test@example.com [password] Add the email account test@example.com. You will be prompted to input a password afterwards if no password was supplied. When supplying `[password]`, it should be in plaintext. ./setup.sh config dkim keysize 2048 domain 'example.com,not-example.com' Creates keys of length 2048 but in an LDAP setup where domains are not known to Postfix by default, so you need to provide them yourself in a comma-separated list. ./setup.sh config dkim help This will provide you with a detailed explanation on how to use the config dkim command, showing what arguments can be passed and what they do. OPTIONS Config path, container or image adjustments -i IMAGE_NAME Provides the name of the 'docker-mailserver' image. The default value is 'docker.io/mailserver/docker-mailserver:latest' -c CONTAINER_NAME Provides the name of the running container. -p PATH Provides the config folder path to the temporary container (does not work if a 'docker-mailserver' container already exists). SELinux -z Allows container access to the bind mount content that is shared among multiple containers on a SELinux-enabled host. -Z Allows container access to the bind mount content that is private and unshared with other containers on a SELinux-enabled host. EXIT STATUS Exit status is 0 if the command was successful. If there was an unexpected error, an error message is shown describing the error. In case of an error, the script will exit with exit status 1.","title":"Your Best Friend setup.sh"},{"location":"config/setup.sh/#usage","text":"Run ./setup.sh help and you'll get all you have ever wanted some usage information: SETUP(1) NAME setup.sh - docker-mailserver administration script SYNOPSIS ./setup.sh [ OPTIONS... ] COMMAND [ help | ARGUMENTS... ] COMMAND := { email | alias | quota | config | relay | debug } SUBCOMMAND DESCRIPTION This is the main administration script that you use for all your interactions with 'docker-mailserver'. Setup, configuration and much more is done with this script. Please note that the script executes most of the commands inside the container itself. If the image was not found, this script will pull the ':latest' tag of 'mailserver/docker-mailserver'. This tag refers to the latest release, see the tagging convention in the README under https://github.com/docker-mailserver/docker-mailserver/blob/master/README.md You will be able to see detailed information about the script you're invoking and its arguments by appending help after your command. Currently, this does not work with all scripts. [SUB]COMMANDS COMMAND email := ./setup.sh email add [] ./setup.sh email update [] ./setup.sh email del [ OPTIONS... ] [ ... ] ./setup.sh email restrict [] ./setup.sh email list COMMAND alias := ./setup.sh alias add ./setup.sh alias del ./setup.sh alias list COMMAND quota := ./setup.sh quota set [] ./setup.sh quota del COMMAND config := ./setup.sh config dkim [ ARGUMENTS... ] COMMAND relay := ./setup.sh relay add-auth [] ./setup.sh relay add-domain [] ./setup.sh relay exclude-domain COMMAND fail2ban = ./setup.sh fail2ban ./setup.sh fail2ban ban ./setup.sh fail2ban unban COMMAND debug := ./setup.sh debug fetchmail ./setup.sh debug login ./setup.sh debug show-mail-logs EXAMPLES ./setup.sh email add test@example.com [password] Add the email account test@example.com. You will be prompted to input a password afterwards if no password was supplied. When supplying `[password]`, it should be in plaintext. ./setup.sh config dkim keysize 2048 domain 'example.com,not-example.com' Creates keys of length 2048 but in an LDAP setup where domains are not known to Postfix by default, so you need to provide them yourself in a comma-separated list. ./setup.sh config dkim help This will provide you with a detailed explanation on how to use the config dkim command, showing what arguments can be passed and what they do. OPTIONS Config path, container or image adjustments -i IMAGE_NAME Provides the name of the 'docker-mailserver' image. The default value is 'docker.io/mailserver/docker-mailserver:latest' -c CONTAINER_NAME Provides the name of the running container. -p PATH Provides the config folder path to the temporary container (does not work if a 'docker-mailserver' container already exists). SELinux -z Allows container access to the bind mount content that is shared among multiple containers on a SELinux-enabled host. -Z Allows container access to the bind mount content that is private and unshared with other containers on a SELinux-enabled host. EXIT STATUS Exit status is 0 if the command was successful. If there was an unexpected error, an error message is shown describing the error. In case of an error, the script will exit with exit status 1.","title":"Usage"},{"location":"config/advanced/auth-ldap/","text":"Introduction Getting started with ldap and docker-mailserver we need to take 3 parts in account: postfix for incoming & outgoing email dovecot for accessing mailboxes saslauthd for SMTP authentication (this can also be delegated to dovecot) Variables to Control Provisioning by the Container Have a look at the ENV page for information on the default values. LDAP_QUERY_FILTER_* Those variables contain the LDAP lookup filters for postfix, using %s as the placeholder for the domain or email address in question. This means that... ...for incoming email, the domain must return an entry for the DOMAIN filter (see virtual_alias_domains ). ...for incoming email, the inboxes which receive the email are chosen by the USER , ALIAS and GROUP filters. The USER filter specifies personal mailboxes, for which only one should exist per address, for example (mail=%s) (also see virtual_mailbox_maps ) The ALIAS filter specifies aliases for mailboxes, using virtual_alias_maps , for example (mailAlias=%s) The GROUP filter specifies the personal mailboxes in a group (for emails that multiple people shall receive), using virtual_alias_maps , for example (mailGroupMember=%s) . Technically, there is no difference between ALIAS and GROUP , but ideally you should use ALIAS for personal aliases for a singular person (like ceo@example.org ) and GROUP for multiple people (like hr@example.org ). ...for outgoing email, the sender address is put through the SENDERS filter, and only if the authenticated user is one of the returned entries, the email can be sent. This only applies if SPOOF_PROTECTION=1 . If the SENDERS filter is missing, the USER , ALIAS and GROUP filters will be used in in a disjunction (OR). To for example allow users from the admin group to spoof any sender email address, and to force everyone else to only use their personal mailbox address for outgoing email, you can use something like this: (|(memberOf=cn=admin,*)(mail=%s)) Example A really simple LDAP_QUERY_FILTER configuration, using only the user filter and allowing only admin@* to spoof any sender addresses. - ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER - LDAP_START_TLS=yes - ACCOUNT_PROVISIONER=LDAP - LDAP_SERVER_HOST=ldap.example.org - LDAP_SEARCH_BASE=dc=example,dc=org\" - LDAP_BIND_DN=cn=admin,dc=example,dc=org - LDAP_BIND_PW=mypassword - SPOOF_PROTECTION=1 - LDAP_QUERY_FILTER_DOMAIN=(mail=*@%s) - LDAP_QUERY_FILTER_USER=(mail=%s) - LDAP_QUERY_FILTER_ALIAS=(|) # doesn't match anything - LDAP_QUERY_FILTER_GROUP=(|) # doesn't match anything - LDAP_QUERY_FILTER_SENDERS=(|(mail=%s)(mail=admin@*)) DOVECOT_*_FILTER & DOVECOT_*_ATTRS These variables specify the LDAP filters that dovecot uses to determine if a user can log in to their IMAP account, and which mailbox is responsible to receive email for a specific postfix user. This is split into the following two lookups, both using %u as the placeholder for the full login name ( see dovecot documentation for a full list of placeholders ). Usually you only need to set DOVECOT_USER_FILTER , in which case it will be used for both filters. DOVECOT_USER_FILTER is used to get the account details (uid, gid, home directory, quota, ...) of a user. DOVECOT_PASS_FILTER is used to get the password information of the user, and is in pretty much all cases identical to DOVECOT_USER_FILTER (which is the default behaviour if left away). If your directory doesn't have the postfix-book schema installed, then you must change the internal attribute handling for dovecot. For this you have to change the pass_attr and the user_attr mapping, as shown in the example below: - DOVECOT_PASS_ATTRS==user,=password - DOVECOT_USER_ATTRS==home,=mail,=uid,=gid Note For DOVECOT_*_ATTRS , you can replace ldapAttr=dovecotAttr with =dovecotAttr=%{ldap:ldapAttr} for more flexibility, like for example =home=/var/mail/%{ldap:uid} or just =uid=5000 . A list of dovecot attributes can be found in the dovecot documentation . Defaults - DOVECOT_USER_ATTRS=mailHomeDirectory=home,mailUidNumber=uid,mailGidNumber=gid,mailStorageDirectory=mail - DOVECOT_PASS_ATTRS=uniqueIdentifier=user,userPassword=password - DOVECOT_USER_FILTER=(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n)) Example Setup for a directory that has the qmail-schema installed and uses uid : - DOVECOT_PASS_ATTRS=uid=user,userPassword=password - DOVECOT_USER_ATTRS=homeDirectory=home,qmailUID=uid,qmailGID=gid,mailMessageStore=mail - DOVECOT_USER_FILTER=(&(objectClass=qmailUser)(uid=%u)(accountStatus=active)) The LDAP server configuration for dovecot will be taken mostly from postfix, other options can be found in the environment section in the docs . DOVECOT_AUTH_BIND Set this to yes to enable authentication binds ( more details in the dovecot documentation ). Currently, only DN lookup is supported without further changes to the configuration files, so this is only useful when you want to bind as a readonly user without the permission to read passwords. SASLAUTHD_LDAP_FILTER This filter is used for saslauthd , which is called by postfix when someone is authenticating through SMTP (assuming that SASLAUTHD_MECHANISMS=ldap is being used). Note that you'll need to set up the LDAP server for saslauthd separately from postfix. The filter variables are explained in detail in the LDAP_SASLAUTHD file , but unfortunately, this method doesn't really support domains right now - that means that %U is the only token that makes sense in this variable. When to use this and how to avoid it Using a separate filter for SMTP authentication allows you to for example allow noreply@example.org to send email, but not log in to IMAP or receive email: (&(mail=%U@example.org)(|(memberOf=cn=email,*)(mail=noreply@example.org))) If you don't want to use a separate filter for SMTP authentication, you can set SASLAUTHD_MECHANISMS=rimap and SASLAUTHD_MECH_OPTIONS=127.0.0.1 to authenticate against dovecot instead - this means that the DOVECOT_USER_FILTER and DOVECOT_PASS_FILTER will be used for SMTP authentication as well. Configure LDAP with saslauthd - ENABLE_SASLAUTHD=1 - SASLAUTHD_MECHANISMS=ldap - SASLAUTHD_LDAP_FILTER=(mail=%U@example.org) Secure Connection with LDAPS or StartTLS To enable LDAPS, all you need to do is to add the protocol to LDAP_SERVER_HOST , for example ldaps://example.org:636 . To enable LDAP over StartTLS (on port 389), you need to set the following environment variables instead (the protocol must not be ldaps:// in this case!): - LDAP_START_TLS=yes - DOVECOT_TLS=yes - SASLAUTHD_LDAP_START_TLS=yes Active Directory Configurations (Tested with Samba4 AD Implementation) In addition to LDAP explanation above, when Docker Mailserver is intended to be used with Active Directory (or the equivalent implementations like Samba4 AD DC) the following points should be taken into consideration: Samba4 Active Directory requires a secure connection to the domain controller (DC), either via SSL/TLS (LDAPS) or via StartTLS. The username equivalent in Active Directory is: sAMAccountName . proxyAddresses can be used to store email aliases of single users. The convention is to prefix the email aliases with smtp: (e.g: smtp:some.name@example.com ). Active Directory is used typically not only as LDAP Directory storage, but also as a domain controller , i.e., it will do many things including authenticating users. Mixing Linux and Windows clients requires the usage of RFC2307 attributes , namely uidNumber , gidNumber instead of the typical uid . Assigning different owner to email folders can also be done in this approach, nevertheless there is a bug at the moment in Docker Mailserver that overwrites all permissions when starting the container. Either a manual fix is necessary now, or a temporary workaround to use a hard-coded ldap:uidNumber that equals to 5000 until this issue is fixed. To deliver the emails to different members of Active Directory Security Group or Distribution Group (similar to mailing lists), use a user-patches.sh script to modify ldap-groups.cf so that it includes leaf_result_attribute = mail and special_result_attribute = member . This can be achieved simply by: The configuration shown to get the Group to work is from here and here . # user-patches.sh ... grep -q '^leaf_result_attribute = mail$' /etc/postfix/ldap-groups.cf || echo \"leaf_result_attribute = mail\" >> /etc/postfix/ldap-groups.cf grep -q '^special_result_attribute = member$' /etc/postfix/ldap-groups.cf || echo \"special_result_attribute = member\" >> /etc/postfix/ldap-groups.cf ... In /etc/ldap/ldap.conf , if the TLS_REQCERT is demand / hard (default), the CA certificate used to verify the LDAP server certificate must be recognized as a trusted CA. This can be done by volume mounting the ca.crt file and updating the trust store via a user-patches.sh script: # user-patches.sh ... cp /MOUNTED_FOLDER/ca.crt /usr/local/share/ca-certificates/ update-ca-certificates ... The changes on the configurations necessary to work with Active Directory ( only changes are listed, the rest of the LDAP configuration can be taken from the other examples shown in this documentation): # If StartTLS is the chosen method to establish a secure connection with Active Directory. - LDAP_START_TLS=yes - SASLAUTHD_LDAP_START_TLS=yes - DOVECOT_TLS=yes - LDAP_QUERY_FILTER_USER=(&(objectclass=person)(mail=%s)) - LDAP_QUERY_FILTER_ALIAS=(&(objectclass=person)(proxyAddresses=smtp:%s)) # Filters Active Directory groups (mail lists). Additional changes on ldap-groups.cf are also required as shown above. - LDAP_QUERY_FILTER_GROUP=(&(objectClass=group)(mail=%s)) - LDAP_QUERY_FILTER_DOMAIN=(mail=*@%s) # Allows only Domain admins to send any sender email address, otherwise the sender address must match the LDAP attribute `mail`. - SPOOF_PROTECTION=1 - LDAP_QUERY_FILTER_SENDERS=(|(mail=%s)(proxyAddresses=smtp:%s)(memberOf=cn=Domain Admins,cn=Users,dc=*)) - DOVECOT_USER_FILTER=(&(objectclass=person)(sAMAccountName=%n)) # At the moment to be able to use %{ldap:uidNumber}, a manual bug fix as described above must be used. Otherwise %{ldap:uidNumber} %{ldap:uidNumber} must be replaced by the hard-coded value 5000. - DOVECOT_USER_ATTRS==uid=%{ldap:uidNumber},=gid=5000,=home=/var/mail/%Ln,=mail=maildir:~/Maildir - DOVECOT_PASS_ATTRS=sAMAccountName=user,userPassword=password - SASLAUTHD_LDAP_FILTER=(&(sAMAccountName=%U)(objectClass=person)) LDAP Setup Examples Basic Setup services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail.example.com ports : - \"25:25\" - \"143:143\" - \"587:587\" - \"993:993\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - /etc/localtime:/etc/localtime:ro environment : - ENABLE_SPAMASSASSIN=1 - ENABLE_CLAMAV=1 - ENABLE_FAIL2BAN=1 - ENABLE_POSTGREY=1 # >>> Postfix LDAP Integration - ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER - ACCOUNT_PROVISIONER=LDAP - LDAP_SERVER_HOST=ldap.example.org - LDAP_BIND_DN=cn=admin,ou=users,dc=example,dc=org - LDAP_BIND_PW=mypassword - LDAP_SEARCH_BASE=dc=example,dc=org - LDAP_QUERY_FILTER_DOMAIN=(|(mail=*@%s)(mailAlias=*@%s)(mailGroupMember=*@%s)) - LDAP_QUERY_FILTER_USER=(&(objectClass=inetOrgPerson)(mail=%s)) - LDAP_QUERY_FILTER_ALIAS=(&(objectClass=inetOrgPerson)(mailAlias=%s)) - LDAP_QUERY_FILTER_GROUP=(&(objectClass=inetOrgPerson)(mailGroupMember=%s)) - LDAP_QUERY_FILTER_SENDERS=(&(objectClass=inetOrgPerson)(|(mail=%s)(mailAlias=%s)(mailGroupMember=%s))) - SPOOF_PROTECTION=1 # <<< Postfix LDAP Integration # >>> Dovecot LDAP Integration - DOVECOT_USER_FILTER=(&(objectClass=inetOrgPerson)(mail=%u)) - DOVECOT_PASS_ATTRS=uid=user,userPassword=password - DOVECOT_USER_ATTRS==home=/var/mail/%{ldap:uid},=mail=maildir:~/Maildir,uidNumber=uid,gidNumber=gid # <<< Dovecot LDAP Integration # >>> SASL LDAP Authentication - ENABLE_SASLAUTHD=1 - SASLAUTHD_MECHANISMS=ldap - SASLAUTHD_LDAP_FILTER=(&(mail=%U@example.org)(objectClass=inetOrgPerson)) # <<< SASL LDAP Authentication - SSL_TYPE=letsencrypt - PERMIT_DOCKER=host cap_add : - NET_ADMIN Kopano / Zarafa services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail.example.com ports : - \"25:25\" - \"143:143\" - \"587:587\" - \"993:993\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ environment : # We are not using dovecot here - SMTP_ONLY=1 - ENABLE_SPAMASSASSIN=1 - ENABLE_CLAMAV=1 - ENABLE_FAIL2BAN=1 - ENABLE_POSTGREY=1 - SASLAUTHD_PASSWD= # >>> SASL Authentication - ENABLE_SASLAUTHD=1 - SASLAUTHD_LDAP_FILTER=(&(sAMAccountName=%U)(objectClass=person)) - SASLAUTHD_MECHANISMS=ldap # <<< SASL Authentication # >>> Postfix Ldap Integration - ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER - ACCOUNT_PROVISIONER=LDAP - LDAP_SERVER_HOST= - LDAP_SEARCH_BASE=dc=mydomain,dc=loc - LDAP_BIND_DN=cn=Administrator,cn=Users,dc=mydomain,dc=loc - LDAP_BIND_PW=mypassword - LDAP_QUERY_FILTER_USER=(&(objectClass=user)(mail=%s)) - LDAP_QUERY_FILTER_GROUP=(&(objectclass=group)(mail=%s)) - LDAP_QUERY_FILTER_ALIAS=(&(objectClass=user)(otherMailbox=%s)) - LDAP_QUERY_FILTER_DOMAIN=(&(|(mail=*@%s)(mailalias=*@%s)(mailGroupMember=*@%s))(mailEnabled=TRUE)) # <<< Postfix Ldap Integration # >>> Kopano Integration - POSTFIX_DAGENT=lmtp:kopano:2003 # <<< Kopano Integration - SSL_TYPE=letsencrypt - PERMIT_DOCKER=host cap_add : - NET_ADMIN","title":"LDAP Authentication"},{"location":"config/advanced/auth-ldap/#introduction","text":"Getting started with ldap and docker-mailserver we need to take 3 parts in account: postfix for incoming & outgoing email dovecot for accessing mailboxes saslauthd for SMTP authentication (this can also be delegated to dovecot)","title":"Introduction"},{"location":"config/advanced/auth-ldap/#variables-to-control-provisioning-by-the-container","text":"Have a look at the ENV page for information on the default values.","title":"Variables to Control Provisioning by the Container"},{"location":"config/advanced/auth-ldap/#ldap_query_filter_","text":"Those variables contain the LDAP lookup filters for postfix, using %s as the placeholder for the domain or email address in question. This means that... ...for incoming email, the domain must return an entry for the DOMAIN filter (see virtual_alias_domains ). ...for incoming email, the inboxes which receive the email are chosen by the USER , ALIAS and GROUP filters. The USER filter specifies personal mailboxes, for which only one should exist per address, for example (mail=%s) (also see virtual_mailbox_maps ) The ALIAS filter specifies aliases for mailboxes, using virtual_alias_maps , for example (mailAlias=%s) The GROUP filter specifies the personal mailboxes in a group (for emails that multiple people shall receive), using virtual_alias_maps , for example (mailGroupMember=%s) . Technically, there is no difference between ALIAS and GROUP , but ideally you should use ALIAS for personal aliases for a singular person (like ceo@example.org ) and GROUP for multiple people (like hr@example.org ). ...for outgoing email, the sender address is put through the SENDERS filter, and only if the authenticated user is one of the returned entries, the email can be sent. This only applies if SPOOF_PROTECTION=1 . If the SENDERS filter is missing, the USER , ALIAS and GROUP filters will be used in in a disjunction (OR). To for example allow users from the admin group to spoof any sender email address, and to force everyone else to only use their personal mailbox address for outgoing email, you can use something like this: (|(memberOf=cn=admin,*)(mail=%s)) Example A really simple LDAP_QUERY_FILTER configuration, using only the user filter and allowing only admin@* to spoof any sender addresses. - ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER - LDAP_START_TLS=yes - ACCOUNT_PROVISIONER=LDAP - LDAP_SERVER_HOST=ldap.example.org - LDAP_SEARCH_BASE=dc=example,dc=org\" - LDAP_BIND_DN=cn=admin,dc=example,dc=org - LDAP_BIND_PW=mypassword - SPOOF_PROTECTION=1 - LDAP_QUERY_FILTER_DOMAIN=(mail=*@%s) - LDAP_QUERY_FILTER_USER=(mail=%s) - LDAP_QUERY_FILTER_ALIAS=(|) # doesn't match anything - LDAP_QUERY_FILTER_GROUP=(|) # doesn't match anything - LDAP_QUERY_FILTER_SENDERS=(|(mail=%s)(mail=admin@*))","title":"LDAP_QUERY_FILTER_*"},{"location":"config/advanced/auth-ldap/#dovecot__filter-dovecot__attrs","text":"These variables specify the LDAP filters that dovecot uses to determine if a user can log in to their IMAP account, and which mailbox is responsible to receive email for a specific postfix user. This is split into the following two lookups, both using %u as the placeholder for the full login name ( see dovecot documentation for a full list of placeholders ). Usually you only need to set DOVECOT_USER_FILTER , in which case it will be used for both filters. DOVECOT_USER_FILTER is used to get the account details (uid, gid, home directory, quota, ...) of a user. DOVECOT_PASS_FILTER is used to get the password information of the user, and is in pretty much all cases identical to DOVECOT_USER_FILTER (which is the default behaviour if left away). If your directory doesn't have the postfix-book schema installed, then you must change the internal attribute handling for dovecot. For this you have to change the pass_attr and the user_attr mapping, as shown in the example below: - DOVECOT_PASS_ATTRS==user,=password - DOVECOT_USER_ATTRS==home,=mail,=uid,=gid Note For DOVECOT_*_ATTRS , you can replace ldapAttr=dovecotAttr with =dovecotAttr=%{ldap:ldapAttr} for more flexibility, like for example =home=/var/mail/%{ldap:uid} or just =uid=5000 . A list of dovecot attributes can be found in the dovecot documentation . Defaults - DOVECOT_USER_ATTRS=mailHomeDirectory=home,mailUidNumber=uid,mailGidNumber=gid,mailStorageDirectory=mail - DOVECOT_PASS_ATTRS=uniqueIdentifier=user,userPassword=password - DOVECOT_USER_FILTER=(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n)) Example Setup for a directory that has the qmail-schema installed and uses uid : - DOVECOT_PASS_ATTRS=uid=user,userPassword=password - DOVECOT_USER_ATTRS=homeDirectory=home,qmailUID=uid,qmailGID=gid,mailMessageStore=mail - DOVECOT_USER_FILTER=(&(objectClass=qmailUser)(uid=%u)(accountStatus=active)) The LDAP server configuration for dovecot will be taken mostly from postfix, other options can be found in the environment section in the docs .","title":"DOVECOT_*_FILTER & DOVECOT_*_ATTRS"},{"location":"config/advanced/auth-ldap/#dovecot_auth_bind","text":"Set this to yes to enable authentication binds ( more details in the dovecot documentation ). Currently, only DN lookup is supported without further changes to the configuration files, so this is only useful when you want to bind as a readonly user without the permission to read passwords.","title":"DOVECOT_AUTH_BIND"},{"location":"config/advanced/auth-ldap/#saslauthd_ldap_filter","text":"This filter is used for saslauthd , which is called by postfix when someone is authenticating through SMTP (assuming that SASLAUTHD_MECHANISMS=ldap is being used). Note that you'll need to set up the LDAP server for saslauthd separately from postfix. The filter variables are explained in detail in the LDAP_SASLAUTHD file , but unfortunately, this method doesn't really support domains right now - that means that %U is the only token that makes sense in this variable. When to use this and how to avoid it Using a separate filter for SMTP authentication allows you to for example allow noreply@example.org to send email, but not log in to IMAP or receive email: (&(mail=%U@example.org)(|(memberOf=cn=email,*)(mail=noreply@example.org))) If you don't want to use a separate filter for SMTP authentication, you can set SASLAUTHD_MECHANISMS=rimap and SASLAUTHD_MECH_OPTIONS=127.0.0.1 to authenticate against dovecot instead - this means that the DOVECOT_USER_FILTER and DOVECOT_PASS_FILTER will be used for SMTP authentication as well. Configure LDAP with saslauthd - ENABLE_SASLAUTHD=1 - SASLAUTHD_MECHANISMS=ldap - SASLAUTHD_LDAP_FILTER=(mail=%U@example.org)","title":"SASLAUTHD_LDAP_FILTER"},{"location":"config/advanced/auth-ldap/#secure-connection-with-ldaps-or-starttls","text":"To enable LDAPS, all you need to do is to add the protocol to LDAP_SERVER_HOST , for example ldaps://example.org:636 . To enable LDAP over StartTLS (on port 389), you need to set the following environment variables instead (the protocol must not be ldaps:// in this case!): - LDAP_START_TLS=yes - DOVECOT_TLS=yes - SASLAUTHD_LDAP_START_TLS=yes","title":"Secure Connection with LDAPS or StartTLS"},{"location":"config/advanced/auth-ldap/#active-directory-configurations-tested-with-samba4-ad-implementation","text":"In addition to LDAP explanation above, when Docker Mailserver is intended to be used with Active Directory (or the equivalent implementations like Samba4 AD DC) the following points should be taken into consideration: Samba4 Active Directory requires a secure connection to the domain controller (DC), either via SSL/TLS (LDAPS) or via StartTLS. The username equivalent in Active Directory is: sAMAccountName . proxyAddresses can be used to store email aliases of single users. The convention is to prefix the email aliases with smtp: (e.g: smtp:some.name@example.com ). Active Directory is used typically not only as LDAP Directory storage, but also as a domain controller , i.e., it will do many things including authenticating users. Mixing Linux and Windows clients requires the usage of RFC2307 attributes , namely uidNumber , gidNumber instead of the typical uid . Assigning different owner to email folders can also be done in this approach, nevertheless there is a bug at the moment in Docker Mailserver that overwrites all permissions when starting the container. Either a manual fix is necessary now, or a temporary workaround to use a hard-coded ldap:uidNumber that equals to 5000 until this issue is fixed. To deliver the emails to different members of Active Directory Security Group or Distribution Group (similar to mailing lists), use a user-patches.sh script to modify ldap-groups.cf so that it includes leaf_result_attribute = mail and special_result_attribute = member . This can be achieved simply by: The configuration shown to get the Group to work is from here and here . # user-patches.sh ... grep -q '^leaf_result_attribute = mail$' /etc/postfix/ldap-groups.cf || echo \"leaf_result_attribute = mail\" >> /etc/postfix/ldap-groups.cf grep -q '^special_result_attribute = member$' /etc/postfix/ldap-groups.cf || echo \"special_result_attribute = member\" >> /etc/postfix/ldap-groups.cf ... In /etc/ldap/ldap.conf , if the TLS_REQCERT is demand / hard (default), the CA certificate used to verify the LDAP server certificate must be recognized as a trusted CA. This can be done by volume mounting the ca.crt file and updating the trust store via a user-patches.sh script: # user-patches.sh ... cp /MOUNTED_FOLDER/ca.crt /usr/local/share/ca-certificates/ update-ca-certificates ... The changes on the configurations necessary to work with Active Directory ( only changes are listed, the rest of the LDAP configuration can be taken from the other examples shown in this documentation): # If StartTLS is the chosen method to establish a secure connection with Active Directory. - LDAP_START_TLS=yes - SASLAUTHD_LDAP_START_TLS=yes - DOVECOT_TLS=yes - LDAP_QUERY_FILTER_USER=(&(objectclass=person)(mail=%s)) - LDAP_QUERY_FILTER_ALIAS=(&(objectclass=person)(proxyAddresses=smtp:%s)) # Filters Active Directory groups (mail lists). Additional changes on ldap-groups.cf are also required as shown above. - LDAP_QUERY_FILTER_GROUP=(&(objectClass=group)(mail=%s)) - LDAP_QUERY_FILTER_DOMAIN=(mail=*@%s) # Allows only Domain admins to send any sender email address, otherwise the sender address must match the LDAP attribute `mail`. - SPOOF_PROTECTION=1 - LDAP_QUERY_FILTER_SENDERS=(|(mail=%s)(proxyAddresses=smtp:%s)(memberOf=cn=Domain Admins,cn=Users,dc=*)) - DOVECOT_USER_FILTER=(&(objectclass=person)(sAMAccountName=%n)) # At the moment to be able to use %{ldap:uidNumber}, a manual bug fix as described above must be used. Otherwise %{ldap:uidNumber} %{ldap:uidNumber} must be replaced by the hard-coded value 5000. - DOVECOT_USER_ATTRS==uid=%{ldap:uidNumber},=gid=5000,=home=/var/mail/%Ln,=mail=maildir:~/Maildir - DOVECOT_PASS_ATTRS=sAMAccountName=user,userPassword=password - SASLAUTHD_LDAP_FILTER=(&(sAMAccountName=%U)(objectClass=person))","title":"Active Directory Configurations (Tested with Samba4 AD Implementation)"},{"location":"config/advanced/auth-ldap/#ldap-setup-examples","text":"Basic Setup services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail.example.com ports : - \"25:25\" - \"143:143\" - \"587:587\" - \"993:993\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - /etc/localtime:/etc/localtime:ro environment : - ENABLE_SPAMASSASSIN=1 - ENABLE_CLAMAV=1 - ENABLE_FAIL2BAN=1 - ENABLE_POSTGREY=1 # >>> Postfix LDAP Integration - ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER - ACCOUNT_PROVISIONER=LDAP - LDAP_SERVER_HOST=ldap.example.org - LDAP_BIND_DN=cn=admin,ou=users,dc=example,dc=org - LDAP_BIND_PW=mypassword - LDAP_SEARCH_BASE=dc=example,dc=org - LDAP_QUERY_FILTER_DOMAIN=(|(mail=*@%s)(mailAlias=*@%s)(mailGroupMember=*@%s)) - LDAP_QUERY_FILTER_USER=(&(objectClass=inetOrgPerson)(mail=%s)) - LDAP_QUERY_FILTER_ALIAS=(&(objectClass=inetOrgPerson)(mailAlias=%s)) - LDAP_QUERY_FILTER_GROUP=(&(objectClass=inetOrgPerson)(mailGroupMember=%s)) - LDAP_QUERY_FILTER_SENDERS=(&(objectClass=inetOrgPerson)(|(mail=%s)(mailAlias=%s)(mailGroupMember=%s))) - SPOOF_PROTECTION=1 # <<< Postfix LDAP Integration # >>> Dovecot LDAP Integration - DOVECOT_USER_FILTER=(&(objectClass=inetOrgPerson)(mail=%u)) - DOVECOT_PASS_ATTRS=uid=user,userPassword=password - DOVECOT_USER_ATTRS==home=/var/mail/%{ldap:uid},=mail=maildir:~/Maildir,uidNumber=uid,gidNumber=gid # <<< Dovecot LDAP Integration # >>> SASL LDAP Authentication - ENABLE_SASLAUTHD=1 - SASLAUTHD_MECHANISMS=ldap - SASLAUTHD_LDAP_FILTER=(&(mail=%U@example.org)(objectClass=inetOrgPerson)) # <<< SASL LDAP Authentication - SSL_TYPE=letsencrypt - PERMIT_DOCKER=host cap_add : - NET_ADMIN Kopano / Zarafa services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail.example.com ports : - \"25:25\" - \"143:143\" - \"587:587\" - \"993:993\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ environment : # We are not using dovecot here - SMTP_ONLY=1 - ENABLE_SPAMASSASSIN=1 - ENABLE_CLAMAV=1 - ENABLE_FAIL2BAN=1 - ENABLE_POSTGREY=1 - SASLAUTHD_PASSWD= # >>> SASL Authentication - ENABLE_SASLAUTHD=1 - SASLAUTHD_LDAP_FILTER=(&(sAMAccountName=%U)(objectClass=person)) - SASLAUTHD_MECHANISMS=ldap # <<< SASL Authentication # >>> Postfix Ldap Integration - ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER - ACCOUNT_PROVISIONER=LDAP - LDAP_SERVER_HOST= - LDAP_SEARCH_BASE=dc=mydomain,dc=loc - LDAP_BIND_DN=cn=Administrator,cn=Users,dc=mydomain,dc=loc - LDAP_BIND_PW=mypassword - LDAP_QUERY_FILTER_USER=(&(objectClass=user)(mail=%s)) - LDAP_QUERY_FILTER_GROUP=(&(objectclass=group)(mail=%s)) - LDAP_QUERY_FILTER_ALIAS=(&(objectClass=user)(otherMailbox=%s)) - LDAP_QUERY_FILTER_DOMAIN=(&(|(mail=*@%s)(mailalias=*@%s)(mailGroupMember=*@%s))(mailEnabled=TRUE)) # <<< Postfix Ldap Integration # >>> Kopano Integration - POSTFIX_DAGENT=lmtp:kopano:2003 # <<< Kopano Integration - SSL_TYPE=letsencrypt - PERMIT_DOCKER=host cap_add : - NET_ADMIN","title":"LDAP Setup Examples"},{"location":"config/advanced/dovecot-master-accounts/","text":"Introduction A dovecot master account is able to login as any configured user. This is useful for administrative tasks like hot backups. Configuration It is possible to create, update, delete and list dovecot master accounts using setup.sh . See setup.sh help for usage. This feature is presently not supported with LDAP . Logging in Once a master account is configured, it is possible to connect to any users mailbox using this account. Log in over POP3/IMAP using the following credential scheme: Username: * Password: ","title":"Dovecot Master Accounts"},{"location":"config/advanced/dovecot-master-accounts/#introduction","text":"A dovecot master account is able to login as any configured user. This is useful for administrative tasks like hot backups.","title":"Introduction"},{"location":"config/advanced/dovecot-master-accounts/#configuration","text":"It is possible to create, update, delete and list dovecot master accounts using setup.sh . See setup.sh help for usage. This feature is presently not supported with LDAP .","title":"Configuration"},{"location":"config/advanced/dovecot-master-accounts/#logging-in","text":"Once a master account is configured, it is possible to connect to any users mailbox using this account. Log in over POP3/IMAP using the following credential scheme: Username: * Password: ","title":"Logging in"},{"location":"config/advanced/full-text-search/","text":"Overview Full-text search allows all messages to be indexed, so that mail clients can quickly and efficiently search messages by their full text content. Dovecot supports a variety of community supported FTS indexing backends . docker-mailserver comes pre-installed with two plugins that can be enabled with a dovecot config file. Please be aware that indexing consumes memory and takes up additional disk space. Xapian The dovecot-fts-xapian plugin makes use of Xapian . Xapian enables embedding an FTS engine without the need for additional backends. The indexes will be stored as a subfolder named xapian-indexes inside your local mail-data folder ( /var/mail internally ). With the default settings, 10GB of email data may generate around 4GB of indexed data. While indexing is memory intensive, you can configure the plugin to limit the amount of memory consumed by the index workers. With Xapian being small and fast, this plugin is a good choice for low memory environments (2GB) as compared to Solr. Setup To configure fts-xapian as a dovecot plugin, create a file at docker-data/dms/config/dovecot/fts-xapian-plugin.conf and place the following in it: mail_plugins = $mail_plugins fts fts_xapian plugin { fts = xapian fts_xapian = partial=3 full=20 verbose=0 fts_autoindex = yes fts_enforced = yes # disable indexing of folders # fts_autoindex_exclude = \\Trash # Index attachements # fts_decoder = decode2text } service indexer-worker { # limit size of indexer-worker RAM usage, ex: 512MB, 1GB, 2GB vsz_limit = 1GB } # service decode2text { # executable = script /usr/libexec/dovecot/decode2text.sh # user = dovecot # unix_listener decode2text { # mode = 0666 # } # } adjust the settings to tune for your desired memory limits, exclude folders and enable searching text inside of attachments Update docker-compose.yml to load the previously created dovecot plugin config file: version : '3.8' services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail domainname : example.com env_file : mailserver.env ports : - \"25:25\" # SMTP (explicit TLS => STARTTLS) - \"143:143\" # IMAP4 (explicit TLS => STARTTLS) - \"465:465\" # ESMTP (implicit TLS) - \"587:587\" # ESMTP (explicit TLS => STARTTLS) - \"993:993\" # IMAP4 (implicit TLS) volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - ./docker-data/dms/config/dovecot/fts-xapian-plugin.conf:/etc/dovecot/conf.d/10-plugin.conf:ro - /etc/localtime:/etc/localtime:ro restart : always stop_grace_period : 1m cap_add : - NET_ADMIN Recreate containers: docker-compose down docker-compose up -d Initialize indexing on all users for all mail: docker-compose exec mailserver doveadm index -A -q \\* Run the following command in a daily cron job: docker-compose exec mailserver doveadm fts optimize -A Or like the Spamassassin example shows, you can instead use cron from within docker-mailserver to avoid potential errors if the mail-server is not running: Example Create a system cron file: # in the docker-compose.yml root directory mkdir -p ./docker-data/dms/cron # if you didn't have this folder before touch ./docker-data/dms/cron/fts_xapian chown root:root ./docker-data/dms/cron/fts_xapian chmod 0644 ./docker-data/dms/cron/fts_xapian Edit the system cron file nano ./docker-data/dms/cron/fts_xapian , and set an appropriate configuration: # Adding `MAILTO=\"\"` prevents cron emailing notifications of the task outcome each run MAILTO = \"\" # # m h dom mon dow user command # # Everyday 4:00AM, optimize index files 0 4 * * * root doveadm fts optimize -A Then with docker-compose.yml : services : mailserver : image : docker.io/mailserver/docker-mailserver:latest volumes : - ./docker-data/dms/cron/fts_xapian:/etc/cron.d/fts_xapian Solr The dovecot-solr Plugin is used in conjunction with Apache Solr running in a separate container. This is quite straightforward to setup using the following instructions. Solr is a mature and fast indexing backend that runs on the JVM. The indexes are relatively compact compared to the size of your total email. However, Solr also requires a fair bit of RAM. While Solr is highly tuneable , it may require a bit of testing to get it right. Setup docker-compose.yml : solr : image : lmmdock/dovecot-solr:latest volumes : - ./docker-data/dms/config/dovecot/solr-dovecot:/opt/solr/server/solr/dovecot restart : always mailserver : depends_on : - solr image : docker.io/mailserver/docker-mailserver:latest ... volumes : ... - ./docker-data/dms/config/dovecot/10-plugin.conf:/etc/dovecot/conf.d/10-plugin.conf:ro ... ./docker-data/dms/config/dovecot/10-plugin.conf : mail_plugins = $mail_plugins fts fts_solr plugin { fts = solr fts_autoindex = yes fts_solr = url=http://solr:8983/solr/dovecot/ } Recreate containers: docker-compose down ; docker-compose up -d Flag all user mailbox FTS indexes as invalid, so they are rescanned on demand when they are next searched: docker-compose exec mailserver doveadm fts rescan -A Further Discussion See #905","title":"Full-Text Search"},{"location":"config/advanced/full-text-search/#overview","text":"Full-text search allows all messages to be indexed, so that mail clients can quickly and efficiently search messages by their full text content. Dovecot supports a variety of community supported FTS indexing backends . docker-mailserver comes pre-installed with two plugins that can be enabled with a dovecot config file. Please be aware that indexing consumes memory and takes up additional disk space.","title":"Overview"},{"location":"config/advanced/full-text-search/#xapian","text":"The dovecot-fts-xapian plugin makes use of Xapian . Xapian enables embedding an FTS engine without the need for additional backends. The indexes will be stored as a subfolder named xapian-indexes inside your local mail-data folder ( /var/mail internally ). With the default settings, 10GB of email data may generate around 4GB of indexed data. While indexing is memory intensive, you can configure the plugin to limit the amount of memory consumed by the index workers. With Xapian being small and fast, this plugin is a good choice for low memory environments (2GB) as compared to Solr.","title":"Xapian"},{"location":"config/advanced/full-text-search/#setup","text":"To configure fts-xapian as a dovecot plugin, create a file at docker-data/dms/config/dovecot/fts-xapian-plugin.conf and place the following in it: mail_plugins = $mail_plugins fts fts_xapian plugin { fts = xapian fts_xapian = partial=3 full=20 verbose=0 fts_autoindex = yes fts_enforced = yes # disable indexing of folders # fts_autoindex_exclude = \\Trash # Index attachements # fts_decoder = decode2text } service indexer-worker { # limit size of indexer-worker RAM usage, ex: 512MB, 1GB, 2GB vsz_limit = 1GB } # service decode2text { # executable = script /usr/libexec/dovecot/decode2text.sh # user = dovecot # unix_listener decode2text { # mode = 0666 # } # } adjust the settings to tune for your desired memory limits, exclude folders and enable searching text inside of attachments Update docker-compose.yml to load the previously created dovecot plugin config file: version : '3.8' services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail domainname : example.com env_file : mailserver.env ports : - \"25:25\" # SMTP (explicit TLS => STARTTLS) - \"143:143\" # IMAP4 (explicit TLS => STARTTLS) - \"465:465\" # ESMTP (implicit TLS) - \"587:587\" # ESMTP (explicit TLS => STARTTLS) - \"993:993\" # IMAP4 (implicit TLS) volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - ./docker-data/dms/config/dovecot/fts-xapian-plugin.conf:/etc/dovecot/conf.d/10-plugin.conf:ro - /etc/localtime:/etc/localtime:ro restart : always stop_grace_period : 1m cap_add : - NET_ADMIN Recreate containers: docker-compose down docker-compose up -d Initialize indexing on all users for all mail: docker-compose exec mailserver doveadm index -A -q \\* Run the following command in a daily cron job: docker-compose exec mailserver doveadm fts optimize -A Or like the Spamassassin example shows, you can instead use cron from within docker-mailserver to avoid potential errors if the mail-server is not running: Example Create a system cron file: # in the docker-compose.yml root directory mkdir -p ./docker-data/dms/cron # if you didn't have this folder before touch ./docker-data/dms/cron/fts_xapian chown root:root ./docker-data/dms/cron/fts_xapian chmod 0644 ./docker-data/dms/cron/fts_xapian Edit the system cron file nano ./docker-data/dms/cron/fts_xapian , and set an appropriate configuration: # Adding `MAILTO=\"\"` prevents cron emailing notifications of the task outcome each run MAILTO = \"\" # # m h dom mon dow user command # # Everyday 4:00AM, optimize index files 0 4 * * * root doveadm fts optimize -A Then with docker-compose.yml : services : mailserver : image : docker.io/mailserver/docker-mailserver:latest volumes : - ./docker-data/dms/cron/fts_xapian:/etc/cron.d/fts_xapian","title":"Setup"},{"location":"config/advanced/full-text-search/#solr","text":"The dovecot-solr Plugin is used in conjunction with Apache Solr running in a separate container. This is quite straightforward to setup using the following instructions. Solr is a mature and fast indexing backend that runs on the JVM. The indexes are relatively compact compared to the size of your total email. However, Solr also requires a fair bit of RAM. While Solr is highly tuneable , it may require a bit of testing to get it right.","title":"Solr"},{"location":"config/advanced/full-text-search/#setup_1","text":"docker-compose.yml : solr : image : lmmdock/dovecot-solr:latest volumes : - ./docker-data/dms/config/dovecot/solr-dovecot:/opt/solr/server/solr/dovecot restart : always mailserver : depends_on : - solr image : docker.io/mailserver/docker-mailserver:latest ... volumes : ... - ./docker-data/dms/config/dovecot/10-plugin.conf:/etc/dovecot/conf.d/10-plugin.conf:ro ... ./docker-data/dms/config/dovecot/10-plugin.conf : mail_plugins = $mail_plugins fts fts_solr plugin { fts = solr fts_autoindex = yes fts_solr = url=http://solr:8983/solr/dovecot/ } Recreate containers: docker-compose down ; docker-compose up -d Flag all user mailbox FTS indexes as invalid, so they are rescanned on demand when they are next searched: docker-compose exec mailserver doveadm fts rescan -A","title":"Setup"},{"location":"config/advanced/full-text-search/#further-discussion","text":"See #905","title":"Further Discussion"},{"location":"config/advanced/ipv6/","text":"Background If your container host supports IPv6, then docker-mailserver will automatically accept IPv6 connections by way of the docker host's IPv6. However, incoming mail will fail SPF checks because they will appear to come from the IPv4 gateway that docker is using to proxy the IPv6 connection ( 172.20.0.1 is the gateway). This can be solved by supporting IPv6 connections all the way to the docker-mailserver container. Setup steps +++ b/serv/docker-compose.yml @@ -1,4 +1,4 @@ -version: '2' +version: '2.1' @@ -32,6 +32,16 @@ services: + ipv6nat: + image: robbertkl/ipv6nat + restart: always + network_mode: \"host\" + cap_add: + - NET_ADMIN + - SYS_MODULE + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - /lib/modules:/lib/modules:ro @@ -306,4 +316,13 @@ networks: + default: + driver: bridge + enable_ipv6: true + ipam: + driver: default + config: + - subnet: fd00:0123:4567::/48 + gateway: fd00:0123:4567::1 Further Discussion See #1438","title":"IPv6"},{"location":"config/advanced/ipv6/#background","text":"If your container host supports IPv6, then docker-mailserver will automatically accept IPv6 connections by way of the docker host's IPv6. However, incoming mail will fail SPF checks because they will appear to come from the IPv4 gateway that docker is using to proxy the IPv6 connection ( 172.20.0.1 is the gateway). This can be solved by supporting IPv6 connections all the way to the docker-mailserver container.","title":"Background"},{"location":"config/advanced/ipv6/#setup-steps","text":"+++ b/serv/docker-compose.yml @@ -1,4 +1,4 @@ -version: '2' +version: '2.1' @@ -32,6 +32,16 @@ services: + ipv6nat: + image: robbertkl/ipv6nat + restart: always + network_mode: \"host\" + cap_add: + - NET_ADMIN + - SYS_MODULE + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - /lib/modules:/lib/modules:ro @@ -306,4 +316,13 @@ networks: + default: + driver: bridge + enable_ipv6: true + ipam: + driver: default + config: + - subnet: fd00:0123:4567::/48 + gateway: fd00:0123:4567::1","title":"Setup steps"},{"location":"config/advanced/ipv6/#further-discussion","text":"See #1438","title":"Further Discussion"},{"location":"config/advanced/kubernetes/","text":"Introduction This article describes how to deploy docker-mailserver to Kubernetes. Please note that there is also a Helm chart available. Requirements We assume basic knowledge about Kubernetes from the reader. Moreover, we assume the reader to have a basic understanding of mail servers. Ideally, the reader has deployed docker-mailserver before in an easier setup with Docker (Compose). About Support for Kubernetes Please note that Kubernetes is not officially supported and we do not build images specifically designed for it. When opening an issue, please remember that only Docker & Docker Compose are officially supported. This content is entirely community-supported. If you find errors, please open an issue and provide a PR. Manifests Configuration We want to provide the basic configuration in the form of environment variables with a ConfigMap . Note that this is just an example configuration; tune the ConfigMap to your needs. --- apiVersion : v1 kind : ConfigMap metadata : name : mailserver.environment immutable : false data : TLS_LEVEL : modern POSTSCREEN_ACTION : drop OVERRIDE_HOSTNAME : mail.example.com FAIL2BAN_BLOCKTYPE : drop POSTMASTER_ADDRESS : postmaster@example.com UPDATE_CHECK_INTERVAL : 10d POSTFIX_INET_PROTOCOLS : ipv4 ONE_DIR : '1' ENABLE_CLAMAV : '1' ENABLE_POSTGREY : '0' ENABLE_FAIL2BAN : '1' AMAVIS_LOGLEVEL : '-1' SPOOF_PROTECTION : '1' MOVE_SPAM_TO_JUNK : '1' ENABLE_UPDATE_CHECK : '1' ENABLE_SPAMASSASSIN : '1' SUPERVISOR_LOGLEVEL : warn SPAMASSASSIN_SPAM_TO_INBOX : '1' # here, we provide an example for the SSL configuration SSL_TYPE : manual SSL_CERT_PATH : /secrets/ssl/rsa/tls.crt SSL_KEY_PATH : /secrets/ssl/rsa/tls.key We can also make use of user-provided configuration files, e.g. user-patches.sh , postfix-accounts.cf and more, to adjust docker-mailserver to our likings. We encourage you to have a look at Kustomize for creating ConfigMap s from multiple files, but for now, we will provide a simple, hand-written example. This example is absolutely minimal and only goes to show what can be done. --- apiVersion : v1 kind : ConfigMap metadata : name : mailserver.files data : postfix-accounts.cf : | test@example.com|{SHA512-CRYPT}$6$someHashValueHere other@example.com|{SHA512-CRYPT}$6$someOtherHashValueHere Static Configuration With the configuration shown above, you can not dynamically add accounts as the configuration file mounted into the mail server can not be written to. Use persistent volumes for production deployments. Persistence Thereafter, we need persistence for our data. Make sure you have a storage provisioner and that you choose the correct storageClassName . --- apiVersion : v1 kind : PersistentVolumeClaim metadata : name : data spec : storageClassName : local-path accessModes : - ReadWriteOnce resources : requests : storage : 25Gi Service A Service is required for getting the traffic to the pod itself. The service is somewhat crucial. Its configuration determines whether the original IP from the sender will be kept. More about this further down below . The configuration you're seeing does keep the original IP, but you will not be able to scale this way. We have chosen to go this route in this case because we think most Kubernetes users will only want to have one instance. --- apiVersion : v1 kind : Service metadata : name : mailserver labels : app : mailserver spec : type : LoadBalancer selector : app : mailserver ports : # Transfer - name : transfer port : 25 targetPort : transfer protocol : TCP # ESMTP with implicit TLS - name : esmtp-implicit port : 465 targetPort : esmtp-implicit protocol : TCP # ESMTP with explicit TLS (STARTTLS) - name : esmtp-explicit port : 587 targetPort : esmtp-explicit protocol : TCP # IMAPS with implicit TLS - name : imap-implicit port : 993 targetPort : imap-implicit protocol : TCP Deployments Last but not least, the Deployment becomes the most complex component. It instructs Kubernetes how to run the docker-mailserver container and how to apply your ConfigMaps , persisted storage, etc. Additionally, we can set options to enforce runtime security here. --- apiVersion : apps/v1 kind : Deployment metadata : name : mailserver annotations : ignore-check.kube-linter.io/run-as-non-root : >- 'mailserver' needs to run as root ignore-check.kube-linter.io/privileged-ports : >- 'mailserver' needs privilegdes ports ignore-check.kube-linter.io/no-read-only-root-fs : >- There are too many files written to make The root FS read-only spec : replicas : 1 selector : matchLabels : app : mailserver template : metadata : labels : app : mailserver annotations : container.apparmor.security.beta.kubernetes.io/mailserver : runtime/default spec : hostname : mail containers : - name : mailserver image : docker.io/mailserver/docker-mailserver:latest imagePullPolicy : IfNotPresent securityContext : allowPrivilegeEscalation : false readOnlyRootFilesystem : false runAsUser : 0 runAsGroup : 0 runAsNonRoot : false privileged : false capabilities : add : # file permission capabilities - CHOWN - FOWNER - MKNOD - SETGID - SETUID - DAC_OVERRIDE # network capabilities - NET_ADMIN # needed for F2B - NET_RAW # needed for F2B - NET_BIND_SERVICE # miscellaneous capabilities - SYS_CHROOT - KILL drop : [ ALL ] seccompProfile : type : RuntimeDefault # You want to tune this to your needs. If you disable ClamAV, # you can use less RAM and CPU. This becomes important in # case you're low on resources and Kubernetes refuses to # schedule new pods. resources : limits : memory : 4Gi cpu : 1500m requests : memory : 2Gi cpu : 600m volumeMounts : - name : files subPath : postfix-accounts.cf mountPath : /tmp/docker-mailserver/postfix-accounts.cf readOnly : true # PVCs - name : data mountPath : /var/mail subPath : data readOnly : false - name : data mountPath : /var/mail-state subPath : state readOnly : false - name : data mountPath : /var/log/mail subPath : log readOnly : false # certificates - name : certificates-rsa mountPath : /secrets/ssl/rsa/ readOnly : true # other - name : tmp-files mountPath : /tmp readOnly : false ports : - name : transfer containerPort : 25 protocol : TCP - name : esmtp-implicit containerPort : 465 protocol : TCP - name : esmtp-explicit containerPort : 587 - name : imap-implicit containerPort : 993 protocol : TCP envFrom : - configMapRef : name : mailserver.environment restartPolicy : Always volumes : # configuration files - name : files configMap : name : mailserver.files # PVCs - name : data persistentVolumeClaim : claimName : data # certificates - name : certificates-rsa secret : secretName : mail-tls-certificate-rsa items : - key : tls.key path : tls.key - key : tls.crt path : tls.crt # other - name : tmp-files emptyDir : {} Certificates - An Example In this example, we use cert-manager to supply RSA certificates. You can also supply RSA certificates as fallback certificates, which docker-mailserver supports out of the box with SSL_ALT_CERT_PATH and SSL_ALT_KEY_PATH , and provide ECDSA as the proper certificates. --- apiVersion : cert-manager.io/v1 kind : Certificate metadata : name : mail-tls-certificate-rsa spec : secretName : mail-tls-certificate-rsa isCA : false privateKey : algorithm : RSA encoding : PKCS1 size : 2048 dnsNames : [ mail.example.com ] issuerRef : name : mail-issuer kind : Issuer Attention You will need to have cert-manager configured. Especially the issue will need to be configured. Since we do not know how you want or need your certificates to be supplied, we do not provide more configuration here. The documentation for cert-manager is excellent. Sensitive Data Sensitive Data For storing OpenDKIM keys, TLS certificates or any sort of sensitive data, you should be using Secret s. You can mount secrets like ConfigMap s and use them the same way. The TLS docs page provides guidance when it comes to certificates and transport layer security. Always provide sensitive information vai Secrets . Exposing your Mail-Server to the Outside World The more difficult part with Kubernetes is to expose a deployed docker-mailserver to the outside world. Kubernetes provides multiple ways for doing that; each has downsides and complexity. The major problem with exposing docker-mailserver to outside world in Kubernetes is to preserve the real client IP . The real client IP is required by docker-mailserver for performing IP-based SPF checks and spam checks. If you do not require SPF checks for incoming mails, you may disable them in your Postfix configuration by dropping the line that states: check_policy_service unix:private/policyd-spf . The easiest approach was covered above, using externalTrafficPolicy : Local , which disables the service proxy, but makes the service local as well (which does not scale). This approach only works when you are given the correct (that is, a public and routable) IP address by a load balancer (like MetalLB). In this sense, the approach above is similar to the next example below. We want to provide you with a few alternatives too. But we also want to communicate the idea of another simple method: you could use a load-balancer without an external IP and DNAT the network traffic to the mail-server. After all, this does not interfere with SPF checks because it keeps the origin IP address. If no dedicated external IP address is available, you could try the latter approach, if one is available, use the former. External IPs Service The simplest way is to expose docker-mailserver as a Service with external IPs . This is very similar to the approach taken above. Here, an external IP is given to the service directly by you. With the approach above, you tell your load-balancer to do this. --- apiVersion : v1 kind : Service metadata : name : mailserver labels : app : mailserver spec : selector : app : mailserver ports : - name : smtp port : 25 targetPort : smtp # ... externalIPs : - 80.11.12.10 This approach does not preserve the real client IP, so SPF check of incoming mail will fail. requires you to specify the exposed IPs explicitly. Proxy port to Service The proxy pod helps to avoid the necessity of specifying external IPs explicitly. This comes at the cost of complexity; you must deploy a proxy pod on each Node you want to expose docker-mailserver on. This approach does not preserve the real client IP, so SPF check of incoming mail will fail. Bind to concrete Node and use host network One way to preserve the real client IP is to use hostPort and hostNetwork: true . This comes at the cost of availability; you can reach docker-mailserver from the outside world only via IPs of Node where docker-mailserver is deployed. --- apiVersion : extensions/v1beta1 kind : Deployment metadata : name : mailserver # ... spec : hostNetwork : true # ... containers : # ... ports : - name : smtp containerPort : 25 hostPort : 25 - name : smtp-auth containerPort : 587 hostPort : 587 - name : imap-secure containerPort : 993 hostPort : 993 # ... With this approach, it is not possible to access docker-mailserver via other cluster Nodes, only via the Node docker-mailserver was deployed at. every Port within the Container is exposed on the Host side. Proxy Port to Service via PROXY Protocol This way is ideologically the same as using a proxy pod , but instead of a separate proxy pod, you configure your ingress to proxy TCP traffic to the docker-mailserver pod using the PROXY protocol, which preserves the real client IP. Configure your Ingress With an NGINX ingress controller , set externalTrafficPolicy: Local for its service, and add the following to the TCP services config map (as described here ): 25 : \"mailserver/mailserver:25::PROXY\" 465 : \"mailserver/mailserver:465::PROXY\" 587 : \"mailserver/mailserver:587::PROXY\" 993 : \"mailserver/mailserver:993::PROXY\" HAProxy With HAProxy , the configuration should look similar to the above. If you know what it actually looks like, add an example here. Configure the Mailserver Then, configure both Postfix and Dovecot to expect the PROXY protocol: HAProxy Example kind : ConfigMap apiVersion : v1 metadata : name : mailserver.config labels : app : mailserver data : postfix-main.cf : | postscreen_upstream_proxy_protocol = haproxy postfix-master.cf : | smtp/inet/postscreen_upstream_proxy_protocol=haproxy submission/inet/smtpd_upstream_proxy_protocol=haproxy smtps/inet/smtpd_upstream_proxy_protocol=haproxy dovecot.cf : | # Assuming your ingress controller is bound to 10.0.0.0/8 haproxy_trusted_networks = 10.0.0.0/8, 127.0.0.0/8 service imap-login { inet_listener imap { haproxy = yes } inet_listener imaps { haproxy = yes } } # ... --- kind : Deployment apiVersion : extensions/v1beta1 metadata : name : mailserver spec : template : spec : containers : - name : docker-mailserver volumeMounts : - name : config subPath : postfix-main.cf mountPath : /tmp/docker-mailserver/postfix-main.cf readOnly : true - name : config subPath : postfix-master.cf mountPath : /tmp/docker-mailserver/postfix-master.cf readOnly : true - name : config subPath : dovecot.cf mountPath : /tmp/docker-mailserver/dovecot.cf readOnly : true With this approach, it is not possible to access docker-mailserver via cluster-DNS, as the PROXY protocol is required for incoming connections.","title":"Kubernetes"},{"location":"config/advanced/kubernetes/#introduction","text":"This article describes how to deploy docker-mailserver to Kubernetes. Please note that there is also a Helm chart available. Requirements We assume basic knowledge about Kubernetes from the reader. Moreover, we assume the reader to have a basic understanding of mail servers. Ideally, the reader has deployed docker-mailserver before in an easier setup with Docker (Compose). About Support for Kubernetes Please note that Kubernetes is not officially supported and we do not build images specifically designed for it. When opening an issue, please remember that only Docker & Docker Compose are officially supported. This content is entirely community-supported. If you find errors, please open an issue and provide a PR.","title":"Introduction"},{"location":"config/advanced/kubernetes/#manifests","text":"","title":"Manifests"},{"location":"config/advanced/kubernetes/#configuration","text":"We want to provide the basic configuration in the form of environment variables with a ConfigMap . Note that this is just an example configuration; tune the ConfigMap to your needs. --- apiVersion : v1 kind : ConfigMap metadata : name : mailserver.environment immutable : false data : TLS_LEVEL : modern POSTSCREEN_ACTION : drop OVERRIDE_HOSTNAME : mail.example.com FAIL2BAN_BLOCKTYPE : drop POSTMASTER_ADDRESS : postmaster@example.com UPDATE_CHECK_INTERVAL : 10d POSTFIX_INET_PROTOCOLS : ipv4 ONE_DIR : '1' ENABLE_CLAMAV : '1' ENABLE_POSTGREY : '0' ENABLE_FAIL2BAN : '1' AMAVIS_LOGLEVEL : '-1' SPOOF_PROTECTION : '1' MOVE_SPAM_TO_JUNK : '1' ENABLE_UPDATE_CHECK : '1' ENABLE_SPAMASSASSIN : '1' SUPERVISOR_LOGLEVEL : warn SPAMASSASSIN_SPAM_TO_INBOX : '1' # here, we provide an example for the SSL configuration SSL_TYPE : manual SSL_CERT_PATH : /secrets/ssl/rsa/tls.crt SSL_KEY_PATH : /secrets/ssl/rsa/tls.key We can also make use of user-provided configuration files, e.g. user-patches.sh , postfix-accounts.cf and more, to adjust docker-mailserver to our likings. We encourage you to have a look at Kustomize for creating ConfigMap s from multiple files, but for now, we will provide a simple, hand-written example. This example is absolutely minimal and only goes to show what can be done. --- apiVersion : v1 kind : ConfigMap metadata : name : mailserver.files data : postfix-accounts.cf : | test@example.com|{SHA512-CRYPT}$6$someHashValueHere other@example.com|{SHA512-CRYPT}$6$someOtherHashValueHere Static Configuration With the configuration shown above, you can not dynamically add accounts as the configuration file mounted into the mail server can not be written to. Use persistent volumes for production deployments.","title":"Configuration"},{"location":"config/advanced/kubernetes/#persistence","text":"Thereafter, we need persistence for our data. Make sure you have a storage provisioner and that you choose the correct storageClassName . --- apiVersion : v1 kind : PersistentVolumeClaim metadata : name : data spec : storageClassName : local-path accessModes : - ReadWriteOnce resources : requests : storage : 25Gi","title":"Persistence"},{"location":"config/advanced/kubernetes/#service","text":"A Service is required for getting the traffic to the pod itself. The service is somewhat crucial. Its configuration determines whether the original IP from the sender will be kept. More about this further down below . The configuration you're seeing does keep the original IP, but you will not be able to scale this way. We have chosen to go this route in this case because we think most Kubernetes users will only want to have one instance. --- apiVersion : v1 kind : Service metadata : name : mailserver labels : app : mailserver spec : type : LoadBalancer selector : app : mailserver ports : # Transfer - name : transfer port : 25 targetPort : transfer protocol : TCP # ESMTP with implicit TLS - name : esmtp-implicit port : 465 targetPort : esmtp-implicit protocol : TCP # ESMTP with explicit TLS (STARTTLS) - name : esmtp-explicit port : 587 targetPort : esmtp-explicit protocol : TCP # IMAPS with implicit TLS - name : imap-implicit port : 993 targetPort : imap-implicit protocol : TCP","title":"Service"},{"location":"config/advanced/kubernetes/#deployments","text":"Last but not least, the Deployment becomes the most complex component. It instructs Kubernetes how to run the docker-mailserver container and how to apply your ConfigMaps , persisted storage, etc. Additionally, we can set options to enforce runtime security here. --- apiVersion : apps/v1 kind : Deployment metadata : name : mailserver annotations : ignore-check.kube-linter.io/run-as-non-root : >- 'mailserver' needs to run as root ignore-check.kube-linter.io/privileged-ports : >- 'mailserver' needs privilegdes ports ignore-check.kube-linter.io/no-read-only-root-fs : >- There are too many files written to make The root FS read-only spec : replicas : 1 selector : matchLabels : app : mailserver template : metadata : labels : app : mailserver annotations : container.apparmor.security.beta.kubernetes.io/mailserver : runtime/default spec : hostname : mail containers : - name : mailserver image : docker.io/mailserver/docker-mailserver:latest imagePullPolicy : IfNotPresent securityContext : allowPrivilegeEscalation : false readOnlyRootFilesystem : false runAsUser : 0 runAsGroup : 0 runAsNonRoot : false privileged : false capabilities : add : # file permission capabilities - CHOWN - FOWNER - MKNOD - SETGID - SETUID - DAC_OVERRIDE # network capabilities - NET_ADMIN # needed for F2B - NET_RAW # needed for F2B - NET_BIND_SERVICE # miscellaneous capabilities - SYS_CHROOT - KILL drop : [ ALL ] seccompProfile : type : RuntimeDefault # You want to tune this to your needs. If you disable ClamAV, # you can use less RAM and CPU. This becomes important in # case you're low on resources and Kubernetes refuses to # schedule new pods. resources : limits : memory : 4Gi cpu : 1500m requests : memory : 2Gi cpu : 600m volumeMounts : - name : files subPath : postfix-accounts.cf mountPath : /tmp/docker-mailserver/postfix-accounts.cf readOnly : true # PVCs - name : data mountPath : /var/mail subPath : data readOnly : false - name : data mountPath : /var/mail-state subPath : state readOnly : false - name : data mountPath : /var/log/mail subPath : log readOnly : false # certificates - name : certificates-rsa mountPath : /secrets/ssl/rsa/ readOnly : true # other - name : tmp-files mountPath : /tmp readOnly : false ports : - name : transfer containerPort : 25 protocol : TCP - name : esmtp-implicit containerPort : 465 protocol : TCP - name : esmtp-explicit containerPort : 587 - name : imap-implicit containerPort : 993 protocol : TCP envFrom : - configMapRef : name : mailserver.environment restartPolicy : Always volumes : # configuration files - name : files configMap : name : mailserver.files # PVCs - name : data persistentVolumeClaim : claimName : data # certificates - name : certificates-rsa secret : secretName : mail-tls-certificate-rsa items : - key : tls.key path : tls.key - key : tls.crt path : tls.crt # other - name : tmp-files emptyDir : {}","title":"Deployments"},{"location":"config/advanced/kubernetes/#certificates-an-example","text":"In this example, we use cert-manager to supply RSA certificates. You can also supply RSA certificates as fallback certificates, which docker-mailserver supports out of the box with SSL_ALT_CERT_PATH and SSL_ALT_KEY_PATH , and provide ECDSA as the proper certificates. --- apiVersion : cert-manager.io/v1 kind : Certificate metadata : name : mail-tls-certificate-rsa spec : secretName : mail-tls-certificate-rsa isCA : false privateKey : algorithm : RSA encoding : PKCS1 size : 2048 dnsNames : [ mail.example.com ] issuerRef : name : mail-issuer kind : Issuer Attention You will need to have cert-manager configured. Especially the issue will need to be configured. Since we do not know how you want or need your certificates to be supplied, we do not provide more configuration here. The documentation for cert-manager is excellent.","title":"Certificates - An Example"},{"location":"config/advanced/kubernetes/#sensitive-data","text":"Sensitive Data For storing OpenDKIM keys, TLS certificates or any sort of sensitive data, you should be using Secret s. You can mount secrets like ConfigMap s and use them the same way. The TLS docs page provides guidance when it comes to certificates and transport layer security. Always provide sensitive information vai Secrets .","title":"Sensitive Data"},{"location":"config/advanced/kubernetes/#exposing-your-mail-server-to-the-outside-world","text":"The more difficult part with Kubernetes is to expose a deployed docker-mailserver to the outside world. Kubernetes provides multiple ways for doing that; each has downsides and complexity. The major problem with exposing docker-mailserver to outside world in Kubernetes is to preserve the real client IP . The real client IP is required by docker-mailserver for performing IP-based SPF checks and spam checks. If you do not require SPF checks for incoming mails, you may disable them in your Postfix configuration by dropping the line that states: check_policy_service unix:private/policyd-spf . The easiest approach was covered above, using externalTrafficPolicy : Local , which disables the service proxy, but makes the service local as well (which does not scale). This approach only works when you are given the correct (that is, a public and routable) IP address by a load balancer (like MetalLB). In this sense, the approach above is similar to the next example below. We want to provide you with a few alternatives too. But we also want to communicate the idea of another simple method: you could use a load-balancer without an external IP and DNAT the network traffic to the mail-server. After all, this does not interfere with SPF checks because it keeps the origin IP address. If no dedicated external IP address is available, you could try the latter approach, if one is available, use the former.","title":"Exposing your Mail-Server to the Outside World"},{"location":"config/advanced/kubernetes/#external-ips-service","text":"The simplest way is to expose docker-mailserver as a Service with external IPs . This is very similar to the approach taken above. Here, an external IP is given to the service directly by you. With the approach above, you tell your load-balancer to do this. --- apiVersion : v1 kind : Service metadata : name : mailserver labels : app : mailserver spec : selector : app : mailserver ports : - name : smtp port : 25 targetPort : smtp # ... externalIPs : - 80.11.12.10 This approach does not preserve the real client IP, so SPF check of incoming mail will fail. requires you to specify the exposed IPs explicitly.","title":"External IPs Service"},{"location":"config/advanced/kubernetes/#proxy-port-to-service","text":"The proxy pod helps to avoid the necessity of specifying external IPs explicitly. This comes at the cost of complexity; you must deploy a proxy pod on each Node you want to expose docker-mailserver on. This approach does not preserve the real client IP, so SPF check of incoming mail will fail.","title":"Proxy port to Service"},{"location":"config/advanced/kubernetes/#bind-to-concrete-node-and-use-host-network","text":"One way to preserve the real client IP is to use hostPort and hostNetwork: true . This comes at the cost of availability; you can reach docker-mailserver from the outside world only via IPs of Node where docker-mailserver is deployed. --- apiVersion : extensions/v1beta1 kind : Deployment metadata : name : mailserver # ... spec : hostNetwork : true # ... containers : # ... ports : - name : smtp containerPort : 25 hostPort : 25 - name : smtp-auth containerPort : 587 hostPort : 587 - name : imap-secure containerPort : 993 hostPort : 993 # ... With this approach, it is not possible to access docker-mailserver via other cluster Nodes, only via the Node docker-mailserver was deployed at. every Port within the Container is exposed on the Host side.","title":"Bind to concrete Node and use host network"},{"location":"config/advanced/kubernetes/#proxy-port-to-service-via-proxy-protocol","text":"This way is ideologically the same as using a proxy pod , but instead of a separate proxy pod, you configure your ingress to proxy TCP traffic to the docker-mailserver pod using the PROXY protocol, which preserves the real client IP.","title":"Proxy Port to Service via PROXY Protocol"},{"location":"config/advanced/kubernetes/#configure-your-ingress","text":"With an NGINX ingress controller , set externalTrafficPolicy: Local for its service, and add the following to the TCP services config map (as described here ): 25 : \"mailserver/mailserver:25::PROXY\" 465 : \"mailserver/mailserver:465::PROXY\" 587 : \"mailserver/mailserver:587::PROXY\" 993 : \"mailserver/mailserver:993::PROXY\" HAProxy With HAProxy , the configuration should look similar to the above. If you know what it actually looks like, add an example here.","title":"Configure your Ingress"},{"location":"config/advanced/kubernetes/#configure-the-mailserver","text":"Then, configure both Postfix and Dovecot to expect the PROXY protocol: HAProxy Example kind : ConfigMap apiVersion : v1 metadata : name : mailserver.config labels : app : mailserver data : postfix-main.cf : | postscreen_upstream_proxy_protocol = haproxy postfix-master.cf : | smtp/inet/postscreen_upstream_proxy_protocol=haproxy submission/inet/smtpd_upstream_proxy_protocol=haproxy smtps/inet/smtpd_upstream_proxy_protocol=haproxy dovecot.cf : | # Assuming your ingress controller is bound to 10.0.0.0/8 haproxy_trusted_networks = 10.0.0.0/8, 127.0.0.0/8 service imap-login { inet_listener imap { haproxy = yes } inet_listener imaps { haproxy = yes } } # ... --- kind : Deployment apiVersion : extensions/v1beta1 metadata : name : mailserver spec : template : spec : containers : - name : docker-mailserver volumeMounts : - name : config subPath : postfix-main.cf mountPath : /tmp/docker-mailserver/postfix-main.cf readOnly : true - name : config subPath : postfix-master.cf mountPath : /tmp/docker-mailserver/postfix-master.cf readOnly : true - name : config subPath : dovecot.cf mountPath : /tmp/docker-mailserver/dovecot.cf readOnly : true With this approach, it is not possible to access docker-mailserver via cluster-DNS, as the PROXY protocol is required for incoming connections.","title":"Configure the Mailserver"},{"location":"config/advanced/mail-fetchmail/","text":"To enable the fetchmail service to retrieve e-mails set the environment variable ENABLE_FETCHMAIL to 1 . Your docker-compose.yml file should look like following snippet: environment : - ENABLE_FETCHMAIL=1 - FETCHMAIL_POLL=300 Generate a file called fetchmail.cf and place it in the docker-data/dms/config/ folder. Your docker-mailserver folder should look like this example: \u251c\u2500\u2500 docker-data/dms/config \u2502 \u251c\u2500\u2500 dovecot.cf \u2502 \u251c\u2500\u2500 fetchmail.cf \u2502 \u251c\u2500\u2500 postfix-accounts.cf \u2502 \u2514\u2500\u2500 postfix-virtual.cf \u251c\u2500\u2500 docker-compose.yml \u2514\u2500\u2500 README.md Configuration A detailed description of the configuration options can be found in the online version of the manual page . IMAP Configuration Example poll 'imap.gmail.com' proto imap user 'username' pass 'secret' is 'user1@example.com' ssl POP3 Configuration Example poll 'pop3.gmail.com' proto pop3 user 'username' pass 'secret' is 'user2@example.com' ssl Caution Don\u2019t forget the last line! ( eg: is 'user1@example.com' ). After is , you have to specify an email address from the configuration file: docker-data/dms/config/postfix-accounts.cf . More details how to configure fetchmail can be found in the fetchmail man page in the chapter \u201cThe run control file\u201d . Polling Interval By default the fetchmail service searches every 5 minutes for new mails on your external mail accounts. You can override this default value by changing the ENV variable FETCHMAIL_POLL : environment : - FETCHMAIL_POLL=60 You must specify a numeric argument which is a polling interval in seconds. The example above polls every minute for new mails. Debugging To debug your fetchmail.cf configuration run this command: ./setup.sh debug fetchmail For more informations about the configuration script setup.sh read the corresponding docs . Here a sample output of ./setup.sh debug fetchmail : fetchmail: 6.3.26 querying outlook.office365.com (protocol POP3) at Mon Aug 29 22:11:09 2016: poll started Trying to connect to 132.245.48.18/995...connected. fetchmail: Server certificate: fetchmail: Issuer Organization: Microsoft Corporation fetchmail: Issuer CommonName: Microsoft IT SSL SHA2 fetchmail: Subject CommonName: outlook.com fetchmail: Subject Alternative Name: outlook.com fetchmail: Subject Alternative Name: *.outlook.com fetchmail: Subject Alternative Name: office365.com fetchmail: Subject Alternative Name: *.office365.com fetchmail: Subject Alternative Name: *.live.com fetchmail: Subject Alternative Name: *.internal.outlook.com fetchmail: Subject Alternative Name: *.outlook.office365.com fetchmail: Subject Alternative Name: outlook.office.com fetchmail: Subject Alternative Name: attachment.outlook.office.net fetchmail: Subject Alternative Name: attachment.outlook.officeppe.net fetchmail: Subject Alternative Name: *.office.com fetchmail: outlook.office365.com key fingerprint: 3A:A4:58:42:56:CD:BD:11:19:5B:CF:1E:85:16:8E:4D fetchmail: POP3< +OK The Microsoft Exchange POP3 service is ready. [SABFADEAUABSADAAMQBDAEEAMAAwADAANwAuAGUAdQByAHAAcgBkADAAMQAuAHAAcgBvAGQALgBlAHgAYwBoAGEAbgBnAGUAbABhAGIAcwAuAGMAbwBtAA==] fetchmail: POP3> CAPA fetchmail: POP3< +OK fetchmail: POP3< TOP fetchmail: POP3< UIDL fetchmail: POP3< SASL PLAIN fetchmail: POP3< USER fetchmail: POP3< . fetchmail: POP3> USER user1@outlook.com fetchmail: POP3< +OK fetchmail: POP3> PASS * fetchmail: POP3< +OK User successfully logged on. fetchmail: POP3> STAT fetchmail: POP3< +OK 0 0 fetchmail: No mail for user1@outlook.com at outlook.office365.com fetchmail: POP3> QUIT fetchmail: POP3< +OK Microsoft Exchange Server 2016 POP3 server signing off. fetchmail: 6.3.26 querying outlook.office365.com (protocol POP3) at Mon Aug 29 22:11:11 2016: poll completed fetchmail: normal termination, status 1","title":"Email Gathering with Fetchmail"},{"location":"config/advanced/mail-fetchmail/#configuration","text":"A detailed description of the configuration options can be found in the online version of the manual page .","title":"Configuration"},{"location":"config/advanced/mail-fetchmail/#imap-configuration","text":"Example poll 'imap.gmail.com' proto imap user 'username' pass 'secret' is 'user1@example.com' ssl","title":"IMAP Configuration"},{"location":"config/advanced/mail-fetchmail/#pop3-configuration","text":"Example poll 'pop3.gmail.com' proto pop3 user 'username' pass 'secret' is 'user2@example.com' ssl Caution Don\u2019t forget the last line! ( eg: is 'user1@example.com' ). After is , you have to specify an email address from the configuration file: docker-data/dms/config/postfix-accounts.cf . More details how to configure fetchmail can be found in the fetchmail man page in the chapter \u201cThe run control file\u201d .","title":"POP3 Configuration"},{"location":"config/advanced/mail-fetchmail/#polling-interval","text":"By default the fetchmail service searches every 5 minutes for new mails on your external mail accounts. You can override this default value by changing the ENV variable FETCHMAIL_POLL : environment : - FETCHMAIL_POLL=60 You must specify a numeric argument which is a polling interval in seconds. The example above polls every minute for new mails.","title":"Polling Interval"},{"location":"config/advanced/mail-fetchmail/#debugging","text":"To debug your fetchmail.cf configuration run this command: ./setup.sh debug fetchmail For more informations about the configuration script setup.sh read the corresponding docs . Here a sample output of ./setup.sh debug fetchmail : fetchmail: 6.3.26 querying outlook.office365.com (protocol POP3) at Mon Aug 29 22:11:09 2016: poll started Trying to connect to 132.245.48.18/995...connected. fetchmail: Server certificate: fetchmail: Issuer Organization: Microsoft Corporation fetchmail: Issuer CommonName: Microsoft IT SSL SHA2 fetchmail: Subject CommonName: outlook.com fetchmail: Subject Alternative Name: outlook.com fetchmail: Subject Alternative Name: *.outlook.com fetchmail: Subject Alternative Name: office365.com fetchmail: Subject Alternative Name: *.office365.com fetchmail: Subject Alternative Name: *.live.com fetchmail: Subject Alternative Name: *.internal.outlook.com fetchmail: Subject Alternative Name: *.outlook.office365.com fetchmail: Subject Alternative Name: outlook.office.com fetchmail: Subject Alternative Name: attachment.outlook.office.net fetchmail: Subject Alternative Name: attachment.outlook.officeppe.net fetchmail: Subject Alternative Name: *.office.com fetchmail: outlook.office365.com key fingerprint: 3A:A4:58:42:56:CD:BD:11:19:5B:CF:1E:85:16:8E:4D fetchmail: POP3< +OK The Microsoft Exchange POP3 service is ready. [SABFADEAUABSADAAMQBDAEEAMAAwADAANwAuAGUAdQByAHAAcgBkADAAMQAuAHAAcgBvAGQALgBlAHgAYwBoAGEAbgBnAGUAbABhAGIAcwAuAGMAbwBtAA==] fetchmail: POP3> CAPA fetchmail: POP3< +OK fetchmail: POP3< TOP fetchmail: POP3< UIDL fetchmail: POP3< SASL PLAIN fetchmail: POP3< USER fetchmail: POP3< . fetchmail: POP3> USER user1@outlook.com fetchmail: POP3< +OK fetchmail: POP3> PASS * fetchmail: POP3< +OK User successfully logged on. fetchmail: POP3> STAT fetchmail: POP3< +OK 0 0 fetchmail: No mail for user1@outlook.com at outlook.office365.com fetchmail: POP3> QUIT fetchmail: POP3< +OK Microsoft Exchange Server 2016 POP3 server signing off. fetchmail: 6.3.26 querying outlook.office365.com (protocol POP3) at Mon Aug 29 22:11:11 2016: poll completed fetchmail: normal termination, status 1","title":"Debugging"},{"location":"config/advanced/mail-sieve/","text":"User-Defined Sieve Filters Sieve allows to specify filtering rules for incoming emails that allow for example sorting mails into different folders depending on the title of an email. There are global and user specific filters which are filtering the incoming emails in the following order: Global-before -> User specific -> Global-after Global filters are applied to EVERY incoming mail for EVERY email address. To specify a global Sieve filter provide a docker-data/dms/config/before.dovecot.sieve or a docker-data/dms/config/after.dovecot.sieve file with your filter rules. If any filter in this filtering chain discards an incoming mail, the delivery process will stop as well and the mail will not reach any following filters(e.g. global-before stops an incoming spam mail: The mail will get discarded and a user-specific filter won't get applied.) To specify a user-defined Sieve filter place a .dovecot.sieve file into a virtual user's mail folder e.g. /var/mail/example.com/user1/.dovecot.sieve . If this file exists dovecot will apply the filtering rules. It's even possible to install a user provided Sieve filter at startup during users setup: simply include a Sieve file in the docker-data/dms/config/ path for each user login that needs a filter. The file name provided should be in the form .dovecot.sieve , so for example for user1@example.com you should provide a Sieve file named docker-data/dms/config/user1@example.com.dovecot.sieve . An example of a sieve filter that moves mails to a folder INBOX/spam depending on the sender address: Example require [ \"fileinto\" , \"reject\" ]; if address :contains [ \"From\" ] \"spam@spam.com\" { fileinto \"INBOX.spam\" ; } else { keep ; } Warning That folders have to exist beforehand if sieve should move them. Another example of a sieve filter that forward mails to a different address: Example require [ \"copy\" ]; redirect :copy \"user2@not-example.com\" ; Just forward all incoming emails and do not save them locally: Example redirect \"user2@not-example.com\" ; You can also use external programs to filter or pipe (process) messages by adding executable scripts in docker-data/dms/config/sieve-pipe or docker-data/dms/config/sieve-filter . This can be used in lieu of a local alias file, for instance to forward an email to a webservice. These programs can then be referenced by filename, by all users. Note that the process running the scripts run as a privileged user. For further information see Dovecot's wiki . require [ \"vnd.dovecot.pipe\" ]; pipe \"external-program\" ; For more examples or a detailed description of the Sieve language have a look at the official site . Other resources are available on the internet where you can find several examples . Automatic Sorting Based on Subaddresses It is possible to sort subaddresses such as user+mailing-lists@example.com into a corresponding folder (here: INBOX/Mailing-lists ) automatically. require [ \"envelope\" , \"fileinto\" , \"mailbox\" , \"subaddress\" , \"variables\" ]; if envelope :detail :matches \"to\" \"*\" { set :lower :upperfirst \"tag\" \"${1}\" ; if mailbox exists \"INBOX.${1}\" { fileinto \"INBOX.${1}\" ; } else { fileinto :create \"INBOX.${tag}\" ; } } Manage Sieve The Manage Sieve extension allows users to modify their Sieve script by themselves. The authentication mechanisms are the same as for the main dovecot service. ManageSieve runs on port 4190 and needs to be enabled using the ENABLE_MANAGESIEVE=1 environment variable. Example # docker-compose.yml ports : - \"4190:4190\" environment : - ENABLE_MANAGESIEVE=1 All user defined sieve scripts that are managed by ManageSieve are stored in the user's home folder in /var/mail/example.com/user1/sieve . Just one sieve script might be active for a user and is sym-linked to /var/mail/example.com/user1/.dovecot.sieve automatically. Note ManageSieve makes sure to not overwrite an existing .dovecot.sieve file. If a user activates a new sieve script the old one is backuped and moved to the sieve folder. The extension is known to work with the following ManageSieve clients: Sieve Editor a portable standalone application based on the former Thunderbird plugin. Kmail the mail client of KDE 's Kontact Suite.","title":"Email Filtering with Sieve"},{"location":"config/advanced/mail-sieve/#user-defined-sieve-filters","text":"Sieve allows to specify filtering rules for incoming emails that allow for example sorting mails into different folders depending on the title of an email. There are global and user specific filters which are filtering the incoming emails in the following order: Global-before -> User specific -> Global-after Global filters are applied to EVERY incoming mail for EVERY email address. To specify a global Sieve filter provide a docker-data/dms/config/before.dovecot.sieve or a docker-data/dms/config/after.dovecot.sieve file with your filter rules. If any filter in this filtering chain discards an incoming mail, the delivery process will stop as well and the mail will not reach any following filters(e.g. global-before stops an incoming spam mail: The mail will get discarded and a user-specific filter won't get applied.) To specify a user-defined Sieve filter place a .dovecot.sieve file into a virtual user's mail folder e.g. /var/mail/example.com/user1/.dovecot.sieve . If this file exists dovecot will apply the filtering rules. It's even possible to install a user provided Sieve filter at startup during users setup: simply include a Sieve file in the docker-data/dms/config/ path for each user login that needs a filter. The file name provided should be in the form .dovecot.sieve , so for example for user1@example.com you should provide a Sieve file named docker-data/dms/config/user1@example.com.dovecot.sieve . An example of a sieve filter that moves mails to a folder INBOX/spam depending on the sender address: Example require [ \"fileinto\" , \"reject\" ]; if address :contains [ \"From\" ] \"spam@spam.com\" { fileinto \"INBOX.spam\" ; } else { keep ; } Warning That folders have to exist beforehand if sieve should move them. Another example of a sieve filter that forward mails to a different address: Example require [ \"copy\" ]; redirect :copy \"user2@not-example.com\" ; Just forward all incoming emails and do not save them locally: Example redirect \"user2@not-example.com\" ; You can also use external programs to filter or pipe (process) messages by adding executable scripts in docker-data/dms/config/sieve-pipe or docker-data/dms/config/sieve-filter . This can be used in lieu of a local alias file, for instance to forward an email to a webservice. These programs can then be referenced by filename, by all users. Note that the process running the scripts run as a privileged user. For further information see Dovecot's wiki . require [ \"vnd.dovecot.pipe\" ]; pipe \"external-program\" ; For more examples or a detailed description of the Sieve language have a look at the official site . Other resources are available on the internet where you can find several examples .","title":"User-Defined Sieve Filters"},{"location":"config/advanced/mail-sieve/#automatic-sorting-based-on-subaddresses","text":"It is possible to sort subaddresses such as user+mailing-lists@example.com into a corresponding folder (here: INBOX/Mailing-lists ) automatically. require [ \"envelope\" , \"fileinto\" , \"mailbox\" , \"subaddress\" , \"variables\" ]; if envelope :detail :matches \"to\" \"*\" { set :lower :upperfirst \"tag\" \"${1}\" ; if mailbox exists \"INBOX.${1}\" { fileinto \"INBOX.${1}\" ; } else { fileinto :create \"INBOX.${tag}\" ; } }","title":"Automatic Sorting Based on Subaddresses"},{"location":"config/advanced/mail-sieve/#manage-sieve","text":"The Manage Sieve extension allows users to modify their Sieve script by themselves. The authentication mechanisms are the same as for the main dovecot service. ManageSieve runs on port 4190 and needs to be enabled using the ENABLE_MANAGESIEVE=1 environment variable. Example # docker-compose.yml ports : - \"4190:4190\" environment : - ENABLE_MANAGESIEVE=1 All user defined sieve scripts that are managed by ManageSieve are stored in the user's home folder in /var/mail/example.com/user1/sieve . Just one sieve script might be active for a user and is sym-linked to /var/mail/example.com/user1/.dovecot.sieve automatically. Note ManageSieve makes sure to not overwrite an existing .dovecot.sieve file. If a user activates a new sieve script the old one is backuped and moved to the sieve folder. The extension is known to work with the following ManageSieve clients: Sieve Editor a portable standalone application based on the former Thunderbird plugin. Kmail the mail client of KDE 's Kontact Suite.","title":"Manage Sieve"},{"location":"config/advanced/optional-config/","text":"This is a list of all configuration files and directories which are optional or automatically generated in your docker-data/dms/config/ directory. Directories sieve-filter: directory for sieve filter scripts. (Docs: Sieve ) sieve-pipe: directory for sieve pipe scripts. (Docs: Sieve ) opendkim: DKIM directory. Auto-configurable via setup.sh config dkim . (Docs: DKIM ) ssl: SSL Certificate directory if SSL_TYPE is set to self-signed or custom . (Docs: SSL ) Files {user_email_address}.dovecot.sieve: User specific Sieve filter file. (Docs: Sieve ) before.dovecot.sieve: Global Sieve filter file, applied prior to the ${login}.dovecot.sieve filter. (Docs: Sieve ) after.dovecot.sieve : Global Sieve filter file, applied after the ${login}.dovecot.sieve filter. (Docs: Sieve ) postfix-main.cf: Every line will be added to the postfix main configuration. (Docs: Override Postfix Defaults ) postfix-master.cf: Every line will be added to the postfix master configuration. (Docs: Override Postfix Defaults ) postfix-accounts.cf: User accounts file. Modify via the setup.sh email script. postfix-send-access.cf: List of users denied sending. Modify via setup.sh email restrict . postfix-receive-access.cf: List of users denied receiving. Modify via setup.sh email restrict . postfix-virtual.cf: Alias configuration file. Modify via setup.sh alias . postfix-sasl-password.cf: listing of relayed domains with their respective : . Modify via setup.sh relay add-auth [] . (Docs: Relay-Hosts Auth ) postfix-relaymap.cf: domain-specific relays and exclusions. Modify via setup.sh relay add-domain and setup.sh relay exclude-domain . (Docs: Relay-Hosts Senders ) postfix-regexp.cf: Regular expression alias file. (Docs: Aliases ) ldap-users.cf: Configuration for the virtual user mapping virtual_mailbox_maps . See the setup-stack.sh script. ldap-groups.cf: Configuration for the virtual alias mapping virtual_alias_maps . See the setup-stack.sh script. ldap-aliases.cf: Configuration for the virtual alias mapping virtual_alias_maps . See the setup-stack.sh script. ldap-domains.cf: Configuration for the virtual domain mapping virtual_mailbox_domains . See the setup-stack.sh script. whitelist_clients.local: Whitelisted domains, not considered by postgrey. Enter one host or domain per line. spamassassin-rules.cf: Antispam rules for Spamassassin. (Docs: FAQ - SpamAssassin Rules ) fail2ban-fail2ban.cf: Additional config options for fail2ban.cf . (Docs: Fail2Ban ) fail2ban-jail.cf: Additional config options for fail2ban's jail behaviour. (Docs: Fail2Ban ) amavis.cf: replaces the /etc/amavis/conf.d/50-user file dovecot.cf: replaces /etc/dovecot/local.conf . (Docs: Override Dovecot Defaults ) dovecot-quotas.cf: list of custom quotas per mailbox. (Docs: Accounts ) user-patches.sh: this file will be run after all configuration files are set up, but before the postfix, amavis and other daemons are started. (Docs: FAQ - How to adjust settings with the user-patches.sh script ) rspamd-commands: list of simple commands to adjust Rspamd modules in an easy way (Docs: Rspamd )","title":"Optional Configuration"},{"location":"config/advanced/optional-config/#directories","text":"sieve-filter: directory for sieve filter scripts. (Docs: Sieve ) sieve-pipe: directory for sieve pipe scripts. (Docs: Sieve ) opendkim: DKIM directory. Auto-configurable via setup.sh config dkim . (Docs: DKIM ) ssl: SSL Certificate directory if SSL_TYPE is set to self-signed or custom . (Docs: SSL )","title":"Directories"},{"location":"config/advanced/optional-config/#files","text":"{user_email_address}.dovecot.sieve: User specific Sieve filter file. (Docs: Sieve ) before.dovecot.sieve: Global Sieve filter file, applied prior to the ${login}.dovecot.sieve filter. (Docs: Sieve ) after.dovecot.sieve : Global Sieve filter file, applied after the ${login}.dovecot.sieve filter. (Docs: Sieve ) postfix-main.cf: Every line will be added to the postfix main configuration. (Docs: Override Postfix Defaults ) postfix-master.cf: Every line will be added to the postfix master configuration. (Docs: Override Postfix Defaults ) postfix-accounts.cf: User accounts file. Modify via the setup.sh email script. postfix-send-access.cf: List of users denied sending. Modify via setup.sh email restrict . postfix-receive-access.cf: List of users denied receiving. Modify via setup.sh email restrict . postfix-virtual.cf: Alias configuration file. Modify via setup.sh alias . postfix-sasl-password.cf: listing of relayed domains with their respective : . Modify via setup.sh relay add-auth [] . (Docs: Relay-Hosts Auth ) postfix-relaymap.cf: domain-specific relays and exclusions. Modify via setup.sh relay add-domain and setup.sh relay exclude-domain . (Docs: Relay-Hosts Senders ) postfix-regexp.cf: Regular expression alias file. (Docs: Aliases ) ldap-users.cf: Configuration for the virtual user mapping virtual_mailbox_maps . See the setup-stack.sh script. ldap-groups.cf: Configuration for the virtual alias mapping virtual_alias_maps . See the setup-stack.sh script. ldap-aliases.cf: Configuration for the virtual alias mapping virtual_alias_maps . See the setup-stack.sh script. ldap-domains.cf: Configuration for the virtual domain mapping virtual_mailbox_domains . See the setup-stack.sh script. whitelist_clients.local: Whitelisted domains, not considered by postgrey. Enter one host or domain per line. spamassassin-rules.cf: Antispam rules for Spamassassin. (Docs: FAQ - SpamAssassin Rules ) fail2ban-fail2ban.cf: Additional config options for fail2ban.cf . (Docs: Fail2Ban ) fail2ban-jail.cf: Additional config options for fail2ban's jail behaviour. (Docs: Fail2Ban ) amavis.cf: replaces the /etc/amavis/conf.d/50-user file dovecot.cf: replaces /etc/dovecot/local.conf . (Docs: Override Dovecot Defaults ) dovecot-quotas.cf: list of custom quotas per mailbox. (Docs: Accounts ) user-patches.sh: this file will be run after all configuration files are set up, but before the postfix, amavis and other daemons are started. (Docs: FAQ - How to adjust settings with the user-patches.sh script ) rspamd-commands: list of simple commands to adjust Rspamd modules in an easy way (Docs: Rspamd )","title":"Files"},{"location":"config/advanced/podman/","text":"Introduction Podman is a daemonless container engine for developing, managing, and running OCI Containers on your Linux System. About Support for Podman Please note that Podman is not officially supported as docker-mailserver is built and verified on top of the Docker Engine . This content is entirely community supported. If you find errors, please open an issue and provide a PR. About this Guide This guide was tested with Fedora 34 using systemd and firewalld . Moreover, it requires Podman version >= 3.2. You may be able to substitute dnf - Fedora's package manager - with others such as apt . About Security Running podman in rootless mode requires additional modifications in order to keep your mailserver secure. Make sure to read the related documentation. Installation in Rootfull Mode While using Podman, you can just manage docker-mailserver as what you did with Docker. Your best friend setup.sh includes the minimum code in order to support Podman since it's 100% compatible with the Docker CLI. The installation is basically the same. Podman v3.2 introduced a RESTful API that is 100% compatible with the Docker API, so you can use docker-compose with Podman easily. Install Podman and docker-compose with your package manager first. sudo dnf install podman docker-compose Then enable podman.socket using systemctl . systemctl enable --now podman.socket This will create a unix socket locate under /run/podman/podman.sock , which is the entrypoint of Podman's API. Now, configure docker-mailserver and start it. export DOCKER_HOST = \"unix:///run/podman/podman.sock\" docker-compose up -d mailserver docker-compose ps You should see that docker-mailserver is running now. Self-start in Rootfull Mode Podman is daemonless, that means if you want docker-mailserver self-start while boot up the system, you have to generate a systemd file with Podman CLI. podman generate systemd mailserver > /etc/systemd/system/mailserver.service systemctl daemon-reload systemctl enable --now mailserver.service Installation in Rootless Mode Running rootless containers is one of Podman's major features. But due to some restrictions, deploying docker-mailserver in rootless mode is not as easy compared to rootfull mode. a rootless container is running in a user namespace so you cannot bind ports lower than 1024 a rootless container's systemd file can only be placed in folder under ~/.config a rootless container can result in an open relay, make sure to read the security section . Also notice that Podman's rootless mode is not about running as a non-root user inside the container, but about the mapping of (normal, non-root) host users to root inside the container. Warning In order to make rootless docker-mailserver work we must modify some settings in the Linux system, it requires some basic linux server knowledge so don't follow this guide if you not sure what this guide is talking about. Podman rootfull mode and Docker are still good and security enough for normal daily usage. First, enable podman.socket in systemd's userspace with a non-root user. systemctl enable --now --user podman.socket The socket file should be located at /var/run/user/$(id -u)/podman/podman.sock . Then, modify docker-compose.yml to make sure all ports are bindings are on non-privileged ports. services : mailserver : ports : - \"10025:25\" # SMTP (explicit TLS => STARTTLS) - \"10143:143\" # IMAP4 (explicit TLS => STARTTLS) - \"10465:465\" # ESMTP (implicit TLS) - \"10587:587\" # ESMTP (explicit TLS => STARTTLS) - \"10993:993\" # IMAP4 (implicit TLS) Then, setup your mailserver.env file follow the documentation and use docker-compose to start the container. export DOCKER_HOST = \"unix:///var/run/user/ $( id -u ) /podman/podman.sock\" docker-compose up -d mailserver docker-compose ps Security in Rootless Mode In rootless mode, podman resolves all incoming IPs as localhost, which results in an open gateway in the default configuration. There are two workarounds to fix this problem, both of which have their own drawbacks. Enforce authentication from localhost The PERMIT_DOCKER variable in the mailserver.env file allows to specify trusted networks that do not need to authenticate. If the variable is left empty, only requests from localhost and the container IP are allowed, but in the case of rootless podman any IP will be resolved as localhost. Setting PERMIT_DOCKER=none enforces authentication also from localhost, which prevents sending unauthenticated emails. Use the slip4netns network driver The second workaround is slightly more complicated because the docker-compose.yml has to be modified. As shown in the fail2ban section the slirp4netns network driver has to be enabled. This network driver enables podman to correctly resolve IP addresses but it is not compatible with user defined networks which might be a problem depending on your setup. Rootless Podman requires adding the value slirp4netns:port_handler=slirp4netns to the --network CLI option, or network_mode setting in your docker-compose.yml . You must also add the ENV NETWORK_INTERFACE=tap0 , because Podman uses a hard-coded interface name for slirp4netns . Example services : mailserver : network_mode : \"slirp4netns:port_handler=slirp4netns\" environment : - NETWORK_INTERFACE=tap0 ... Note podman-compose is not compatible with this configuration. Self-start in Rootless Mode Generate a systemd file with the Podman CLI. podman generate systemd mailserver > ~/.config/systemd/user/mailserver.service systemctl --user daemon-reload systemctl enable --user --now mailserver.service Systemd's user space service is only started when a specific user logs in and stops when you log out. In order to make it to start with the system, we need to enable linger with loginctl loginctl enable-linger Remember to run this command as root user. Port Forwarding When it comes to forwarding ports using firewalld , see https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/securing_networks/using-and-configuring-firewalld_securing-networks#port-forwarding_using-and-configuring-firewalld for more information. firewall-cmd --permanent --add-forward-port = port = < 25 | 143 | 465 | 587 | 993 >:proto = :toport = < 10025 | 10143 | 10465 | 10587 | 10993 > ... # After you set all ports up. firewall-cmd --reload Notice that this will only open the access to the external client. If you want to access privileges port in your server, do this: firewall-cmd --permanent --direct --add-rule nat OUTPUT 0 -p -o lo --dport < 25 | 143 | 465 | 587 | 993 > -j REDIRECT --to-ports < 10025 | 10143 | 10465 | 10587 | 10993 > ... # After you set all ports up. firewall-cmd --reload Just map all the privilege port with non-privilege port you set in docker-compose.yml before as root user.","title":"Podman"},{"location":"config/advanced/podman/#introduction","text":"Podman is a daemonless container engine for developing, managing, and running OCI Containers on your Linux System. About Support for Podman Please note that Podman is not officially supported as docker-mailserver is built and verified on top of the Docker Engine . This content is entirely community supported. If you find errors, please open an issue and provide a PR. About this Guide This guide was tested with Fedora 34 using systemd and firewalld . Moreover, it requires Podman version >= 3.2. You may be able to substitute dnf - Fedora's package manager - with others such as apt . About Security Running podman in rootless mode requires additional modifications in order to keep your mailserver secure. Make sure to read the related documentation.","title":"Introduction"},{"location":"config/advanced/podman/#installation-in-rootfull-mode","text":"While using Podman, you can just manage docker-mailserver as what you did with Docker. Your best friend setup.sh includes the minimum code in order to support Podman since it's 100% compatible with the Docker CLI. The installation is basically the same. Podman v3.2 introduced a RESTful API that is 100% compatible with the Docker API, so you can use docker-compose with Podman easily. Install Podman and docker-compose with your package manager first. sudo dnf install podman docker-compose Then enable podman.socket using systemctl . systemctl enable --now podman.socket This will create a unix socket locate under /run/podman/podman.sock , which is the entrypoint of Podman's API. Now, configure docker-mailserver and start it. export DOCKER_HOST = \"unix:///run/podman/podman.sock\" docker-compose up -d mailserver docker-compose ps You should see that docker-mailserver is running now.","title":"Installation in Rootfull Mode"},{"location":"config/advanced/podman/#self-start-in-rootfull-mode","text":"Podman is daemonless, that means if you want docker-mailserver self-start while boot up the system, you have to generate a systemd file with Podman CLI. podman generate systemd mailserver > /etc/systemd/system/mailserver.service systemctl daemon-reload systemctl enable --now mailserver.service","title":"Self-start in Rootfull Mode"},{"location":"config/advanced/podman/#installation-in-rootless-mode","text":"Running rootless containers is one of Podman's major features. But due to some restrictions, deploying docker-mailserver in rootless mode is not as easy compared to rootfull mode. a rootless container is running in a user namespace so you cannot bind ports lower than 1024 a rootless container's systemd file can only be placed in folder under ~/.config a rootless container can result in an open relay, make sure to read the security section . Also notice that Podman's rootless mode is not about running as a non-root user inside the container, but about the mapping of (normal, non-root) host users to root inside the container. Warning In order to make rootless docker-mailserver work we must modify some settings in the Linux system, it requires some basic linux server knowledge so don't follow this guide if you not sure what this guide is talking about. Podman rootfull mode and Docker are still good and security enough for normal daily usage. First, enable podman.socket in systemd's userspace with a non-root user. systemctl enable --now --user podman.socket The socket file should be located at /var/run/user/$(id -u)/podman/podman.sock . Then, modify docker-compose.yml to make sure all ports are bindings are on non-privileged ports. services : mailserver : ports : - \"10025:25\" # SMTP (explicit TLS => STARTTLS) - \"10143:143\" # IMAP4 (explicit TLS => STARTTLS) - \"10465:465\" # ESMTP (implicit TLS) - \"10587:587\" # ESMTP (explicit TLS => STARTTLS) - \"10993:993\" # IMAP4 (implicit TLS) Then, setup your mailserver.env file follow the documentation and use docker-compose to start the container. export DOCKER_HOST = \"unix:///var/run/user/ $( id -u ) /podman/podman.sock\" docker-compose up -d mailserver docker-compose ps","title":"Installation in Rootless Mode"},{"location":"config/advanced/podman/#security-in-rootless-mode","text":"In rootless mode, podman resolves all incoming IPs as localhost, which results in an open gateway in the default configuration. There are two workarounds to fix this problem, both of which have their own drawbacks.","title":"Security in Rootless Mode"},{"location":"config/advanced/podman/#enforce-authentication-from-localhost","text":"The PERMIT_DOCKER variable in the mailserver.env file allows to specify trusted networks that do not need to authenticate. If the variable is left empty, only requests from localhost and the container IP are allowed, but in the case of rootless podman any IP will be resolved as localhost. Setting PERMIT_DOCKER=none enforces authentication also from localhost, which prevents sending unauthenticated emails.","title":"Enforce authentication from localhost"},{"location":"config/advanced/podman/#use-the-slip4netns-network-driver","text":"The second workaround is slightly more complicated because the docker-compose.yml has to be modified. As shown in the fail2ban section the slirp4netns network driver has to be enabled. This network driver enables podman to correctly resolve IP addresses but it is not compatible with user defined networks which might be a problem depending on your setup. Rootless Podman requires adding the value slirp4netns:port_handler=slirp4netns to the --network CLI option, or network_mode setting in your docker-compose.yml . You must also add the ENV NETWORK_INTERFACE=tap0 , because Podman uses a hard-coded interface name for slirp4netns . Example services : mailserver : network_mode : \"slirp4netns:port_handler=slirp4netns\" environment : - NETWORK_INTERFACE=tap0 ... Note podman-compose is not compatible with this configuration.","title":"Use the slip4netns network driver"},{"location":"config/advanced/podman/#self-start-in-rootless-mode","text":"Generate a systemd file with the Podman CLI. podman generate systemd mailserver > ~/.config/systemd/user/mailserver.service systemctl --user daemon-reload systemctl enable --user --now mailserver.service Systemd's user space service is only started when a specific user logs in and stops when you log out. In order to make it to start with the system, we need to enable linger with loginctl loginctl enable-linger Remember to run this command as root user.","title":"Self-start in Rootless Mode"},{"location":"config/advanced/podman/#port-forwarding","text":"When it comes to forwarding ports using firewalld , see https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/securing_networks/using-and-configuring-firewalld_securing-networks#port-forwarding_using-and-configuring-firewalld for more information. firewall-cmd --permanent --add-forward-port = port = < 25 | 143 | 465 | 587 | 993 >:proto = :toport = < 10025 | 10143 | 10465 | 10587 | 10993 > ... # After you set all ports up. firewall-cmd --reload Notice that this will only open the access to the external client. If you want to access privileges port in your server, do this: firewall-cmd --permanent --direct --add-rule nat OUTPUT 0 -p -o lo --dport < 25 | 143 | 465 | 587 | 993 > -j REDIRECT --to-ports < 10025 | 10143 | 10465 | 10587 | 10993 > ... # After you set all ports up. firewall-cmd --reload Just map all the privilege port with non-privilege port you set in docker-compose.yml before as root user.","title":"Port Forwarding"},{"location":"config/advanced/mail-forwarding/aws-ses/","text":"Amazon SES (Simple Email Service) is intended to provide a simple way for cloud based applications to send email and receive email. For the purposes of this project only sending email via SES is supported. Older versions of docker-mailserver used AWS_SES_HOST and AWS_SES_USERPASS to configure sending, this has changed and the setup is mananged through Configure Relay Hosts . You will need to create some Amazon SES SMTP credentials . The SMTP credentials you create will be used to populate the RELAY_USER and RELAY_PASSWORD environment variables. The RELAY_HOST should match your AWS SES region , the RELAY_PORT will be 587. If all of your email is being forwarded through AWS SES, DEFAULT_RELAY_HOST should be set accordingly. Example: DEFAULT_RELAY_HOST=[email-smtp.us-west-2.amazonaws.com]:587 Note If you set up AWS Easy DKIM you can safely skip setting up DKIM as the AWS SES will take care of signing your outgoing email. To verify proper operation, send an email to some external account of yours and inspect the mail headers. You will also see the connection to SES in the mail logs. For example: May 23 07:09:36 mail postfix/smtp[692]: Trusted TLS connection established to email-smtp.us-east-1.amazonaws.com[107.20.142.169]:25: TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits) May 23 07:09:36 mail postfix/smtp[692]: 8C82A7E7: to=, relay=email-smtp.us-east-1.amazonaws.com[107.20.142.169]:25, delay=0.35, delays=0/0.02/0.13/0.2, dsn=2.0.0, status=sent (250 Ok 01000154dc729264-93fdd7ea-f039-43d6-91ed-653e8547867c-000000)","title":"AWS SES"},{"location":"config/advanced/mail-forwarding/relay-hosts/","text":"Introduction Rather than having Postfix deliver mail directly, you can configure Postfix to send mail via another mail relay (smarthost). Examples include Mailgun , Sendgrid and AWS SES . Depending on the domain of the sender, you may want to send via a different relay, or authenticate in a different way. Basic Configuration Basic configuration is done via environment variables: RELAY_HOST : default host to relay mail through, empty (aka '', or no ENV set) will disable this feature RELAY_PORT : port on default relay, defaults to port 25 RELAY_USER : username for the default relay RELAY_PASSWORD : password for the default user Setting these environment variables will cause mail for all sender domains to be routed via the specified host, authenticating with the user/password combination. Warning For users of the previous AWS_SES_* variables: please update your configuration to use these new variables, no other configuration is required. Advanced Configuration Sender-dependent Authentication Sender dependent authentication is done in docker-data/dms/config/postfix-sasl-password.cf . You can create this file manually, or use: setup.sh relay add-auth [ ] An example configuration file looks like this: @domain1.com relay_user_1:password_1 @domain2.com relay_user_2:password_2 If there is no other configuration, this will cause Postfix to deliver email through the relay specified in RELAY_HOST env variable, authenticating as relay_user_1 when sent from domain1.com and authenticating as relay_user_2 when sending from domain2.com . Note To activate the configuration you must either restart the container, or you can also trigger an update by modifying a mail account. Sender-dependent Relay Host Sender dependent relay hosts are configured in docker-data/dms/config/postfix-relaymap.cf . You can create this file manually, or use: setup.sh relay add-domain [ ] An example configuration file looks like this: @domain1.com [relay1.org]:587 @domain2.com [relay2.org]:2525 Combined with the previous configuration in docker-data/dms/config/postfix-sasl-password.cf , this will cause Postfix to deliver mail sent from domain1.com via relay1.org:587 , authenticating as relay_user_1 , and mail sent from domain2.com via relay2.org:2525 authenticating as relay_user_2 . Note You still have to define RELAY_HOST to activate the feature Excluding Sender Domains If you want mail sent from some domains to be delivered directly, you can exclude them from being delivered via the default relay by adding them to docker-data/dms/config/postfix-relaymap.cf with no destination. You can also do this via: setup.sh relay exclude-domain Extending the configuration file from above: @domain1.com [relay1.org]:587 @domain2.com [relay2.org]:2525 @domain3.com This will cause email sent from domain3.com to be delivered directly.","title":"Relay Hosts"},{"location":"config/advanced/mail-forwarding/relay-hosts/#introduction","text":"Rather than having Postfix deliver mail directly, you can configure Postfix to send mail via another mail relay (smarthost). Examples include Mailgun , Sendgrid and AWS SES . Depending on the domain of the sender, you may want to send via a different relay, or authenticate in a different way.","title":"Introduction"},{"location":"config/advanced/mail-forwarding/relay-hosts/#basic-configuration","text":"Basic configuration is done via environment variables: RELAY_HOST : default host to relay mail through, empty (aka '', or no ENV set) will disable this feature RELAY_PORT : port on default relay, defaults to port 25 RELAY_USER : username for the default relay RELAY_PASSWORD : password for the default user Setting these environment variables will cause mail for all sender domains to be routed via the specified host, authenticating with the user/password combination. Warning For users of the previous AWS_SES_* variables: please update your configuration to use these new variables, no other configuration is required.","title":"Basic Configuration"},{"location":"config/advanced/mail-forwarding/relay-hosts/#advanced-configuration","text":"","title":"Advanced Configuration"},{"location":"config/advanced/mail-forwarding/relay-hosts/#sender-dependent-authentication","text":"Sender dependent authentication is done in docker-data/dms/config/postfix-sasl-password.cf . You can create this file manually, or use: setup.sh relay add-auth [ ] An example configuration file looks like this: @domain1.com relay_user_1:password_1 @domain2.com relay_user_2:password_2 If there is no other configuration, this will cause Postfix to deliver email through the relay specified in RELAY_HOST env variable, authenticating as relay_user_1 when sent from domain1.com and authenticating as relay_user_2 when sending from domain2.com . Note To activate the configuration you must either restart the container, or you can also trigger an update by modifying a mail account.","title":"Sender-dependent Authentication"},{"location":"config/advanced/mail-forwarding/relay-hosts/#sender-dependent-relay-host","text":"Sender dependent relay hosts are configured in docker-data/dms/config/postfix-relaymap.cf . You can create this file manually, or use: setup.sh relay add-domain [ ] An example configuration file looks like this: @domain1.com [relay1.org]:587 @domain2.com [relay2.org]:2525 Combined with the previous configuration in docker-data/dms/config/postfix-sasl-password.cf , this will cause Postfix to deliver mail sent from domain1.com via relay1.org:587 , authenticating as relay_user_1 , and mail sent from domain2.com via relay2.org:2525 authenticating as relay_user_2 . Note You still have to define RELAY_HOST to activate the feature","title":"Sender-dependent Relay Host"},{"location":"config/advanced/mail-forwarding/relay-hosts/#excluding-sender-domains","text":"If you want mail sent from some domains to be delivered directly, you can exclude them from being delivered via the default relay by adding them to docker-data/dms/config/postfix-relaymap.cf with no destination. You can also do this via: setup.sh relay exclude-domain Extending the configuration file from above: @domain1.com [relay1.org]:587 @domain2.com [relay2.org]:2525 @domain3.com This will cause email sent from domain3.com to be delivered directly.","title":"Excluding Sender Domains"},{"location":"config/advanced/maintenance/update-and-cleanup/","text":"Automatic Update Docker images are handy but it can become a hassle to keep them updated. Also when a repository is automated you want to get these images when they get out. One could setup a complex action/hook-based workflow using probes, but there is a nice, easy to use docker image that solves this issue and could prove useful: watchtower . A docker-compose example: services : watchtower : restart : always image : containrrr/watchtower:latest volumes : - /var/run/docker.sock:/var/run/docker.sock For more details, see the manual Automatic Cleanup When you are pulling new images in automatically, it would be nice to have them cleaned up as well. There is also a docker image for this: spotify/docker-gc . A docker-compose example: services : docker-gc : restart : always image : spotify/docker-gc:latest volumes : - /var/run/docker.sock:/var/run/docker.sock For more details, see the manual Or you can just use the --cleanup option provided by containrrr/watchtower .","title":"Update and Cleanup"},{"location":"config/advanced/maintenance/update-and-cleanup/#automatic-update","text":"Docker images are handy but it can become a hassle to keep them updated. Also when a repository is automated you want to get these images when they get out. One could setup a complex action/hook-based workflow using probes, but there is a nice, easy to use docker image that solves this issue and could prove useful: watchtower . A docker-compose example: services : watchtower : restart : always image : containrrr/watchtower:latest volumes : - /var/run/docker.sock:/var/run/docker.sock For more details, see the manual","title":"Automatic Update"},{"location":"config/advanced/maintenance/update-and-cleanup/#automatic-cleanup","text":"When you are pulling new images in automatically, it would be nice to have them cleaned up as well. There is also a docker image for this: spotify/docker-gc . A docker-compose example: services : docker-gc : restart : always image : spotify/docker-gc:latest volumes : - /var/run/docker.sock:/var/run/docker.sock For more details, see the manual Or you can just use the --cleanup option provided by containrrr/watchtower .","title":"Automatic Cleanup"},{"location":"config/advanced/override-defaults/dovecot/","text":"Add Configuration The Dovecot default configuration can easily be extended providing a docker-data/dms/config/dovecot.cf file. Dovecot documentation remains the best place to find configuration options. Your docker-mailserver folder should look like this example: \u251c\u2500\u2500 docker-data/dms/config \u2502 \u251c\u2500\u2500 dovecot.cf \u2502 \u251c\u2500\u2500 postfix-accounts.cf \u2502 \u2514\u2500\u2500 postfix-virtual.cf \u251c\u2500\u2500 docker-compose.yml \u2514\u2500\u2500 README.md One common option to change is the maximum number of connections per user: mail_max_userip_connections = 100 Another important option is the default_process_limit (defaults to 100 ). If high-security mode is enabled you'll need to make sure this count is higher than the maximum number of users that can be logged in simultaneously. This limit is quickly reached if users connect to the docker-mailserver with multiple end devices. Override Configuration For major configuration changes it\u2019s best to override the dovecot configuration files. For each configuration file you want to override, add a list entry under the volumes key. services : mailserver : volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/config/dovecot/10-master.conf:/etc/dovecot/conf.d/10-master.conf You will first need to obtain the configuration from the running container ( where mailserver is the container name ): mkdir -p ./docker-data/dms/config/dovecot docker cp mailserver:/etc/dovecot/conf.d/10-master.conf ./docker-data/dms/config/dovecot/10-master.conf Debugging To debug your dovecot configuration you can use: This command: ./setup.sh debug login doveconf | grep Or: docker exec -it mailserver doveconf | grep Note setup.sh is included in the docker-mailserver repository. Make sure to use the one matching your image version release. The file docker-data/dms/config/dovecot.cf is copied internally to /etc/dovecot/local.conf . To verify the file content, run: docker exec -it mailserver cat /etc/dovecot/local.conf","title":"Dovecot"},{"location":"config/advanced/override-defaults/dovecot/#add-configuration","text":"The Dovecot default configuration can easily be extended providing a docker-data/dms/config/dovecot.cf file. Dovecot documentation remains the best place to find configuration options. Your docker-mailserver folder should look like this example: \u251c\u2500\u2500 docker-data/dms/config \u2502 \u251c\u2500\u2500 dovecot.cf \u2502 \u251c\u2500\u2500 postfix-accounts.cf \u2502 \u2514\u2500\u2500 postfix-virtual.cf \u251c\u2500\u2500 docker-compose.yml \u2514\u2500\u2500 README.md One common option to change is the maximum number of connections per user: mail_max_userip_connections = 100 Another important option is the default_process_limit (defaults to 100 ). If high-security mode is enabled you'll need to make sure this count is higher than the maximum number of users that can be logged in simultaneously. This limit is quickly reached if users connect to the docker-mailserver with multiple end devices.","title":"Add Configuration"},{"location":"config/advanced/override-defaults/dovecot/#override-configuration","text":"For major configuration changes it\u2019s best to override the dovecot configuration files. For each configuration file you want to override, add a list entry under the volumes key. services : mailserver : volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/config/dovecot/10-master.conf:/etc/dovecot/conf.d/10-master.conf You will first need to obtain the configuration from the running container ( where mailserver is the container name ): mkdir -p ./docker-data/dms/config/dovecot docker cp mailserver:/etc/dovecot/conf.d/10-master.conf ./docker-data/dms/config/dovecot/10-master.conf","title":"Override Configuration"},{"location":"config/advanced/override-defaults/dovecot/#debugging","text":"To debug your dovecot configuration you can use: This command: ./setup.sh debug login doveconf | grep Or: docker exec -it mailserver doveconf | grep Note setup.sh is included in the docker-mailserver repository. Make sure to use the one matching your image version release. The file docker-data/dms/config/dovecot.cf is copied internally to /etc/dovecot/local.conf . To verify the file content, run: docker exec -it mailserver cat /etc/dovecot/local.conf","title":"Debugging"},{"location":"config/advanced/override-defaults/postfix/","text":"Our default Postfix configuration can easily be extended to add parameters or modify existing ones by providing a docker-data/dms/config/postfix-main.cf . This file uses the same format as Postfix main.cf does ( See official docs for all parameters and syntax rules). Example One can easily increase the backwards-compatibility level and set new Postscreen options: # increase the compatibility level from 2 (default) to 3 compatibility_level = 3 # set a threshold value for Spam detection postscreen_dnsbl_threshold = 4 How are your changes applied? The custom configuration you supply is appended to the default configuration located at /etc/postfix/main.cf , and then postconf -nf is run to remove earlier duplicate entries that have since been replaced. This happens early during container startup before Postfix is started. Similarly, it is possible to add a custom docker-data/dms/config/postfix-master.cf file that will override the standard master.cf . Note : Each line in this file will be passed to postconf -P , i.e. the file is not appended as a whole to /etc/postfix/master.cf like docker-data/dms/config/postfix-main.cf ! The expected format is // , for example: # adjust the submission \"reject_unlisted_recipient\" option submission/inet/smtpd_reject_unlisted_recipient = no Attention There should be no space between the parameter and the value. Run postconf -Mf in the container without arguments to see the active master options.","title":"Postfix"},{"location":"config/advanced/override-defaults/user-patches/","text":"If you'd like to change, patch or alter files or behavior of docker-mailserver , you can use a script. In case you cloned this repository, you can copy the file user-patches.sh.dist ( under config/ ) with cp config/user-patches.sh.dist docker-data/dms/config/user-patches.sh in order to create the user-patches.sh script. If you are managing your directory structure yourself, create a docker-data/dms/config/ directory and add the user-patches.sh file yourself. # 1. Either create the docker-data/dms/config/ directory yourself # or let docker-mailserver create it on initial startup /tmp $ mkdir -p docker-data/dms/config/ && cd docker-data/dms/config/ # 2. Create the user-patches.sh file and edit it /tmp/docker-data/dms/config $ touch user-patches.sh /tmp/docker-data/dms/config $ nano user-patches.sh The contents could look like this: #!/bin/bash cat >/etc/amavis/conf.d/50-user << \"END\" use strict ; $undecipherable_subject_tag = undef ; $admin_maps_by_ccat { +CC_UNCHECKED } = undef ; #------------ Do not modify anything below this line ------------- 1 ; # ensure a defined return END 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. Note Many \"patches\" can already be done with the Docker Compose-/Stack-file. Adding hostnames to /etc/hosts is done with the extra_hosts : section, sysctl commands can be managed with the sysctls : section, etc.","title":"Modifications via Script"},{"location":"config/best-practices/autodiscover/","text":"Email auto-discovery means a client email is able to automagically find out about what ports and security options to use, based on the mail-server URI. It can help simplify the tedious / confusing task of adding own's email account for non-tech savvy users. Email clients will search for auto-discoverable settings and prefill almost everything when a user enters its email address There exists autodiscover-email-settings on which provides IMAP/POP/SMTP/LDAP autodiscover capabilities on Microsoft Outlook/Apple Mail, autoconfig capabilities for Thunderbird or kmail and configuration profiles for iOS/Apple Mail.","title":"Auto-discovery"},{"location":"config/best-practices/dkim/","text":"DKIM is a security measure targeting email spoofing. It is greatly recommended one activates it. Note See the Wikipedia page for more details on DKIM. Enabling DKIM Signature To enable DKIM signature, you must have created at least one email account . Once its done, just run the following command to generate the signature: ./setup.sh config dkim After generating DKIM keys, you should restart docker-mailserver . DNS edits may take a few minutes to hours to propagate. The script should ideally be run with a volume for config attached (eg: ./docker-data/dms/config/:/tmp/docker-mailserver/ ), otherwise by default it will mount ./config/:/tmp/docker-mailserver/ . The default keysize when generating the signature is 4096 bits for now. If you need to change it (e.g. your DNS provider limits the size), then provide the size as the first parameter of the command: ./setup.sh config dkim keysize For LDAP systems that do not have any directly created user account you can run the following command (since 8.0.0 ) to generate the signature by additionally providing the desired domain name (if you have multiple domains use the command multiple times or provide a comma-separated list of domains): ./setup.sh config dkim keysize domain [ , ] Now the keys are generated, you can configure your DNS server with DKIM signature, simply by adding a TXT record. If you have direct access to your DNS zone file, then it's only a matter of pasting the content of docker-data/dms/config/opendkim/keys/example.com/mail.txt in your example.com.hosts zone. $ dig mail._domainkey.example.com TXT --- ;; ANSWER SECTION mail._domainkey. 300 IN TXT \"v=DKIM1; k=rsa; p=AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN\" Configuration using a Web Interface Generate a new record of the type TXT . Paste mail._domainkey the Name txt field. In the Target or Value field fill in v=DKIM1; k=rsa; p=AZERTYUGHJKLMWX... . In TTL (time to live): Time span in seconds. How long the DNS server should cache the TXT record. Save. Note Sometimes the key in docker-data/dms/config/opendkim/keys/example.com/mail.txt can be on multiple lines. If so then you need to concatenate the values in the TXT record: $ dig mail._domainkey.example.com TXT --- ;; ANSWER SECTION mail._domainkey. 300 IN TXT \"v=DKIM1; k=rsa; \" \"p=AZERTYUIOPQSDF...\" \"asdfQWERTYUIOPQSDF...\" The target (or value) field must then have all the parts together: v=DKIM1; k=rsa; p=AZERTYUIOPQSDF...asdfQWERTYUIOPQSDF... Verify-Only If you want DKIM to only verify incoming emails, the following version of /etc/opendkim.conf may be useful (right now there is no easy mechanism for installing it other than forking the repo): # This is a simple config file verifying messages only #LogWhy yes Syslog yes SyslogSuccess yes Socket inet:12301@localhost PidFile /var/run/opendkim/opendkim.pid ReportAddress postmaster@example.com SendReports yes Mode v Switch Off DKIM Simply remove the DKIM key by recreating (not just relaunching) the docker-mailserver container. Debugging DKIM-verifer : A add-on for the mail client Thunderbird. You can debug your TXT records with the dig tool. $ dig TXT mail._domainkey.example.com --- ; <<>> DiG 9.10.3-P4-Debian <<>> TXT mail._domainkey.example.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39669 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 512 ;; QUESTION SECTION: ;mail._domainkey.example.com. IN TXT ;; ANSWER SECTION: mail._domainkey.example.com. 3600 IN TXT \"v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxBSjG6RnWAdU3oOlqsdf2WC0FOUmU8uHVrzxPLW2R3yRBPGLrGO1++yy3tv6kMieWZwEBHVOdefM6uQOQsZ4brahu9lhG8sFLPX4MaKYN/NR6RK4gdjrZu+MYSdfk3THgSbNwIDAQAB\" ;; Query time: 50 msec ;; SERVER: 127.0.1.1#53(127.0.1.1) ;; WHEN: Wed Sep 07 18:22:57 CEST 2016 ;; MSG SIZE rcvd: 310 Key sizes >=4096-bit Keys of 4096 bits could de denied by some mail-servers. According to https://tools.ietf.org/html/rfc6376 keys are preferably between 512 and 2048 bits. See issue #1854 .","title":"DKIM"},{"location":"config/best-practices/dkim/#enabling-dkim-signature","text":"To enable DKIM signature, you must have created at least one email account . Once its done, just run the following command to generate the signature: ./setup.sh config dkim After generating DKIM keys, you should restart docker-mailserver . DNS edits may take a few minutes to hours to propagate. The script should ideally be run with a volume for config attached (eg: ./docker-data/dms/config/:/tmp/docker-mailserver/ ), otherwise by default it will mount ./config/:/tmp/docker-mailserver/ . The default keysize when generating the signature is 4096 bits for now. If you need to change it (e.g. your DNS provider limits the size), then provide the size as the first parameter of the command: ./setup.sh config dkim keysize For LDAP systems that do not have any directly created user account you can run the following command (since 8.0.0 ) to generate the signature by additionally providing the desired domain name (if you have multiple domains use the command multiple times or provide a comma-separated list of domains): ./setup.sh config dkim keysize domain [ , ] Now the keys are generated, you can configure your DNS server with DKIM signature, simply by adding a TXT record. If you have direct access to your DNS zone file, then it's only a matter of pasting the content of docker-data/dms/config/opendkim/keys/example.com/mail.txt in your example.com.hosts zone. $ dig mail._domainkey.example.com TXT --- ;; ANSWER SECTION mail._domainkey. 300 IN TXT \"v=DKIM1; k=rsa; p=AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN\"","title":"Enabling DKIM Signature"},{"location":"config/best-practices/dkim/#configuration-using-a-web-interface","text":"Generate a new record of the type TXT . Paste mail._domainkey the Name txt field. In the Target or Value field fill in v=DKIM1; k=rsa; p=AZERTYUGHJKLMWX... . In TTL (time to live): Time span in seconds. How long the DNS server should cache the TXT record. Save. Note Sometimes the key in docker-data/dms/config/opendkim/keys/example.com/mail.txt can be on multiple lines. If so then you need to concatenate the values in the TXT record: $ dig mail._domainkey.example.com TXT --- ;; ANSWER SECTION mail._domainkey. 300 IN TXT \"v=DKIM1; k=rsa; \" \"p=AZERTYUIOPQSDF...\" \"asdfQWERTYUIOPQSDF...\" The target (or value) field must then have all the parts together: v=DKIM1; k=rsa; p=AZERTYUIOPQSDF...asdfQWERTYUIOPQSDF...","title":"Configuration using a Web Interface"},{"location":"config/best-practices/dkim/#verify-only","text":"If you want DKIM to only verify incoming emails, the following version of /etc/opendkim.conf may be useful (right now there is no easy mechanism for installing it other than forking the repo): # This is a simple config file verifying messages only #LogWhy yes Syslog yes SyslogSuccess yes Socket inet:12301@localhost PidFile /var/run/opendkim/opendkim.pid ReportAddress postmaster@example.com SendReports yes Mode v","title":"Verify-Only"},{"location":"config/best-practices/dkim/#switch-off-dkim","text":"Simply remove the DKIM key by recreating (not just relaunching) the docker-mailserver container.","title":"Switch Off DKIM"},{"location":"config/best-practices/dkim/#debugging","text":"DKIM-verifer : A add-on for the mail client Thunderbird. You can debug your TXT records with the dig tool. $ dig TXT mail._domainkey.example.com --- ; <<>> DiG 9.10.3-P4-Debian <<>> TXT mail._domainkey.example.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39669 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 512 ;; QUESTION SECTION: ;mail._domainkey.example.com. IN TXT ;; ANSWER SECTION: mail._domainkey.example.com. 3600 IN TXT \"v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxBSjG6RnWAdU3oOlqsdf2WC0FOUmU8uHVrzxPLW2R3yRBPGLrGO1++yy3tv6kMieWZwEBHVOdefM6uQOQsZ4brahu9lhG8sFLPX4MaKYN/NR6RK4gdjrZu+MYSdfk3THgSbNwIDAQAB\" ;; Query time: 50 msec ;; SERVER: 127.0.1.1#53(127.0.1.1) ;; WHEN: Wed Sep 07 18:22:57 CEST 2016 ;; MSG SIZE rcvd: 310 Key sizes >=4096-bit Keys of 4096 bits could de denied by some mail-servers. According to https://tools.ietf.org/html/rfc6376 keys are preferably between 512 and 2048 bits. See issue #1854 .","title":"Debugging"},{"location":"config/best-practices/dmarc/","text":"More information at DMARC Guide . Enabling DMARC In docker-mailserver , DMARC is pre-configured out of the box. The only thing you need to do in order to enable it, is to add new TXT entry to your DNS. In contrast with DKIM , the DMARC DNS entry does not require any keys, but merely setting the configuration values . You can either handcraft the entry by yourself or use one of available generators (like this one ). Typically something like this should be good to start with ( don't forget to replace @example.com to your actual domain ): _dmarc.example.com. IN TXT \"v=DMARC1; p=none; rua=mailto:dmarc.report@example.com; ruf=mailto:dmarc.report@example.com; sp=none; ri=86400\" Or a bit more strict policies ( mind p=quarantine and sp=quarantine ): _dmarc IN TXT \"v=DMARC1; p=quarantine; rua=mailto:dmarc.report@example.com; ruf=mailto:dmarc.report@example.com; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; sp=quarantine\" DMARC status is not being displayed instantly in Gmail for instance. If you want to check it directly after DNS entries, you can use some services around the Internet such as from Global Cyber Alliance or RedSift . In other cases, email clients will show \"DMARC: PASS\" in ~1 day or so. Reference: #1511","title":"DMARC"},{"location":"config/best-practices/dmarc/#enabling-dmarc","text":"In docker-mailserver , DMARC is pre-configured out of the box. The only thing you need to do in order to enable it, is to add new TXT entry to your DNS. In contrast with DKIM , the DMARC DNS entry does not require any keys, but merely setting the configuration values . You can either handcraft the entry by yourself or use one of available generators (like this one ). Typically something like this should be good to start with ( don't forget to replace @example.com to your actual domain ): _dmarc.example.com. IN TXT \"v=DMARC1; p=none; rua=mailto:dmarc.report@example.com; ruf=mailto:dmarc.report@example.com; sp=none; ri=86400\" Or a bit more strict policies ( mind p=quarantine and sp=quarantine ): _dmarc IN TXT \"v=DMARC1; p=quarantine; rua=mailto:dmarc.report@example.com; ruf=mailto:dmarc.report@example.com; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; sp=quarantine\" DMARC status is not being displayed instantly in Gmail for instance. If you want to check it directly after DNS entries, you can use some services around the Internet such as from Global Cyber Alliance or RedSift . In other cases, email clients will show \"DMARC: PASS\" in ~1 day or so. Reference: #1511","title":"Enabling DMARC"},{"location":"config/best-practices/spf/","text":"From Wikipedia : Quote Sender Policy Framework (SPF) is a simple email-validation system designed to detect email spoofing by providing a mechanism to allow receiving mail exchangers to check that incoming mail from a domain comes from a host authorized by that domain's administrators. The list of authorized sending hosts for a domain is published in the Domain Name System (DNS) records for that domain in the form of a specially formatted TXT record. Email spam and phishing often use forged \"from\" addresses, so publishing and checking SPF records can be considered anti-spam techniques. Note For a more technical review: https://github.com/internetstandards/toolbox-wiki/blob/master/SPF-how-to.md Add a SPF Record To add a SPF record in your DNS, insert the following line in your DNS zone: ; MX record must be declared for SPF to work example.com. IN MX 1 mail.example.com. ; SPF record example.com. IN TXT \"v=spf1 mx ~all\" This enables the Softfail mode for SPF. You could first add this SPF record with a very low TTL. SoftFail is a good setting for getting started and testing, as it lets all email through, with spams tagged as such in the mailbox. After verification, you might want to change your SPF record to v=spf1 mx -all so as to enforce the HardFail policy. See http://www.open-spf.org/SPF_Record_Syntax for more details about SPF policies. In any case, increment the SPF record's TTL to its final value. Backup MX, Secondary MX For whitelisting a IP Address from the SPF test, you can create a config file (see policyd-spf.conf ) and mount that file into /etc/postfix-policyd-spf-python/policyd-spf.conf . Example: Create and edit a policyd-spf.conf file at docker-data/dms/config/postfix-policyd-spf.conf : debugLevel = 1 #0(only errors)-4(complete data received) skip_addresses = 127.0.0.0/8,::ffff:127.0.0.0/104,::1 # Preferably use IP-Addresses for whitelist lookups: Whitelist = 192.168.0.0/31,192.168.1.0/30 # Domain_Whitelist = mx1.not-example.com,mx2.not-example.com Then add this line to docker-compose.yml : volumes : - ./docker-data/dms/config/postfix-policyd-spf.conf:/etc/postfix-policyd-spf-python/policyd-spf.conf","title":"SPF"},{"location":"config/best-practices/spf/#add-a-spf-record","text":"To add a SPF record in your DNS, insert the following line in your DNS zone: ; MX record must be declared for SPF to work example.com. IN MX 1 mail.example.com. ; SPF record example.com. IN TXT \"v=spf1 mx ~all\" This enables the Softfail mode for SPF. You could first add this SPF record with a very low TTL. SoftFail is a good setting for getting started and testing, as it lets all email through, with spams tagged as such in the mailbox. After verification, you might want to change your SPF record to v=spf1 mx -all so as to enforce the HardFail policy. See http://www.open-spf.org/SPF_Record_Syntax for more details about SPF policies. In any case, increment the SPF record's TTL to its final value.","title":"Add a SPF Record"},{"location":"config/best-practices/spf/#backup-mx-secondary-mx","text":"For whitelisting a IP Address from the SPF test, you can create a config file (see policyd-spf.conf ) and mount that file into /etc/postfix-policyd-spf-python/policyd-spf.conf . Example: Create and edit a policyd-spf.conf file at docker-data/dms/config/postfix-policyd-spf.conf : debugLevel = 1 #0(only errors)-4(complete data received) skip_addresses = 127.0.0.0/8,::ffff:127.0.0.0/104,::1 # Preferably use IP-Addresses for whitelist lookups: Whitelist = 192.168.0.0/31,192.168.1.0/30 # Domain_Whitelist = mx1.not-example.com,mx2.not-example.com Then add this line to docker-compose.yml : volumes : - ./docker-data/dms/config/postfix-policyd-spf.conf:/etc/postfix-policyd-spf-python/policyd-spf.conf","title":"Backup MX, Secondary MX"},{"location":"config/security/fail2ban/","text":"Fail2Ban is installed automatically and bans IP addresses for 3 hours after 3 failed attempts in 10 minutes by default. Configuration files If you want to change this, you can easily edit our github example file: config-examples/fail2ban-jail.cf . You can do the same with the values from fail2ban.conf , e.g dbpurgeage . In that case you need to edit: config-examples/fail2ban-fail2ban.cf . The configuration files need to be located at the root of the /tmp/docker-mailserver/ volume bind (usually ./docker-data/dms/config/:/tmp/docker-mailserver/ ). This following configuration files from /tmp/docker-mailserver/ will be copied during container startup. fail2ban-jail.cf -> /etc/fail2ban/jail.d/user-jail.local fail2ban-fail2ban.cf -> /etc/fail2ban/fail2ban.local Docker-compose config Example configuration volume bind: volumes : - ./docker-data/dms/config/:/tmp/docker-mailserver/ Attention docker-mailserver must be launched with the NET_ADMIN capability in order to be able to install the nftables rules that actually ban IP addresses. Thus either include --cap-add=NET_ADMIN in the docker run command, or the equivalent in docker-compose.yml : cap_add : - NET_ADMIN Running fail2ban in a rootless container RootlessKit is the fakeroot implementation for supporting rootless mode in Docker and Podman. By default RootlessKit uses the builtin port forwarding driver , which does not propagate source IP addresses. It is necessary for fail2ban to have access to the real source IP addresses in order to correctly identify clients. This is achieved by changing the port forwarding driver to slirp4netns , which is slower than builtin but does preserve the real source IPs. Docker with slirp4netns port driver For rootless mode in Docker, create ~/.config/systemd/user/docker.service.d/override.conf with the following content: [Service] Environment=\"DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=slirp4netns\" And then restart the daemon: $ systemctl --user daemon-reload $ systemctl --user restart docker Note This changes the port driver for all rootless containers managed by Docker. Per container configuration is not supported, if you need that consider Podman instead. Podman with slirp4netns port driver Rootless Podman requires adding the value slirp4netns:port_handler=slirp4netns to the --network CLI option, or network_mode setting in your docker-compose.yml . You must also add the ENV NETWORK_INTERFACE=tap0 , because Podman uses a hard-coded interface name for slirp4netns . Example services : mailserver : network_mode : \"slirp4netns:port_handler=slirp4netns\" environment : - ENABLE_FAIL2BAN=1 - NETWORK_INTERFACE=tap0 ... Note slirp4netns is not compatible with user-defined networks. Manage bans You can also manage and list the banned IPs with the setup.sh script. List bans ./setup.sh fail2ban Un-ban Here 192.168.1.15 is our banned IP. ./setup.sh fail2ban unban 192 .168.1.15","title":"Fail2Ban"},{"location":"config/security/fail2ban/#configuration-files","text":"If you want to change this, you can easily edit our github example file: config-examples/fail2ban-jail.cf . You can do the same with the values from fail2ban.conf , e.g dbpurgeage . In that case you need to edit: config-examples/fail2ban-fail2ban.cf . The configuration files need to be located at the root of the /tmp/docker-mailserver/ volume bind (usually ./docker-data/dms/config/:/tmp/docker-mailserver/ ). This following configuration files from /tmp/docker-mailserver/ will be copied during container startup. fail2ban-jail.cf -> /etc/fail2ban/jail.d/user-jail.local fail2ban-fail2ban.cf -> /etc/fail2ban/fail2ban.local","title":"Configuration files"},{"location":"config/security/fail2ban/#docker-compose-config","text":"Example configuration volume bind: volumes : - ./docker-data/dms/config/:/tmp/docker-mailserver/ Attention docker-mailserver must be launched with the NET_ADMIN capability in order to be able to install the nftables rules that actually ban IP addresses. Thus either include --cap-add=NET_ADMIN in the docker run command, or the equivalent in docker-compose.yml : cap_add : - NET_ADMIN","title":"Docker-compose config"},{"location":"config/security/fail2ban/#running-fail2ban-in-a-rootless-container","text":"RootlessKit is the fakeroot implementation for supporting rootless mode in Docker and Podman. By default RootlessKit uses the builtin port forwarding driver , which does not propagate source IP addresses. It is necessary for fail2ban to have access to the real source IP addresses in order to correctly identify clients. This is achieved by changing the port forwarding driver to slirp4netns , which is slower than builtin but does preserve the real source IPs.","title":"Running fail2ban in a rootless container"},{"location":"config/security/fail2ban/#docker-with-slirp4netns-port-driver","text":"For rootless mode in Docker, create ~/.config/systemd/user/docker.service.d/override.conf with the following content: [Service] Environment=\"DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=slirp4netns\" And then restart the daemon: $ systemctl --user daemon-reload $ systemctl --user restart docker Note This changes the port driver for all rootless containers managed by Docker. Per container configuration is not supported, if you need that consider Podman instead.","title":"Docker with slirp4netns port driver"},{"location":"config/security/fail2ban/#podman-with-slirp4netns-port-driver","text":"Rootless Podman requires adding the value slirp4netns:port_handler=slirp4netns to the --network CLI option, or network_mode setting in your docker-compose.yml . You must also add the ENV NETWORK_INTERFACE=tap0 , because Podman uses a hard-coded interface name for slirp4netns . Example services : mailserver : network_mode : \"slirp4netns:port_handler=slirp4netns\" environment : - ENABLE_FAIL2BAN=1 - NETWORK_INTERFACE=tap0 ... Note slirp4netns is not compatible with user-defined networks.","title":"Podman with slirp4netns port driver"},{"location":"config/security/fail2ban/#manage-bans","text":"You can also manage and list the banned IPs with the setup.sh script.","title":"Manage bans"},{"location":"config/security/fail2ban/#list-bans","text":"./setup.sh fail2ban","title":"List bans"},{"location":"config/security/fail2ban/#un-ban","text":"Here 192.168.1.15 is our banned IP. ./setup.sh fail2ban unban 192 .168.1.15","title":"Un-ban"},{"location":"config/security/mail_crypt/","text":"Info The Mail crypt plugin is used to secure email messages stored in a Dovecot system. Messages are encrypted before written to storage and decrypted after reading. Both operations are transparent to the user. In case of unauthorized access to the storage backend, the messages will, without access to the decryption keys, be unreadable to the offending party. There can be a single encryption key for the whole system or each user can have a key of their own. The used cryptographical methods are widely used standards and keys are stored in portable formats, when possible. Official Dovecot documentation: https://doc.dovecot.org/configuration_manual/mail_crypt_plugin/ Single Encryption Key / Global Method Create 10-custom.conf and populate it with the following: # Enables mail_crypt for all services (imap, pop3, etc) mail_plugins = $mail_plugins mail_crypt plugin { mail_crypt_global_private_key = File Names & Extensions For command 1 - 3, we append the .conf suffix to the module name to get the correct file name automatically. For commands 4 - 6, the file name is fixed (you don't even need to provide it). For command 7, you will need to provide the whole file name (including the suffix) yourself! You can also have comments (the line starts with # ) and blank lines in rspamd-modules.conf - they are properly handled and not evaluated. Adjusting Modules This Way These simple commands are meant to give users the ability to easily alter modules and their options. As a consequence, they are not powerful enough to enable multi-line adjustments. If you need to do something more complex, we advise to do that manually ! Advanced Configuration Abusix Integration This subsection gives information about the integration of Abusix , \"a set of blocklists that work as an additional email security layer for your existing mail environment\". The setup is straight-forward and well documented: Create an account Retrieve your API key Navigate to the \"Getting Started\" documentation for Rspamd and follow the steps described there Make sure to change to your private API key We recommend mounting the files directly into the container, as they are rather big and not manageable with the modules script . If mounted to the correct location, Rspamd will automatically pick them up. While Abusix can be integrated into Postfix, Postscreen and a multitude of other software, we recommend integrating Abusix only into a single piece of software running in your mail server - everything else would be excessive and wasting queries. Moreover, we recommend the integration into suitable filtering software and not Postfix itself, as software like Postscreen or Rspamd can properly evaluate the return codes and other configuration.","title":"Rspamd"},{"location":"config/security/rspamd/#about","text":"Rspamd is a \"fast, free and open-source spam filtering system\" . DMS integrates Rspamd like any other service. You will need to enable Rspamd (via ENABLE_RSPAMD=1 ) manually as it is disabled by default. We provide a very simple but easy to maintain setup of Rspamd. If you want to have a look at the default configuration files for Rspamd that DMS packs, navigate to target/rspamd/ inside the repository . Please consult the section \"The Default Configuration\" section down below for a written overview. If you want to adjust Rspamd's configuration, have a look at the \"Providing Custom Settings & Overriding Settings\" section down below. AMD64 vs ARM64 We are currently doing a best-effort installation of Rspamd for ARM64 (from the Debian backports repository for Debian 11). The current version difference is two minor versions (AMD64 is at version 3.4, ARM64 at 3.2 [13th Feb 2023]). Maintainers noticed only few differences, some of them with a big impact though. For those running Rspamd on ARM64, we recommend disabling the DKIM signing module if you don't use it.","title":"About"},{"location":"config/security/rspamd/#the-default-configuration","text":"","title":"The Default Configuration"},{"location":"config/security/rspamd/#mode-of-operation","text":"The proxy worker operates in self-scan mode . This simplifies the setup as we do not require a normal worker. You can easily change this though by overriding the configuration by DMS . DMS does not set a default password for the controller worker. You may want to do that yourself. In setup where you already have an authentication provider in front of the Rspamd webpage, you may add secure_ip = \"0.0.0.0/0\"; to worker-controller.inc to disable password authentication inside Rspamd completely.","title":"Mode of Operation"},{"location":"config/security/rspamd/#modules","text":"You can find a list of all Rspamd modules on their website .","title":"Modules"},{"location":"config/security/rspamd/#disabled-by-default","text":"DMS disables certain modules (clickhouse, elastic, greylist, neural, reputation, spamassassin, url_redirector, metric_exporter) by default. We believe these are not required in a standard setup, and they would otherwise needlessly use system resources.","title":"Disabled By Default"},{"location":"config/security/rspamd/#anti-virus-clamav","text":"You can choose to enable ClamAV, and Rspamd will then use it to check for viruses. Just set the environment variable ENABLE_CLAMAV=1 .","title":"Anti-Virus (ClamAV)"},{"location":"config/security/rspamd/#rbls-realtime-blacklists-dnsbls-dns-based-blacklists","text":"The RBL module is enabled by default. As a consequence, Rspamd will perform DNS lookups to a variety of blacklists. Whether an RBL or a DNSBL is queried depends on where the domain name was obtained: RBL servers are queried with IP addresses extracted from message headers, DNSBL server are queried with domains and IP addresses extracted from the message body [ source ]. Rspamd and DNS Block Lists When the RBL module is enabled, Rspamd will do a variety of DNS requests to (amongst other things) DNSBLs. There are a variety of issues involved when using DNSBLs. Rspamd will try to mitigate some of them by properly evaluating all return codes. This evaluation is a best effort though, so if the DNSBL operators change or add return codes, it may take a while for Rspamd to adjust as well. If you want to use DNSBLs, try to use your own DNS resolver and make sure it is set up correctly, i.e. it should be a non-public & recursive resolver. Otherwise, you might not be able ( see this Spamhaus post ) to make use of the block lists.","title":"RBLs (Realtime Blacklists) / DNSBLs (DNS-based Blacklists)"},{"location":"config/security/rspamd/#missing-in-the-current-implementation","text":"We currently lack easy integration for DKIM signing outgoing mails . We use OpenDKIM though which works just as well. If you want to use Rspamd for DKIM signing, you need to provide all settings yourself and probably also set the environment variable ENABLE_OPENDKIM=0 . Rspamd will still check for valid DKIM signatures for incoming mail by default.","title":"Missing in the Current Implementation"},{"location":"config/security/rspamd/#providing-custom-settings-overriding-settings","text":"","title":"Providing Custom Settings & Overriding Settings"},{"location":"config/security/rspamd/#manually","text":"DMS brings sane default settings for Rspamd. They are located at /etc/rspamd/local.d/ inside the container (or target/rspamd/local.d/ in the repository). If you want to change these settings and / or provide your own settings, you can place files at /etc/rspamd/override.d/ which will override Rspamd settings and DMS settings (re-)place files at /etc/rspamd/local.d/ to override DMS settings and merge them with Rspamd settings Clashing Overrides Note that when also using the rspamd-commands file , files in override.d may be overwritten in case you adjust them manually and with the help of the file.","title":"Manually"},{"location":"config/security/rspamd/#with-the-help-of-a-custom-file","text":"DMS provides the ability to do simple adjustments to Rspamd modules with the help of a single file. Just place a file called rspamd-modules.conf into the directory docker-data/dms/config/ (which translates to /tmp/docker-mailserver/ in the container). If this file is present, DMS will evaluate it. The structure is very simple. Each line in the file looks like this: COMMAND ARGUMENT1 ARGUMENT2 ARGUMENT3 where COMMAND can be: disable-module : disables the module with name ARGUMENT1 enable-module : explicitly enables the module with name ARGUMENT1 set-option-for-module : sets the value for option ARGUMENT2 to ARGUMENT3 inside module ARGUMENT1 set-option-for-controller : set the value of option ARGUMENT1 to ARGUMENT2 for the controller worker set-option-for-proxy : set the value of option ARGUMENT1 to ARGUMENT2 for the proxy worker set-common-option : set the option ARGUMENT1 that defines basic Rspamd behaviour to value ARGUMENT2 add-line : this will add the complete line after ARGUMENT1 (with all characters) to the file /etc/rspamd/override.d/ File Names & Extensions For command 1 - 3, we append the .conf suffix to the module name to get the correct file name automatically. For commands 4 - 6, the file name is fixed (you don't even need to provide it). For command 7, you will need to provide the whole file name (including the suffix) yourself! You can also have comments (the line starts with # ) and blank lines in rspamd-modules.conf - they are properly handled and not evaluated. Adjusting Modules This Way These simple commands are meant to give users the ability to easily alter modules and their options. As a consequence, they are not powerful enough to enable multi-line adjustments. If you need to do something more complex, we advise to do that manually !","title":"With the Help of a Custom File"},{"location":"config/security/rspamd/#advanced-configuration","text":"","title":"Advanced Configuration"},{"location":"config/security/rspamd/#abusix-integration","text":"This subsection gives information about the integration of Abusix , \"a set of blocklists that work as an additional email security layer for your existing mail environment\". The setup is straight-forward and well documented: Create an account Retrieve your API key Navigate to the \"Getting Started\" documentation for Rspamd and follow the steps described there Make sure to change to your private API key We recommend mounting the files directly into the container, as they are rather big and not manageable with the modules script . If mounted to the correct location, Rspamd will automatically pick them up. While Abusix can be integrated into Postfix, Postscreen and a multitude of other software, we recommend integrating Abusix only into a single piece of software running in your mail server - everything else would be excessive and wasting queries. Moreover, we recommend the integration into suitable filtering software and not Postfix itself, as software like Postscreen or Rspamd can properly evaluate the return codes and other configuration.","title":"Abusix Integration"},{"location":"config/security/ssl/","text":"There are multiple options to enable SSL (via SSL_TYPE ): Using letsencrypt (recommended) Using Caddy Using Traefik Using self-signed certificates Using your own certificates After installation, you can test your setup with: checktls.com testssl.sh Exposure of DNS labels through Certificate Transparency All public Certificate Authorities (CAs) are required to log certificates they issue publicly via Certificate Transparency . This helps to better establish trust. When using a public CA for certificates used in private networks, be aware that the associated DNS labels in the certificate are logged publicly and easily searchable . These logs are append only , you cannot redact this information. You could use a wildcard certificate . This avoids accidentally leaking information to the internet, but keep in mind the potential security risks of wildcard certs. The FQDN An FQDN ( Fully Qualified Domain Name ) such as mail.example.com is required for docker-mailserver to function correctly, especially for looking up the correct SSL certificate to use. Internally, hostname -f will be used to retrieve the FQDN as configured in the below examples. Wildcard certificates (eg: *.example.com ) are supported for SSL_TYPE=letsencrypt . Your configured FQDN below may be mail.example.com , and your wildcard certificate provisioned to /etc/letsencrypt/live/example.com which will be checked as a fallback FQDN by docker-mailserver . Docker CLI options --hostname and optionally --domainname docker run --hostname mail --domainname example.com # `--domainname` is not required: docker run --hostname mail.example.com docker-compose.yml config services : mailserver : hostname : mail domainname : example.com # `domainname` is not required: services : mailserver : hostname : mail.example.com Bare domains (eg: example.com ) should only use the hostname option docker run --hostname example.com services : mailserver : hostname : example.com Provisioning methods Let's Encrypt (Recommended) To enable Let's Encrypt for docker-mailserver , you have to: Get your certificate using the Let's Encrypt client Certbot . For your docker-mailserver container: Add the environment variable SSL_TYPE=letsencrypt . Mount your local letsencrypt folder as a volume to /etc/letsencrypt . You don't have to do anything else. Enjoy! Note /etc/letsencrypt/live stores provisioned certificates in individual folders named by their FQDN. Make sure that the entire folder is mounted to docker-mailserver as there are typically symlinks from /etc/letsencrypt/live/mail.example.com to /etc/letsencrypt/archive . Example Add these additions to the mailserver service in your docker-compose.yml : services : mailserver : # For the FQDN 'mail.example.com': hostname : mail domainname : example.com environment : - SSL_TYPE=letsencrypt volumes : - /etc/letsencrypt:/etc/letsencrypt Example using Docker for Let's Encrypt Certbot provisions certificates to /etc/letsencrypt . Add a volume to store these, so that they can later be accessed by docker-mailserver container. You may also want to persist Certbot logs , just in case you need to troubleshoot. Getting a certificate is this simple! ( Referencing: Certbot docker instructions and certonly --standalone mode ): # Change `mail.example.com` below to your own FQDN. # Requires access to port 80 from the internet, adjust your firewall if needed. docker run --rm -it \\ -v \" ${ PWD } /docker-data/certbot/certs/:/etc/letsencrypt/\" \\ -v \" ${ PWD } /docker-data/certbot/logs/:/var/log/letsencrypt/\" \\ -p 80 :80 \\ certbot/certbot certonly --standalone -d mail.example.com Add a volume for docker-mailserver that maps the local certbot/certs/ folder to the container path /etc/letsencrypt/ . Example Add these additions to the mailserver service in your docker-compose.yml : services : mailserver : # For the FQDN 'mail.example.com': hostname : mail domainname : example.com environment : - SSL_TYPE=letsencrypt volumes : - ./docker-data/certbot/certs/:/etc/letsencrypt The certificate setup is complete, but remember it will expire . Consider automating renewals. Renewing Certificates When running the above certonly --standalone snippet again, the existing certificate is renewed if it would expire within 30 days. Alternatively, Certbot can look at all the certificates it manages, and only renew those nearing their expiry via the renew command : # This will need access to port 443 from the internet, adjust your firewall if needed. docker run --rm -it \\ -v \" ${ PWD } /docker-data/certbot/certs/:/etc/letsencrypt/\" \\ -v \" ${ PWD } /docker-data/certbot/logs/:/var/log/letsencrypt/\" \\ -p 80 :80 \\ -p 443 :443 \\ certbot/certbot renew This process can also be automated via cron or systemd timers . Using a different ACME CA Certbot does support alternative certificate providers via the --server option. In most cases you'll want to use the default Let's Encrypt . Example using certbot-dns-cloudflare with Docker If you are unable get a certificate via the HTTP-01 (port 80) or TLS-ALPN-01 (port 443) challenge types , the DNS-01 challenge can be useful ( this challenge can additionally issue wildcard certificates ). This guide shows how to use the DNS-01 challenge with Cloudflare as your DNS provider. Obtain a Cloudflare API token: Login into your Cloudflare dashboard. Navigate to the API Tokens page . Click \"Create Token\", and choose the Edit zone DNS template ( Certbot requires the ZONE:DNS:Edit permission ). Only include the necessary Zone resource configuration Be sure to configure \"Zone Resources\" section on this page to Include -> Specific zone -> . This restricts the API token to only this zone (domain) which is an important security measure. Store the API token you received in a file cloudflare.ini with content: dns_cloudflare_api_token = YOUR_CLOUDFLARE_TOKEN_HERE As this is sensitive data, you should restrict access to it with chmod 600 and chown 0:0 . Store the file in a folder if you like, such as docker-data/certbot/secrets/ . Your docker-compose.yml should include the following: services : mailserver : environments : # Set SSL certificate type. - SSL_TYPE=letsencrypt volumes : # Mount the cert folder generated by Certbot into mail-server: - ./docker-data/certbot/certs/:/etc/letsencrypt/:ro certbot-cloudflare : image : certbot/dns-cloudflare:latest command : certonly --dns-cloudflare --dns-cloudflare-credentials /run/secrets/cloudflare-api-token -d mail.example.com volumes : - ./docker-data/certbot/certs/:/etc/letsencrypt/ - ./docker-data/certbot/logs/:/var/log/letsencrypt/ secrets : - cloudflare-api-token # Docs: https://docs.docker.com/engine/swarm/secrets/#use-secrets-in-compose # WARNING: In compose configs without swarm, the long syntax options have no effect, # Ensure that you properly `chmod 600` and `chown 0:0` the file on disk. Effectively treated as a bind mount. secrets : cloudflare-api-token : file : ./docker-data/certbot/secrets/cloudflare.ini Alternative using the docker run command ( secrets feature is not available): docker run \\ --volume \" ${ PWD } /docker-data/certbot/certs/:/etc/letsencrypt/\" \\ --volume \" ${ PWD } /docker-data/certbot/logs/:/var/log/letsencrypt/\" \\ --volume \" ${ PWD } /docker-data/certbot/secrets/:/tmp/secrets/certbot/\" certbot/dns-cloudflare \\ certonly --dns-cloudflare --dns-cloudflare-credentials /tmp/secrets/certbot/cloudflare.ini -d mail.example.com Run the service to provision a certificate: docker-compose run certbot-cloudflare You should see the following log output: Saving debug log to /var/log/letsencrypt/letsencrypt. log | Requesting a certificate for mail.example.com Waiting 10 seconds for DNS changes to propagate Successfully received certificate. Certificate is saved at: /etc/letsencrypt/live/mail.example.com/fullchain.pem Key is saved at: /etc/letsencrypt/live/mail.example.com/privkey.pem This certificate expires on YYYY-MM-DD. These files will be updated when the certificate renews. NEXT STEPS: - The certificate will need to be renewed before it expires. Certbot can automatically renew the certificate in background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal structions. After completing the steps above, your certificate should be ready to use. Renewing a certificate (Optional) We've only demonstrated how to provision a certificate, but it will expire in 90 days and need to be renewed before then. In the following example, add a new service ( certbot-cloudflare-renew ) into docker-compose.yml that will handle certificate renewals: services : certbot-cloudflare-renew : image : certbot/dns-cloudflare:latest command : renew --dns-cloudflare --dns-cloudflare-credentials /run/secrets/cloudflare-api-token volumes : - ./docker-data/certbot/certs/:/etc/letsencrtypt/ - ./docker-data/certbot/logs/:/var/log/letsencrypt/ secrets : - cloudflare-api-token You can manually run this service to renew the cert within 90 days: docker-compose run certbot-cloudflare-renew You should see the following output (The following log was generated with --dry-run options) Saving debug log to /var/log/letsencrypt/letsencrypt.log - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Processing /etc/letsencrypt/renewal/mail.example.com.conf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Account registered. Simulating renewal of an existing certificate for mail.example.com Waiting 10 seconds for DNS changes to propagate - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/mail.example.com/fullchain.pem (success) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - It is recommended to automate this renewal via a task scheduler like a systemd timer or in crontab ( crontab example: Checks every day if the certificate should be renewed) 0 0 * * * docker-compose -f PATH_TO_YOUR_DOCKER_COMPOSE_YML up certbot-cloudflare-renew Example using nginx-proxy and acme-companion with Docker If you are running a web server already, port 80 will be in use which Certbot requires. You could use the Certbot --webroot feature, but it is more common to leverage a reverse proxy that manages the provisioning and renewal of certificates for your services automatically. In the following example, we show how docker-mailserver can be run alongside the docker containers nginx-proxy and acme-companion ( Referencing: acme-companion documentation ): Start the reverse proxy ( nginx-proxy ): docker run --detach \\ --name nginx-proxy \\ --restart always \\ --publish 80 :80 \\ --publish 443 :443 \\ --volume \" ${ PWD } /docker-data/nginx-proxy/html/:/usr/share/nginx/html/\" \\ --volume \" ${ PWD } /docker-data/nginx-proxy/vhost.d/:/etc/nginx/vhost.d/\" \\ --volume \" ${ PWD } /docker-data/acme-companion/certs/:/etc/nginx/certs/:ro\" \\ --volume '/var/run/docker.sock:/tmp/docker.sock:ro' \\ nginxproxy/nginx-proxy Then start the certificate provisioner ( acme-companion ), which will provide certificates to nginx-proxy : # Inherit `nginx-proxy` volumes via `--volumes-from`, but make `certs/` writeable: docker run --detach \\ --name nginx-proxy-acme \\ --restart always \\ --volumes-from nginx-proxy \\ --volume \" ${ PWD } /docker-data/acme-companion/certs/:/etc/nginx/certs/:rw\" \\ --volume \" ${ PWD } /docker-data/acme-companion/acme-state/:/etc/acme.sh/\" \\ --volume '/var/run/docker.sock:/var/run/docker.sock:ro' \\ --env 'DEFAULT_EMAIL=admin@example.com' \\ nginxproxy/acme-companion Start the rest of your web server containers as usual. Start a dummy container to provision certificates for your FQDN (eg: mail.example.com ). acme-companion will detect the container and generate a Let's Encrypt certificate for your domain, which can be used by docker-mailserver : docker run --detach \\ --name webmail \\ --env 'VIRTUAL_HOST=mail.example.com' \\ --env 'LETSENCRYPT_HOST=mail.example.com' \\ --env 'LETSENCRYPT_EMAIL=admin@example.com' \\ nginx You may want to add --env LETSENCRYPT_TEST=true to the above while testing, to avoid the Let's Encrypt certificate generation rate limits. Make sure your mount path to the letsencrypt certificates directory is correct. Edit your docker-compose.yml for the mailserver service to have volumes added like below: volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - ./docker-data/acme-companion/certs/:/etc/letsencrypt/live/:ro Then from the docker-compose.yml project directory, run: docker-compose up -d mailserver . Example using nginx-proxy and acme-companion with docker-compose The following example is the basic setup you need for using nginx-proxy and acme-companion with docker-mailserver ( Referencing: acme-companion documentation ): Example: docker-compose.yml You should have an existing docker-compose.yml with a mailserver service. Below are the modifications to add for integrating with nginx-proxy and acme-companion services: version : '3.8' services : # Add the following `environment` and `volumes` to your existing `mailserver` service: mailserver : environment : # SSL_TYPE: Uses the `letsencrypt` method to find mounted certificates. # VIRTUAL_HOST: The FQDN that `nginx-proxy` will configure itself to handle for HTTP[S] connections. # LETSENCRYPT_HOST: The FQDN for a certificate that `acme-companion` will provision and renew. - SSL_TYPE=letsencrypt - VIRTUAL_HOST=mail.example.com - LETSENCRYPT_HOST=mail.example.com volumes : - ./docker-data/acme-companion/certs/:/etc/letsencrypt/live/:ro # If you don't yet have your own `nginx-proxy` and `acme-companion` setup, # here is an example you can use: reverse-proxy : image : nginxproxy/nginx-proxy container_name : nginx-proxy restart : always ports : # Port 80: Required for HTTP-01 challenges to `acme-companion`. # Port 443: Only required for containers that need access over HTTPS. TLS-ALPN-01 challenge not supported. - \"80:80\" - \"443:443\" volumes : # `certs/`: Managed by the `acme-companion` container (_read-only_). # `docker.sock`: Required to interact with containers via the Docker API. # `dhparam`: A named data volume to prevent `nginx-proxy` creating an anonymous volume each time. - ./docker-data/nginx-proxy/html/:/usr/share/nginx/html/ - ./docker-data/nginx-proxy/vhost.d/:/etc/nginx/vhost.d/ - ./docker-data/acme-companion/certs/:/etc/nginx/certs/:ro - /var/run/docker.sock:/tmp/docker.sock:ro - dhparam:/etc/nginx/dhparam acme-companion : image : nginxproxy/acme-companion container_name : nginx-proxy-acme restart : always environment : # Only docker-compose v2 supports: `volumes_from: [nginx-proxy]`, # reference the _reverse-proxy_ `container_name` here: - NGINX_PROXY_CONTAINER=nginx-proxy volumes : # `html/`: Write ACME HTTP-01 challenge files that `nginx-proxy` will serve. # `vhost.d/`: To enable web access via `nginx-proxy` to HTTP-01 challenge files. # `certs/`: To store certificates and private keys. # `acme-state/`: To persist config and state for the ACME provisioner (`acme.sh`). # `docker.sock`: Required to interact with containers via the Docker API. - ./docker-data/nginx-proxy/html/:/usr/share/nginx/html/ - ./docker-data/nginx-proxy/vhost.d/:/etc/nginx/vhost.d/ - ./docker-data/acme-companion/certs/:/etc/nginx/certs/:rw - ./docker-data/acme-companion/acme-state/:/etc/acme.sh/ - /var/run/docker.sock:/var/run/docker.sock:ro # Once `nginx-proxy` fixes their Dockerfile, this named data volume can be removed from docs. # Users can opt for a local bind mount volume like all others if they prefer, but this volume # is only intended to be temporary. volumes : dhparam : Optional ENV vars worth knowing about Per container ENV that acme-companion will detect to override default provisioning settings: LETSENCRYPT_TEST=true : Recommended during initial setup . Otherwise the default production endpoint has a rate limit of 5 duplicate certificates per week . Overrides ACME_CA_URI to use the Let's Encrypt staging endpoint. LETSENCRYPT_EMAIL : For when you don't use DEFAULT_EMAIL on acme-companion , or want to assign a different email contact for this container. LETSENCRYPT_KEYSIZE : Allows you to configure the type (RSA or ECDSA) and size of the private key for your certificate. Default is RSA 4096. LETSENCRYPT_RESTART_CONTAINER=true : When the certificate is renewed, the entire container will be restarted to ensure the new certificate is used. acme-companion ENV for default settings that apply to all containers using LETSENCRYPT_HOST : DEFAULT_EMAIL : An email address that the CA ( eg: Let's Encrypt ) can contact you about expiring certificates, failed renewals, or for account recovery. You may want to use an email address not handled by your mail-server to ensure deliverability in the event your mail-server breaks. CERTS_UPDATE_INTERVAL : If you need to adjust the frequency to check for renewals. 3600 seconds (1 hour) by default. DEBUG=1 : Should be helpful when troubleshooting provisioning issues from acme-companion logs. ACME_CA_URI : Useful in combination with CA_BUNDLE to use a private CA. To change the default Let's Encrypt endpoint to the staging endpoint, use https://acme-staging-v02.api.letsencrypt.org/directory . CA_BUNDLE : If you want to use a private CA instead of Let's Encrypt . Alternative to required ENV on mailserver service While you will still need both nginx-proxy and acme-companion containers, you can manage certificates without adding ENV vars to containers. Instead the ENV is moved into a file and uses the acme-companion feature Standalone certificates . This requires adding another shared volume between nginx-proxy and acme-companion : services : reverse-proxy : volumes : - ./docker-data/nginx-proxy/conf.d/:/etc/nginx/conf.d/ acme-companion : volumes : - ./docker-data/nginx-proxy/conf.d/:/etc/nginx/conf.d/ - ./docker-data/acme-companion/standalone.sh:/app/letsencrypt_user_data:ro acme-companion mounts a shell script ( standalone.sh ), which defines variables to customize certificate provisioning: # A list IDs for certificates to provision: LETSENCRYPT_STANDALONE_CERTS =( 'mail' ) # Each ID inserts itself into the standard `acme-companion` supported container ENV vars below. # The LETSENCRYPT__HOST var is a list of FQDNs to provision a certificate for as the SAN field: LETSENCRYPT_mail_HOST =( 'mail.example.com' ) # Optional variables: LETSENCRYPT_mail_TEST = true LETSENCRYPT_mail_EMAIL = 'admin@example.com' # RSA-4096 => `4096`, ECDSA-256 => `ec-256`: LETSENCRYPT_mail_KEYSIZE = 4096 Unlike with the equivalent ENV for containers, changes to this file will not be detected automatically . You would need to wait until the next renewal check by acme-companion ( every hour by default ), restart acme-companion , or manually invoke the service loop : docker exec nginx-proxy-acme /app/signal_le_service Example using Let's Encrypt Certificates with a Synology NAS Version 6.2 and later of the Synology NAS DSM OS now come with an interface to generate and renew letencrypt certificates. Navigation into your DSM control panel and go to Security, then click on the tab Certificate to generate and manage letsencrypt certificates. Amongst other things, you can use these to secure your mail-server. DSM locates the generated certificates in a folder below /usr/syno/etc/certificate/_archive/ . Navigate to that folder and note the 6 character random folder name of the certificate you'd like to use. Then, add the following to your docker-compose.yml declaration file: # Note: If you have an existing setup that was working pre docker-mailserver v10.2, # '/tmp/dms/custom-certs' below has replaced the previous '/tmp/ssl' container path. volumes : - /usr/syno/etc/certificate/_archive//:/tmp/dms/custom-certs/ environment : - SSL_TYPE=manual - SSL_CERT_PATH=/tmp/dms/custom-certs/fullchain.pem - SSL_KEY_PATH=/tmp/dms/custom-certs/privkey.pem DSM-generated letsencrypt certificates get auto-renewed every three months. Caddy If you are using Caddy to renew your certificates, please note that only RSA certificates work. Read #1440 for details. In short for Caddy v1 the Caddyfile should look something like: https://mail.example.com { tls admin@example.com { key_type rsa2048 } } For Caddy v2 you can specify the key_type in your server's global settings, which would end up looking something like this if you're using a Caddyfile : { debug admin localhost:2019 http_port 80 https_port 443 default_sni example.com key_type rsa4096 } If you are instead using a json config for Caddy v2, you can set it in your site's TLS automation policies: Example Code { \"apps\" : { \"http\" : { \"servers\" : { \"srv0\" : { \"listen\" : [ \":443\" ], \"routes\" : [ { \"match\" : [ { \"host\" : [ \"mail.example.com\" , ] } ], \"handle\" : [ { \"handler\" : \"subroute\" , \"routes\" : [ { \"handle\" : [ { \"body\" : \"\" , \"handler\" : \"static_response\" } ] } ] } ], \"terminal\" : true }, ] } } }, \"tls\" : { \"automation\" : { \"policies\" : [ { \"subjects\" : [ \"mail.example.com\" , ], \"key_type\" : \"rsa2048\" , \"issuer\" : { \"email\" : \"admin@example.com\" , \"module\" : \"acme\" } }, { \"issuer\" : { \"email\" : \"admin@example.com\" , \"module\" : \"acme\" } } ] } } } } The generated certificates can be mounted: volumes : - ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.crt:/etc/letsencrypt/live/mail.example.com/fullchain.pem - ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.key:/etc/letsencrypt/live/mail.example.com/privkey.pem EC certificates fail in the TLS handshake: CONNECTED(00000003) 140342221178112:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:ssl/record/rec_layer_s3.c:1543:SSL alert number 40 no peer certificate available No client certificate CA names sent Traefik v2 Traefik is an open-source application proxy using the ACME protocol . Traefik can request certificates for domains and subdomains, and it will take care of renewals, challenge negotiations, etc. We strongly recommend to use Traefik 's major version 2. Traefik 's storage format is natively supported if the acme.json store is mounted into the container at /etc/letsencrypt/acme.json . The file is also monitored for changes and will trigger a reload of the mail services (Postfix and Dovecot). Wildcard certificates are supported. If your FQDN is mail.example.com and your wildcard certificate is *.example.com , add the ENV: SSL_DOMAIN = example.com . The mail-server will select it's certificate from acme.json checking these ENV for a matching FQDN ( in order of priority ): ${ SSL_DOMAIN } ${ HOSTNAME } ${ DOMAINNAME } This setup only comes with one caveat: The domain has to be configured on another service for Traefik to actually request it from Let's Encrypt , i.e. Traefik will not issue a certificate without a service / router demanding it. Example Code Here is an example setup for docker-compose : version : '3.8' services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail domainname : example.com volumes : - ./docker-data/traefik/acme.json:/etc/letsencrypt/acme.json:ro environment : SSL_TYPE : letsencrypt SSL_DOMAIN : mail.example.com # for a wildcard certificate, use # SSL_DOMAIN: example.com reverse-proxy : image : docker.io/traefik:latest #v2.5 container_name : docker-traefik ports : - \"80:80\" - \"443:443\" command : - --providers.docker - --entrypoints.http.address=:80 - --entrypoints.http.http.redirections.entryPoint.to=https - --entrypoints.http.http.redirections.entryPoint.scheme=https - --entrypoints.https.address=:443 - --entrypoints.https.http.tls.certResolver=letsencrypt - --certificatesresolvers.letsencrypt.acme.email=admin@example.com - --certificatesresolvers.letsencrypt.acme.storage=/acme.json - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=http volumes : - ./docker-data/traefik/acme.json:/acme.json - /var/run/docker.sock:/var/run/docker.sock:ro whoami : image : docker.io/traefik/whoami:latest labels : - \"traefik.http.routers.whoami.rule=Host(`mail.example.com`)\" Self-Signed Certificates Warning Use self-signed certificates only for testing purposes! This feature requires you to provide the following files into your docker-data/dms/config/ssl/ directory ( internal location: /tmp/docker-mailserver/ssl/ ): -key.pem -cert.pem demoCA/cacert.pem Where is the FQDN you've configured for your docker-mailserver container. Add SSL_TYPE=self-signed to your docker-mailserver environment variables. Postfix and Dovecot will be configured to use the provided certificate ( .pem files above ) during container startup. Generating a self-signed certificate Note Since docker-mailserver v10, support in setup.sh for generating a self-signed SSL certificate internally was removed. One way to generate self-signed certificates is with Smallstep's step CLI . This is exactly what docker-mailserver does for creating test certificates . For example with the FQDN mail.example.test , you can generate the required files by running: #! /bin/sh mkdir -p demoCA step certificate create \"Smallstep Root CA\" \"demoCA/cacert.pem\" \"demoCA/cakey.pem\" \\ --no-password --insecure \\ --profile root-ca \\ --not-before \"2021-01-01T00:00:00+00:00\" \\ --not-after \"2031-01-01T00:00:00+00:00\" \\ --san \"example.test\" \\ --san \"mail.example.test\" \\ --kty RSA --size 2048 step certificate create \"Smallstep Leaf\" mail.example.test-cert.pem mail.example.test-key.pem \\ --no-password --insecure \\ --profile leaf \\ --ca \"demoCA/cacert.pem\" \\ --ca-key \"demoCA/cakey.pem\" \\ --not-before \"2021-01-01T00:00:00+00:00\" \\ --not-after \"2031-01-01T00:00:00+00:00\" \\ --san \"example.test\" \\ --san \"mail.example.test\" \\ --kty RSA --size 2048 If you'd rather not install the CLI tool locally to run the step commands above; you can save the script above to a file such as generate-certs.sh ( and make it executable chmod +x generate-certs.sh ) in a directory that you want the certs to be placed (eg: docker-data/dms/custom-certs/ ), then use docker to run that script in a container: # '--user' is to keep ownership of the files written to # the local volume to use your systems User and Group ID values. docker run --rm -it \\ --user \" $( id -u ) : $( id -g ) \" \\ --volume \" ${ PWD } /docker-data/dms/custom-certs/:/tmp/step-ca/\" \\ --workdir \"/tmp/step-ca/\" \\ --entrypoint \"/tmp/step-ca/generate-certs.sh\" \\ smallstep/step-ca Bring Your Own Certificates You can also provide your own certificate files. Add these entries to your docker-compose.yml : volumes : - ./docker-data/dms/custom-certs/:/tmp/dms/custom-certs/:ro environment : - SSL_TYPE=manual # Values should match the file paths inside the container: - SSL_CERT_PATH=/tmp/dms/custom-certs/public.crt - SSL_KEY_PATH=/tmp/dms/custom-certs/private.key This will mount the path where your certificate files reside locally into the read-only container folder: /tmp/dms/custom-certs . The local and internal paths may be whatever you prefer, so long as both SSL_CERT_PATH and SSL_KEY_PATH point to the correct internal file paths. The certificate files may also be named to your preference, but should be PEM encoded. SSL_ALT_CERT_PATH and SSL_ALT_KEY_PATH are additional ENV vars to support a 2nd certificate as a fallback. Commonly known as hybrid or dual certificate support. This is useful for using a modern ECDSA as your primary certificate, and RSA as your fallback for older connections. They work in the same manner as the non- ALT versions. Info You may have to restart docker-mailserver once the certificates change. Testing a Certificate is Valid From your host: docker exec mailserver openssl s_client \\ -connect 0 .0.0.0:25 \\ -starttls smtp \\ -CApath /etc/ssl/certs/ Or: docker exec mailserver openssl s_client \\ -connect 0 .0.0.0:143 \\ -starttls imap \\ -CApath /etc/ssl/certs/ And you should see the certificate chain, the server certificate and: Verify return code: 0 (ok) In addition, to verify certificate dates: docker exec mailserver openssl s_client \\ -connect 0 .0.0.0:25 \\ -starttls smtp \\ -CApath /etc/ssl/certs/ \\ 2 >/dev/null | openssl x509 -noout -dates Plain-Text Access Warning Not recommended for purposes other than testing. Add this to docker-data/dms/config/dovecot.cf : ssl = yes disable_plaintext_auth = no These options in conjunction mean: SSL/TLS is offered to the client, but the client isn't required to use it. The client is allowed to login with plaintext authentication even when SSL/TLS isn't enabled on the connection. This is insecure , because the plaintext password is exposed to the internet. Importing Certificates Obtained via Another Source If you have another source for SSL/TLS certificates you can import them into the server via an external script. The external script can be found here: external certificate import script . Only compatible with docker-mailserver releases < v10.2 The script expects /etc/postfix/ssl/cert and /etc/postfix/ssl/key files to be configured paths for both Postfix and Dovecot to use. Since the docker-mailserver 10.2 release, certificate files have moved to /etc/dms/tls/ , and the file name may differ depending on provisioning method. This third-party script also has fullchain.pem and privkey.pem as hard-coded, thus is incompatible with other filenames. Additionally it has never supported handling ALT fallback certificates (for supporting dual/hybrid, RSA + ECDSA). The steps to follow are these: Transfer the new certificates to ./docker-data/dms/custom-certs/ (volume mounted to: /tmp/ssl/ ) You should provide fullchain.key and privkey.pem Place the script in ./docker-data/dms/config/ (volume mounted to: /tmp/docker-mailserver/ ) Make the script executable ( chmod +x tomav-renew-certs.sh ) Run the script: docker exec mailserver /tmp/docker-mailserver/tomav-renew-certs.sh If an error occurs the script will inform you. If not you will see both postfix and dovecot restart. After the certificates have been loaded you can check the certificate: openssl s_client \\ -servername mail.example.com \\ -connect 192 .168.0.72:465 \\ 2 >/dev/null | openssl x509 # or openssl s_client \\ -servername mail.example.com \\ -connect mail.example.com:465 \\ 2 >/dev/null | openssl x509 Or you can check how long the new certificate is valid with commands like: export SITE_URL = \"mail.example.com\" export SITE_IP_URL = \"192.168.0.72\" # can also use `mail.example.com` export SITE_SSL_PORT = \"993\" # imap port dovecot ##works: check if certificate will expire in two weeks #2 weeks is 1209600 seconds #3 weeks is 1814400 #12 weeks is 7257600 #15 weeks is 9072000 certcheck_2weeks = ` openssl s_client -connect ${ SITE_IP_URL } : ${ SITE_SSL_PORT } \\ -servername ${ SITE_URL } 2 > /dev/null | openssl x509 -noout -checkend 1209600 ` #################################### #notes: output could be either: #Certificate will not expire #Certificate will expire #################### What does the script that imports the certificates do: Check if there are new certs in the internal container folder: /tmp/ssl . Check with the ssl cert fingerprint if they differ from the current certificates. If so it will copy the certs to the right places. And restart postfix and dovecot. You can of course run the script by cron once a week or something. In that way you could automate cert renewal. If you do so it is probably wise to run an automated check on certificate expiry as well. Such a check could look something like this: # This script is run inside docker-mailserver via 'docker exec ...', using the 'mail' command to send alerts. ## code below will alert if certificate expires in less than two weeks ## please adjust varables! ## make sure the 'mail -s' command works! Test! export SITE_URL = \"mail.example.com\" export SITE_IP_URL = \"192.168.2.72\" # can also use `mail.example.com` export SITE_SSL_PORT = \"993\" # imap port dovecot # Below can be from a different domain; like your personal email, not handled by this docker-mailserver: export ALERT_EMAIL_ADDR = \"external-account@gmail.com\" certcheck_2weeks = ` openssl s_client -connect ${ SITE_IP_URL } : ${ SITE_SSL_PORT } \\ -servername ${ SITE_URL } 2 > /dev/null | openssl x509 -noout -checkend 1209600 ` #################################### #notes: output can be #Certificate will not expire #Certificate will expire #################### #echo \"certcheck 2 weeks gives $certcheck_2weeks\" ##automated check you might run by cron or something ## does the certificate expire within two weeks? if [ \" $certcheck_2weeks \" = \"Certificate will not expire\" ] ; then echo \"all is well, certwatch 2 weeks says $certcheck_2weeks \" else echo \"Cert seems to be expiring pretty soon, within two weeks: $certcheck_2weeks \" echo \"we will send an alert email and log as well\" logger Certwatch: cert $SITE_URL will expire in two weeks echo \"Certwatch: cert $SITE_URL will expire in two weeks\" | mail -s \"cert $SITE_URL expires in two weeks \" $ALERT_EMAIL_ADDR fi Custom DH Parameters By default docker-mailserver uses ffdhe4096 from IETF RFC 7919 . These are standardized pre-defined DH groups and the only available DH groups for TLS 1.3. It is discouraged to generate your own DH parameters as it is often less secure. Despite this, if you must use non-standard DH parameters or you would like to swap ffdhe4096 for a different group (eg ffdhe2048 ); Add your own PEM encoded DH params file via a volume to /tmp/docker-mailserver/dhparams.pem . This will replace DH params for both Dovecot and Postfix services during container startup.","title":"SSL/TLS"},{"location":"config/security/ssl/#the-fqdn","text":"An FQDN ( Fully Qualified Domain Name ) such as mail.example.com is required for docker-mailserver to function correctly, especially for looking up the correct SSL certificate to use. Internally, hostname -f will be used to retrieve the FQDN as configured in the below examples. Wildcard certificates (eg: *.example.com ) are supported for SSL_TYPE=letsencrypt . Your configured FQDN below may be mail.example.com , and your wildcard certificate provisioned to /etc/letsencrypt/live/example.com which will be checked as a fallback FQDN by docker-mailserver . Docker CLI options --hostname and optionally --domainname docker run --hostname mail --domainname example.com # `--domainname` is not required: docker run --hostname mail.example.com docker-compose.yml config services : mailserver : hostname : mail domainname : example.com # `domainname` is not required: services : mailserver : hostname : mail.example.com Bare domains (eg: example.com ) should only use the hostname option docker run --hostname example.com services : mailserver : hostname : example.com","title":"The FQDN"},{"location":"config/security/ssl/#provisioning-methods","text":"","title":"Provisioning methods"},{"location":"config/security/ssl/#lets-encrypt-recommended","text":"To enable Let's Encrypt for docker-mailserver , you have to: Get your certificate using the Let's Encrypt client Certbot . For your docker-mailserver container: Add the environment variable SSL_TYPE=letsencrypt . Mount your local letsencrypt folder as a volume to /etc/letsencrypt . You don't have to do anything else. Enjoy! Note /etc/letsencrypt/live stores provisioned certificates in individual folders named by their FQDN. Make sure that the entire folder is mounted to docker-mailserver as there are typically symlinks from /etc/letsencrypt/live/mail.example.com to /etc/letsencrypt/archive . Example Add these additions to the mailserver service in your docker-compose.yml : services : mailserver : # For the FQDN 'mail.example.com': hostname : mail domainname : example.com environment : - SSL_TYPE=letsencrypt volumes : - /etc/letsencrypt:/etc/letsencrypt","title":"Let's Encrypt (Recommended)"},{"location":"config/security/ssl/#example-using-docker-for-lets-encrypt","text":"Certbot provisions certificates to /etc/letsencrypt . Add a volume to store these, so that they can later be accessed by docker-mailserver container. You may also want to persist Certbot logs , just in case you need to troubleshoot. Getting a certificate is this simple! ( Referencing: Certbot docker instructions and certonly --standalone mode ): # Change `mail.example.com` below to your own FQDN. # Requires access to port 80 from the internet, adjust your firewall if needed. docker run --rm -it \\ -v \" ${ PWD } /docker-data/certbot/certs/:/etc/letsencrypt/\" \\ -v \" ${ PWD } /docker-data/certbot/logs/:/var/log/letsencrypt/\" \\ -p 80 :80 \\ certbot/certbot certonly --standalone -d mail.example.com Add a volume for docker-mailserver that maps the local certbot/certs/ folder to the container path /etc/letsencrypt/ . Example Add these additions to the mailserver service in your docker-compose.yml : services : mailserver : # For the FQDN 'mail.example.com': hostname : mail domainname : example.com environment : - SSL_TYPE=letsencrypt volumes : - ./docker-data/certbot/certs/:/etc/letsencrypt The certificate setup is complete, but remember it will expire . Consider automating renewals. Renewing Certificates When running the above certonly --standalone snippet again, the existing certificate is renewed if it would expire within 30 days. Alternatively, Certbot can look at all the certificates it manages, and only renew those nearing their expiry via the renew command : # This will need access to port 443 from the internet, adjust your firewall if needed. docker run --rm -it \\ -v \" ${ PWD } /docker-data/certbot/certs/:/etc/letsencrypt/\" \\ -v \" ${ PWD } /docker-data/certbot/logs/:/var/log/letsencrypt/\" \\ -p 80 :80 \\ -p 443 :443 \\ certbot/certbot renew This process can also be automated via cron or systemd timers . Using a different ACME CA Certbot does support alternative certificate providers via the --server option. In most cases you'll want to use the default Let's Encrypt .","title":"Certbot with Docker"},{"location":"config/security/ssl/#example-using-certbot-dns-cloudflare-with-docker","text":"If you are unable get a certificate via the HTTP-01 (port 80) or TLS-ALPN-01 (port 443) challenge types , the DNS-01 challenge can be useful ( this challenge can additionally issue wildcard certificates ). This guide shows how to use the DNS-01 challenge with Cloudflare as your DNS provider. Obtain a Cloudflare API token: Login into your Cloudflare dashboard. Navigate to the API Tokens page . Click \"Create Token\", and choose the Edit zone DNS template ( Certbot requires the ZONE:DNS:Edit permission ). Only include the necessary Zone resource configuration Be sure to configure \"Zone Resources\" section on this page to Include -> Specific zone -> . This restricts the API token to only this zone (domain) which is an important security measure. Store the API token you received in a file cloudflare.ini with content: dns_cloudflare_api_token = YOUR_CLOUDFLARE_TOKEN_HERE As this is sensitive data, you should restrict access to it with chmod 600 and chown 0:0 . Store the file in a folder if you like, such as docker-data/certbot/secrets/ . Your docker-compose.yml should include the following: services : mailserver : environments : # Set SSL certificate type. - SSL_TYPE=letsencrypt volumes : # Mount the cert folder generated by Certbot into mail-server: - ./docker-data/certbot/certs/:/etc/letsencrypt/:ro certbot-cloudflare : image : certbot/dns-cloudflare:latest command : certonly --dns-cloudflare --dns-cloudflare-credentials /run/secrets/cloudflare-api-token -d mail.example.com volumes : - ./docker-data/certbot/certs/:/etc/letsencrypt/ - ./docker-data/certbot/logs/:/var/log/letsencrypt/ secrets : - cloudflare-api-token # Docs: https://docs.docker.com/engine/swarm/secrets/#use-secrets-in-compose # WARNING: In compose configs without swarm, the long syntax options have no effect, # Ensure that you properly `chmod 600` and `chown 0:0` the file on disk. Effectively treated as a bind mount. secrets : cloudflare-api-token : file : ./docker-data/certbot/secrets/cloudflare.ini Alternative using the docker run command ( secrets feature is not available): docker run \\ --volume \" ${ PWD } /docker-data/certbot/certs/:/etc/letsencrypt/\" \\ --volume \" ${ PWD } /docker-data/certbot/logs/:/var/log/letsencrypt/\" \\ --volume \" ${ PWD } /docker-data/certbot/secrets/:/tmp/secrets/certbot/\" certbot/dns-cloudflare \\ certonly --dns-cloudflare --dns-cloudflare-credentials /tmp/secrets/certbot/cloudflare.ini -d mail.example.com Run the service to provision a certificate: docker-compose run certbot-cloudflare You should see the following log output: Saving debug log to /var/log/letsencrypt/letsencrypt. log | Requesting a certificate for mail.example.com Waiting 10 seconds for DNS changes to propagate Successfully received certificate. Certificate is saved at: /etc/letsencrypt/live/mail.example.com/fullchain.pem Key is saved at: /etc/letsencrypt/live/mail.example.com/privkey.pem This certificate expires on YYYY-MM-DD. These files will be updated when the certificate renews. NEXT STEPS: - The certificate will need to be renewed before it expires. Certbot can automatically renew the certificate in background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal structions. After completing the steps above, your certificate should be ready to use. Renewing a certificate (Optional) We've only demonstrated how to provision a certificate, but it will expire in 90 days and need to be renewed before then. In the following example, add a new service ( certbot-cloudflare-renew ) into docker-compose.yml that will handle certificate renewals: services : certbot-cloudflare-renew : image : certbot/dns-cloudflare:latest command : renew --dns-cloudflare --dns-cloudflare-credentials /run/secrets/cloudflare-api-token volumes : - ./docker-data/certbot/certs/:/etc/letsencrtypt/ - ./docker-data/certbot/logs/:/var/log/letsencrypt/ secrets : - cloudflare-api-token You can manually run this service to renew the cert within 90 days: docker-compose run certbot-cloudflare-renew You should see the following output (The following log was generated with --dry-run options) Saving debug log to /var/log/letsencrypt/letsencrypt.log - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Processing /etc/letsencrypt/renewal/mail.example.com.conf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Account registered. Simulating renewal of an existing certificate for mail.example.com Waiting 10 seconds for DNS changes to propagate - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/mail.example.com/fullchain.pem (success) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - It is recommended to automate this renewal via a task scheduler like a systemd timer or in crontab ( crontab example: Checks every day if the certificate should be renewed) 0 0 * * * docker-compose -f PATH_TO_YOUR_DOCKER_COMPOSE_YML up certbot-cloudflare-renew","title":"certbot-dns-cloudflare with Docker"},{"location":"config/security/ssl/#example-using-nginx-proxy-and-acme-companion-with-docker","text":"If you are running a web server already, port 80 will be in use which Certbot requires. You could use the Certbot --webroot feature, but it is more common to leverage a reverse proxy that manages the provisioning and renewal of certificates for your services automatically. In the following example, we show how docker-mailserver can be run alongside the docker containers nginx-proxy and acme-companion ( Referencing: acme-companion documentation ): Start the reverse proxy ( nginx-proxy ): docker run --detach \\ --name nginx-proxy \\ --restart always \\ --publish 80 :80 \\ --publish 443 :443 \\ --volume \" ${ PWD } /docker-data/nginx-proxy/html/:/usr/share/nginx/html/\" \\ --volume \" ${ PWD } /docker-data/nginx-proxy/vhost.d/:/etc/nginx/vhost.d/\" \\ --volume \" ${ PWD } /docker-data/acme-companion/certs/:/etc/nginx/certs/:ro\" \\ --volume '/var/run/docker.sock:/tmp/docker.sock:ro' \\ nginxproxy/nginx-proxy Then start the certificate provisioner ( acme-companion ), which will provide certificates to nginx-proxy : # Inherit `nginx-proxy` volumes via `--volumes-from`, but make `certs/` writeable: docker run --detach \\ --name nginx-proxy-acme \\ --restart always \\ --volumes-from nginx-proxy \\ --volume \" ${ PWD } /docker-data/acme-companion/certs/:/etc/nginx/certs/:rw\" \\ --volume \" ${ PWD } /docker-data/acme-companion/acme-state/:/etc/acme.sh/\" \\ --volume '/var/run/docker.sock:/var/run/docker.sock:ro' \\ --env 'DEFAULT_EMAIL=admin@example.com' \\ nginxproxy/acme-companion Start the rest of your web server containers as usual. Start a dummy container to provision certificates for your FQDN (eg: mail.example.com ). acme-companion will detect the container and generate a Let's Encrypt certificate for your domain, which can be used by docker-mailserver : docker run --detach \\ --name webmail \\ --env 'VIRTUAL_HOST=mail.example.com' \\ --env 'LETSENCRYPT_HOST=mail.example.com' \\ --env 'LETSENCRYPT_EMAIL=admin@example.com' \\ nginx You may want to add --env LETSENCRYPT_TEST=true to the above while testing, to avoid the Let's Encrypt certificate generation rate limits. Make sure your mount path to the letsencrypt certificates directory is correct. Edit your docker-compose.yml for the mailserver service to have volumes added like below: volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - ./docker-data/acme-companion/certs/:/etc/letsencrypt/live/:ro Then from the docker-compose.yml project directory, run: docker-compose up -d mailserver .","title":"nginx-proxy with Docker"},{"location":"config/security/ssl/#example-using-nginx-proxy-and-acme-companion-with-docker-compose","text":"The following example is the basic setup you need for using nginx-proxy and acme-companion with docker-mailserver ( Referencing: acme-companion documentation ): Example: docker-compose.yml You should have an existing docker-compose.yml with a mailserver service. Below are the modifications to add for integrating with nginx-proxy and acme-companion services: version : '3.8' services : # Add the following `environment` and `volumes` to your existing `mailserver` service: mailserver : environment : # SSL_TYPE: Uses the `letsencrypt` method to find mounted certificates. # VIRTUAL_HOST: The FQDN that `nginx-proxy` will configure itself to handle for HTTP[S] connections. # LETSENCRYPT_HOST: The FQDN for a certificate that `acme-companion` will provision and renew. - SSL_TYPE=letsencrypt - VIRTUAL_HOST=mail.example.com - LETSENCRYPT_HOST=mail.example.com volumes : - ./docker-data/acme-companion/certs/:/etc/letsencrypt/live/:ro # If you don't yet have your own `nginx-proxy` and `acme-companion` setup, # here is an example you can use: reverse-proxy : image : nginxproxy/nginx-proxy container_name : nginx-proxy restart : always ports : # Port 80: Required for HTTP-01 challenges to `acme-companion`. # Port 443: Only required for containers that need access over HTTPS. TLS-ALPN-01 challenge not supported. - \"80:80\" - \"443:443\" volumes : # `certs/`: Managed by the `acme-companion` container (_read-only_). # `docker.sock`: Required to interact with containers via the Docker API. # `dhparam`: A named data volume to prevent `nginx-proxy` creating an anonymous volume each time. - ./docker-data/nginx-proxy/html/:/usr/share/nginx/html/ - ./docker-data/nginx-proxy/vhost.d/:/etc/nginx/vhost.d/ - ./docker-data/acme-companion/certs/:/etc/nginx/certs/:ro - /var/run/docker.sock:/tmp/docker.sock:ro - dhparam:/etc/nginx/dhparam acme-companion : image : nginxproxy/acme-companion container_name : nginx-proxy-acme restart : always environment : # Only docker-compose v2 supports: `volumes_from: [nginx-proxy]`, # reference the _reverse-proxy_ `container_name` here: - NGINX_PROXY_CONTAINER=nginx-proxy volumes : # `html/`: Write ACME HTTP-01 challenge files that `nginx-proxy` will serve. # `vhost.d/`: To enable web access via `nginx-proxy` to HTTP-01 challenge files. # `certs/`: To store certificates and private keys. # `acme-state/`: To persist config and state for the ACME provisioner (`acme.sh`). # `docker.sock`: Required to interact with containers via the Docker API. - ./docker-data/nginx-proxy/html/:/usr/share/nginx/html/ - ./docker-data/nginx-proxy/vhost.d/:/etc/nginx/vhost.d/ - ./docker-data/acme-companion/certs/:/etc/nginx/certs/:rw - ./docker-data/acme-companion/acme-state/:/etc/acme.sh/ - /var/run/docker.sock:/var/run/docker.sock:ro # Once `nginx-proxy` fixes their Dockerfile, this named data volume can be removed from docs. # Users can opt for a local bind mount volume like all others if they prefer, but this volume # is only intended to be temporary. volumes : dhparam : Optional ENV vars worth knowing about Per container ENV that acme-companion will detect to override default provisioning settings: LETSENCRYPT_TEST=true : Recommended during initial setup . Otherwise the default production endpoint has a rate limit of 5 duplicate certificates per week . Overrides ACME_CA_URI to use the Let's Encrypt staging endpoint. LETSENCRYPT_EMAIL : For when you don't use DEFAULT_EMAIL on acme-companion , or want to assign a different email contact for this container. LETSENCRYPT_KEYSIZE : Allows you to configure the type (RSA or ECDSA) and size of the private key for your certificate. Default is RSA 4096. LETSENCRYPT_RESTART_CONTAINER=true : When the certificate is renewed, the entire container will be restarted to ensure the new certificate is used. acme-companion ENV for default settings that apply to all containers using LETSENCRYPT_HOST : DEFAULT_EMAIL : An email address that the CA ( eg: Let's Encrypt ) can contact you about expiring certificates, failed renewals, or for account recovery. You may want to use an email address not handled by your mail-server to ensure deliverability in the event your mail-server breaks. CERTS_UPDATE_INTERVAL : If you need to adjust the frequency to check for renewals. 3600 seconds (1 hour) by default. DEBUG=1 : Should be helpful when troubleshooting provisioning issues from acme-companion logs. ACME_CA_URI : Useful in combination with CA_BUNDLE to use a private CA. To change the default Let's Encrypt endpoint to the staging endpoint, use https://acme-staging-v02.api.letsencrypt.org/directory . CA_BUNDLE : If you want to use a private CA instead of Let's Encrypt . Alternative to required ENV on mailserver service While you will still need both nginx-proxy and acme-companion containers, you can manage certificates without adding ENV vars to containers. Instead the ENV is moved into a file and uses the acme-companion feature Standalone certificates . This requires adding another shared volume between nginx-proxy and acme-companion : services : reverse-proxy : volumes : - ./docker-data/nginx-proxy/conf.d/:/etc/nginx/conf.d/ acme-companion : volumes : - ./docker-data/nginx-proxy/conf.d/:/etc/nginx/conf.d/ - ./docker-data/acme-companion/standalone.sh:/app/letsencrypt_user_data:ro acme-companion mounts a shell script ( standalone.sh ), which defines variables to customize certificate provisioning: # A list IDs for certificates to provision: LETSENCRYPT_STANDALONE_CERTS =( 'mail' ) # Each ID inserts itself into the standard `acme-companion` supported container ENV vars below. # The LETSENCRYPT__HOST var is a list of FQDNs to provision a certificate for as the SAN field: LETSENCRYPT_mail_HOST =( 'mail.example.com' ) # Optional variables: LETSENCRYPT_mail_TEST = true LETSENCRYPT_mail_EMAIL = 'admin@example.com' # RSA-4096 => `4096`, ECDSA-256 => `ec-256`: LETSENCRYPT_mail_KEYSIZE = 4096 Unlike with the equivalent ENV for containers, changes to this file will not be detected automatically . You would need to wait until the next renewal check by acme-companion ( every hour by default ), restart acme-companion , or manually invoke the service loop : docker exec nginx-proxy-acme /app/signal_le_service","title":"nginx-proxy with docker-compose"},{"location":"config/security/ssl/#example-using-lets-encrypt-certificates-with-a-synology-nas","text":"Version 6.2 and later of the Synology NAS DSM OS now come with an interface to generate and renew letencrypt certificates. Navigation into your DSM control panel and go to Security, then click on the tab Certificate to generate and manage letsencrypt certificates. Amongst other things, you can use these to secure your mail-server. DSM locates the generated certificates in a folder below /usr/syno/etc/certificate/_archive/ . Navigate to that folder and note the 6 character random folder name of the certificate you'd like to use. Then, add the following to your docker-compose.yml declaration file: # Note: If you have an existing setup that was working pre docker-mailserver v10.2, # '/tmp/dms/custom-certs' below has replaced the previous '/tmp/ssl' container path. volumes : - /usr/syno/etc/certificate/_archive//:/tmp/dms/custom-certs/ environment : - SSL_TYPE=manual - SSL_CERT_PATH=/tmp/dms/custom-certs/fullchain.pem - SSL_KEY_PATH=/tmp/dms/custom-certs/privkey.pem DSM-generated letsencrypt certificates get auto-renewed every three months.","title":"Synology NAS"},{"location":"config/security/ssl/#caddy","text":"If you are using Caddy to renew your certificates, please note that only RSA certificates work. Read #1440 for details. In short for Caddy v1 the Caddyfile should look something like: https://mail.example.com { tls admin@example.com { key_type rsa2048 } } For Caddy v2 you can specify the key_type in your server's global settings, which would end up looking something like this if you're using a Caddyfile : { debug admin localhost:2019 http_port 80 https_port 443 default_sni example.com key_type rsa4096 } If you are instead using a json config for Caddy v2, you can set it in your site's TLS automation policies: Example Code { \"apps\" : { \"http\" : { \"servers\" : { \"srv0\" : { \"listen\" : [ \":443\" ], \"routes\" : [ { \"match\" : [ { \"host\" : [ \"mail.example.com\" , ] } ], \"handle\" : [ { \"handler\" : \"subroute\" , \"routes\" : [ { \"handle\" : [ { \"body\" : \"\" , \"handler\" : \"static_response\" } ] } ] } ], \"terminal\" : true }, ] } } }, \"tls\" : { \"automation\" : { \"policies\" : [ { \"subjects\" : [ \"mail.example.com\" , ], \"key_type\" : \"rsa2048\" , \"issuer\" : { \"email\" : \"admin@example.com\" , \"module\" : \"acme\" } }, { \"issuer\" : { \"email\" : \"admin@example.com\" , \"module\" : \"acme\" } } ] } } } } The generated certificates can be mounted: volumes : - ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.crt:/etc/letsencrypt/live/mail.example.com/fullchain.pem - ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.key:/etc/letsencrypt/live/mail.example.com/privkey.pem EC certificates fail in the TLS handshake: CONNECTED(00000003) 140342221178112:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:ssl/record/rec_layer_s3.c:1543:SSL alert number 40 no peer certificate available No client certificate CA names sent","title":"Caddy"},{"location":"config/security/ssl/#traefik-v2","text":"Traefik is an open-source application proxy using the ACME protocol . Traefik can request certificates for domains and subdomains, and it will take care of renewals, challenge negotiations, etc. We strongly recommend to use Traefik 's major version 2. Traefik 's storage format is natively supported if the acme.json store is mounted into the container at /etc/letsencrypt/acme.json . The file is also monitored for changes and will trigger a reload of the mail services (Postfix and Dovecot). Wildcard certificates are supported. If your FQDN is mail.example.com and your wildcard certificate is *.example.com , add the ENV: SSL_DOMAIN = example.com . The mail-server will select it's certificate from acme.json checking these ENV for a matching FQDN ( in order of priority ): ${ SSL_DOMAIN } ${ HOSTNAME } ${ DOMAINNAME } This setup only comes with one caveat: The domain has to be configured on another service for Traefik to actually request it from Let's Encrypt , i.e. Traefik will not issue a certificate without a service / router demanding it. Example Code Here is an example setup for docker-compose : version : '3.8' services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail domainname : example.com volumes : - ./docker-data/traefik/acme.json:/etc/letsencrypt/acme.json:ro environment : SSL_TYPE : letsencrypt SSL_DOMAIN : mail.example.com # for a wildcard certificate, use # SSL_DOMAIN: example.com reverse-proxy : image : docker.io/traefik:latest #v2.5 container_name : docker-traefik ports : - \"80:80\" - \"443:443\" command : - --providers.docker - --entrypoints.http.address=:80 - --entrypoints.http.http.redirections.entryPoint.to=https - --entrypoints.http.http.redirections.entryPoint.scheme=https - --entrypoints.https.address=:443 - --entrypoints.https.http.tls.certResolver=letsencrypt - --certificatesresolvers.letsencrypt.acme.email=admin@example.com - --certificatesresolvers.letsencrypt.acme.storage=/acme.json - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=http volumes : - ./docker-data/traefik/acme.json:/acme.json - /var/run/docker.sock:/var/run/docker.sock:ro whoami : image : docker.io/traefik/whoami:latest labels : - \"traefik.http.routers.whoami.rule=Host(`mail.example.com`)\"","title":"Traefik v2"},{"location":"config/security/ssl/#self-signed-certificates","text":"Warning Use self-signed certificates only for testing purposes! This feature requires you to provide the following files into your docker-data/dms/config/ssl/ directory ( internal location: /tmp/docker-mailserver/ssl/ ): -key.pem -cert.pem demoCA/cacert.pem Where is the FQDN you've configured for your docker-mailserver container. Add SSL_TYPE=self-signed to your docker-mailserver environment variables. Postfix and Dovecot will be configured to use the provided certificate ( .pem files above ) during container startup.","title":"Self-Signed Certificates"},{"location":"config/security/ssl/#generating-a-self-signed-certificate","text":"Note Since docker-mailserver v10, support in setup.sh for generating a self-signed SSL certificate internally was removed. One way to generate self-signed certificates is with Smallstep's step CLI . This is exactly what docker-mailserver does for creating test certificates . For example with the FQDN mail.example.test , you can generate the required files by running: #! /bin/sh mkdir -p demoCA step certificate create \"Smallstep Root CA\" \"demoCA/cacert.pem\" \"demoCA/cakey.pem\" \\ --no-password --insecure \\ --profile root-ca \\ --not-before \"2021-01-01T00:00:00+00:00\" \\ --not-after \"2031-01-01T00:00:00+00:00\" \\ --san \"example.test\" \\ --san \"mail.example.test\" \\ --kty RSA --size 2048 step certificate create \"Smallstep Leaf\" mail.example.test-cert.pem mail.example.test-key.pem \\ --no-password --insecure \\ --profile leaf \\ --ca \"demoCA/cacert.pem\" \\ --ca-key \"demoCA/cakey.pem\" \\ --not-before \"2021-01-01T00:00:00+00:00\" \\ --not-after \"2031-01-01T00:00:00+00:00\" \\ --san \"example.test\" \\ --san \"mail.example.test\" \\ --kty RSA --size 2048 If you'd rather not install the CLI tool locally to run the step commands above; you can save the script above to a file such as generate-certs.sh ( and make it executable chmod +x generate-certs.sh ) in a directory that you want the certs to be placed (eg: docker-data/dms/custom-certs/ ), then use docker to run that script in a container: # '--user' is to keep ownership of the files written to # the local volume to use your systems User and Group ID values. docker run --rm -it \\ --user \" $( id -u ) : $( id -g ) \" \\ --volume \" ${ PWD } /docker-data/dms/custom-certs/:/tmp/step-ca/\" \\ --workdir \"/tmp/step-ca/\" \\ --entrypoint \"/tmp/step-ca/generate-certs.sh\" \\ smallstep/step-ca","title":"Generating a self-signed certificate"},{"location":"config/security/ssl/#bring-your-own-certificates","text":"You can also provide your own certificate files. Add these entries to your docker-compose.yml : volumes : - ./docker-data/dms/custom-certs/:/tmp/dms/custom-certs/:ro environment : - SSL_TYPE=manual # Values should match the file paths inside the container: - SSL_CERT_PATH=/tmp/dms/custom-certs/public.crt - SSL_KEY_PATH=/tmp/dms/custom-certs/private.key This will mount the path where your certificate files reside locally into the read-only container folder: /tmp/dms/custom-certs . The local and internal paths may be whatever you prefer, so long as both SSL_CERT_PATH and SSL_KEY_PATH point to the correct internal file paths. The certificate files may also be named to your preference, but should be PEM encoded. SSL_ALT_CERT_PATH and SSL_ALT_KEY_PATH are additional ENV vars to support a 2nd certificate as a fallback. Commonly known as hybrid or dual certificate support. This is useful for using a modern ECDSA as your primary certificate, and RSA as your fallback for older connections. They work in the same manner as the non- ALT versions. Info You may have to restart docker-mailserver once the certificates change.","title":"Bring Your Own Certificates"},{"location":"config/security/ssl/#testing-a-certificate-is-valid","text":"From your host: docker exec mailserver openssl s_client \\ -connect 0 .0.0.0:25 \\ -starttls smtp \\ -CApath /etc/ssl/certs/ Or: docker exec mailserver openssl s_client \\ -connect 0 .0.0.0:143 \\ -starttls imap \\ -CApath /etc/ssl/certs/ And you should see the certificate chain, the server certificate and: Verify return code: 0 (ok) In addition, to verify certificate dates: docker exec mailserver openssl s_client \\ -connect 0 .0.0.0:25 \\ -starttls smtp \\ -CApath /etc/ssl/certs/ \\ 2 >/dev/null | openssl x509 -noout -dates","title":"Testing a Certificate is Valid"},{"location":"config/security/ssl/#plain-text-access","text":"Warning Not recommended for purposes other than testing. Add this to docker-data/dms/config/dovecot.cf : ssl = yes disable_plaintext_auth = no These options in conjunction mean: SSL/TLS is offered to the client, but the client isn't required to use it. The client is allowed to login with plaintext authentication even when SSL/TLS isn't enabled on the connection. This is insecure , because the plaintext password is exposed to the internet.","title":"Plain-Text Access"},{"location":"config/security/ssl/#importing-certificates-obtained-via-another-source","text":"If you have another source for SSL/TLS certificates you can import them into the server via an external script. The external script can be found here: external certificate import script . Only compatible with docker-mailserver releases < v10.2 The script expects /etc/postfix/ssl/cert and /etc/postfix/ssl/key files to be configured paths for both Postfix and Dovecot to use. Since the docker-mailserver 10.2 release, certificate files have moved to /etc/dms/tls/ , and the file name may differ depending on provisioning method. This third-party script also has fullchain.pem and privkey.pem as hard-coded, thus is incompatible with other filenames. Additionally it has never supported handling ALT fallback certificates (for supporting dual/hybrid, RSA + ECDSA). The steps to follow are these: Transfer the new certificates to ./docker-data/dms/custom-certs/ (volume mounted to: /tmp/ssl/ ) You should provide fullchain.key and privkey.pem Place the script in ./docker-data/dms/config/ (volume mounted to: /tmp/docker-mailserver/ ) Make the script executable ( chmod +x tomav-renew-certs.sh ) Run the script: docker exec mailserver /tmp/docker-mailserver/tomav-renew-certs.sh If an error occurs the script will inform you. If not you will see both postfix and dovecot restart. After the certificates have been loaded you can check the certificate: openssl s_client \\ -servername mail.example.com \\ -connect 192 .168.0.72:465 \\ 2 >/dev/null | openssl x509 # or openssl s_client \\ -servername mail.example.com \\ -connect mail.example.com:465 \\ 2 >/dev/null | openssl x509 Or you can check how long the new certificate is valid with commands like: export SITE_URL = \"mail.example.com\" export SITE_IP_URL = \"192.168.0.72\" # can also use `mail.example.com` export SITE_SSL_PORT = \"993\" # imap port dovecot ##works: check if certificate will expire in two weeks #2 weeks is 1209600 seconds #3 weeks is 1814400 #12 weeks is 7257600 #15 weeks is 9072000 certcheck_2weeks = ` openssl s_client -connect ${ SITE_IP_URL } : ${ SITE_SSL_PORT } \\ -servername ${ SITE_URL } 2 > /dev/null | openssl x509 -noout -checkend 1209600 ` #################################### #notes: output could be either: #Certificate will not expire #Certificate will expire #################### What does the script that imports the certificates do: Check if there are new certs in the internal container folder: /tmp/ssl . Check with the ssl cert fingerprint if they differ from the current certificates. If so it will copy the certs to the right places. And restart postfix and dovecot. You can of course run the script by cron once a week or something. In that way you could automate cert renewal. If you do so it is probably wise to run an automated check on certificate expiry as well. Such a check could look something like this: # This script is run inside docker-mailserver via 'docker exec ...', using the 'mail' command to send alerts. ## code below will alert if certificate expires in less than two weeks ## please adjust varables! ## make sure the 'mail -s' command works! Test! export SITE_URL = \"mail.example.com\" export SITE_IP_URL = \"192.168.2.72\" # can also use `mail.example.com` export SITE_SSL_PORT = \"993\" # imap port dovecot # Below can be from a different domain; like your personal email, not handled by this docker-mailserver: export ALERT_EMAIL_ADDR = \"external-account@gmail.com\" certcheck_2weeks = ` openssl s_client -connect ${ SITE_IP_URL } : ${ SITE_SSL_PORT } \\ -servername ${ SITE_URL } 2 > /dev/null | openssl x509 -noout -checkend 1209600 ` #################################### #notes: output can be #Certificate will not expire #Certificate will expire #################### #echo \"certcheck 2 weeks gives $certcheck_2weeks\" ##automated check you might run by cron or something ## does the certificate expire within two weeks? if [ \" $certcheck_2weeks \" = \"Certificate will not expire\" ] ; then echo \"all is well, certwatch 2 weeks says $certcheck_2weeks \" else echo \"Cert seems to be expiring pretty soon, within two weeks: $certcheck_2weeks \" echo \"we will send an alert email and log as well\" logger Certwatch: cert $SITE_URL will expire in two weeks echo \"Certwatch: cert $SITE_URL will expire in two weeks\" | mail -s \"cert $SITE_URL expires in two weeks \" $ALERT_EMAIL_ADDR fi","title":"Importing Certificates Obtained via Another Source"},{"location":"config/security/ssl/#custom-dh-parameters","text":"By default docker-mailserver uses ffdhe4096 from IETF RFC 7919 . These are standardized pre-defined DH groups and the only available DH groups for TLS 1.3. It is discouraged to generate your own DH parameters as it is often less secure. Despite this, if you must use non-standard DH parameters or you would like to swap ffdhe4096 for a different group (eg ffdhe2048 ); Add your own PEM encoded DH params file via a volume to /tmp/docker-mailserver/dhparams.pem . This will replace DH params for both Dovecot and Postfix services during container startup.","title":"Custom DH Parameters"},{"location":"config/security/understanding-the-ports/","text":"Quick Reference Prefer Implicit TLS ports, they're more secure and if you use a Reverse Proxy, should be less hassle (although it's probably wiser to expose these ports directly to docker-mailserver ). Overview of Email Ports Protocol Explicit TLS 1 Implicit TLS Purpose SMTP 25 N/A Transfer 2 ESMTP 587 465 3 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, docker-mailserver additionally filters for spam and viruses. For submitting email to the server to be sent to third-parties, you should prefer the submission ports(465, 587) - which require authentication. Unless a relay host is configured(eg SendGrid), 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 ). Previously a secure variant of port 25. What Ports Should I Use? (SMTP) flowchart LR subgraph your-server [\"Your Server\"] in_25(25) --> server in_465(465) --> server server((\"docker-mailserver
hello@world.com\")) server --- out_25(25) server --- out_465(465) end third-party(\"Third-party
(sending you email)\") ---|\"Receive email for
hello@world.com\"| in_25 subgraph clients [\"Clients (MUA)\"] mua-client(Thunderbird,
Webmail,
Mutt,
etc) mua-service(Backend software
on another server) end clients ---|\"Send email as
hello@world.com\"| in_465 out_25(25) -->|\"Direct
Delivery\"| tin_25 out_465(465) --> relay(\"MTA
Relay Server\") --> tin_25(25) subgraph third-party-server[\"Third-party Server\"] third-party-mta(\"MTA
friend@example.com\") tin_25(25) --> third-party-mta end Inbound Traffic (On the left) Port 25: Think of this like a physical mailbox, it is open to receive email from anyone who wants to. docker-mailserver will actively filter email delivered on this port for spam or viruses and refuse mail from known bad sources. While you could also use this port internally to send email outbound without requiring authentication, you really should prefer the Submission ports(587, 465). Port 465( and 587 ): This is the equivalent of a post office box where you would send email to be delivered on your behalf( docker-mailserver is that metaphorical post office, aka the MTA). Unlike port 25, these two ports are known as the Submission ports and require a valid email account on the server with a password to be able to send email to anyone outside of the server(an MTA you do not control, eg Outlook or Gmail). Prefer port 465 which provides Implicit TLS. Outbound Traffic (On the Right) Port 25: Send the email directly to the given email address MTA as possible. Like your own docker-mailserver port 25, this is the standard port for receiving email on, thus email will almost always arrive to the final MTA on this port. Note that, there may be additional MTAs further in the chain, but this would be the public facing one representing that email address. Port 465( and 587 ): SMTP Relays are a popular choice to hand-off delivery of email through. Services like SendGrid are useful for bulk email(marketing) or when your webhost or ISP are preventing you from using standard ports like port 25 to send out email(which can be abused by spammers). docker-mailserver can serve as a relay too, but the difference between a DIY relay and a professional service is reputation, which is referenced by MTAs you're delivering to such as Outlook, Gmail or others(perhaps another docker-mailserver server!), when deciding if email should be marked as junked or potentially not delivered at all. As a service like SendGrid has a reputation to maintain, relay is restricted to registered users who must authenticate(even on port 25), they do not store email, merely forward it to another MTA which could be delivered on a different port like 25. Explicit vs Implicit TLS Explicit TLS (aka Opportunistic TLS) - Opt-in Encryption Communication on these ports begin in cleartext , indicating support for STARTTLS . If both client and server support STARTTLS the connection will be secured over TLS, otherwise the connection is unable to use encryption to secure it. By default, docker-mailserver is configured to reject connections that fail to establish a secure connection when authentication is required, rather than allow an insecure connection ( Port 25 will allow receiving unencrypted deliveries which doesn't require authentication ). Support for STARTTLS is not always implemented correctly, which can lead to leaking credentials(client sending too early) prior to a TLS connection being established. Third-parties such as some ISPs have also been known to intercept the STARTTLS exchange, modifying network traffic to prevent establishing a secure connection. Due to these security concerns, RFC 8314 (Section 4.1) encourages you to prefer Implicit TLS ports where possible . Implicit TLS - Enforced Encryption Communication is always encrypted, avoiding the above mentioned issues with Explicit TLS. You may know of these ports as SMTPS, POP3S, IMAPS , which indicate the protocol in combination with a TLS connection. However, Explicit TLS ports provide the same benefit when STARTTLS is successfully negotiated; Implicit TLS better communicates the improved security to all three protocols (SMTP/POP3/IMAP over Implicit TLS). Additionally, referring to port 465 as SMTPS would be incorrect, as it is a submissions port requiring authentication to proceed via ESMTP , whereas ESMTPS has a different meaning(STARTTLS supported). Port 25 may lack Implicit TLS, but can be configured to be more secure between trusted parties via MTA-STS, STARTTLS Policy List, DNSSEC and DANE. Security Todo This section should provide any related configuration advice, and probably expand on and link to resources about DANE, DNSSEC, MTA-STS and STARTTLS Policy list, with advice on how to configure/setup these added security layers. Todo A related section or page on ciphers used may be useful, although less important for users to be concerned about. TLS connections for a Mail-Server, compared to web browsers Unlike with HTTP where a web browser client communicates directly with the server providing a website, a secure TLS connection as discussed below is not the equivalent safety that HTTPS provides when the transit of email (receiving or sending) is sent through third-parties, as the secure connection is only between two machines, any additional machines (MTAs) between the MUA and the MDA depends on them establishing secure connections between one another successfully. Other machines that facilitate a connection that generally aren't taken into account can exist between a client and server, such as those where your connection passes through your ISP provider are capable of compromising a cleartext connection through interception.","title":"Understanding the Ports"},{"location":"config/security/understanding-the-ports/#quick-reference","text":"Prefer Implicit TLS ports, they're more secure and if you use a Reverse Proxy, should be less hassle (although it's probably wiser to expose these ports directly to docker-mailserver ).","title":"Quick Reference"},{"location":"config/security/understanding-the-ports/#overview-of-email-ports","text":"Protocol Explicit TLS 1 Implicit TLS Purpose SMTP 25 N/A Transfer 2 ESMTP 587 465 3 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, docker-mailserver additionally filters for spam and viruses. For submitting email to the server to be sent to third-parties, you should prefer the submission ports(465, 587) - which require authentication. Unless a relay host is configured(eg SendGrid), 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 ). Previously a secure variant of port 25.","title":"Overview of Email Ports"},{"location":"config/security/understanding-the-ports/#what-ports-should-i-use-smtp","text":"flowchart LR subgraph your-server [\"Your Server\"] in_25(25) --> server in_465(465) --> server server((\"docker-mailserver
hello@world.com\")) server --- out_25(25) server --- out_465(465) end third-party(\"Third-party
(sending you email)\") ---|\"Receive email for
hello@world.com\"| in_25 subgraph clients [\"Clients (MUA)\"] mua-client(Thunderbird,
Webmail,
Mutt,
etc) mua-service(Backend software
on another server) end clients ---|\"Send email as
hello@world.com\"| in_465 out_25(25) -->|\"Direct
Delivery\"| tin_25 out_465(465) --> relay(\"MTA
Relay Server\") --> tin_25(25) subgraph third-party-server[\"Third-party Server\"] third-party-mta(\"MTA
friend@example.com\") tin_25(25) --> third-party-mta end","title":"What Ports Should I Use? (SMTP)"},{"location":"config/security/understanding-the-ports/#inbound-traffic-on-the-left","text":"Port 25: Think of this like a physical mailbox, it is open to receive email from anyone who wants to. docker-mailserver will actively filter email delivered on this port for spam or viruses and refuse mail from known bad sources. While you could also use this port internally to send email outbound without requiring authentication, you really should prefer the Submission ports(587, 465). Port 465( and 587 ): This is the equivalent of a post office box where you would send email to be delivered on your behalf( docker-mailserver is that metaphorical post office, aka the MTA). Unlike port 25, these two ports are known as the Submission ports and require a valid email account on the server with a password to be able to send email to anyone outside of the server(an MTA you do not control, eg Outlook or Gmail). Prefer port 465 which provides Implicit TLS.","title":"Inbound Traffic (On the left)"},{"location":"config/security/understanding-the-ports/#outbound-traffic-on-the-right","text":"Port 25: Send the email directly to the given email address MTA as possible. Like your own docker-mailserver port 25, this is the standard port for receiving email on, thus email will almost always arrive to the final MTA on this port. Note that, there may be additional MTAs further in the chain, but this would be the public facing one representing that email address. Port 465( and 587 ): SMTP Relays are a popular choice to hand-off delivery of email through. Services like SendGrid are useful for bulk email(marketing) or when your webhost or ISP are preventing you from using standard ports like port 25 to send out email(which can be abused by spammers). docker-mailserver can serve as a relay too, but the difference between a DIY relay and a professional service is reputation, which is referenced by MTAs you're delivering to such as Outlook, Gmail or others(perhaps another docker-mailserver server!), when deciding if email should be marked as junked or potentially not delivered at all. As a service like SendGrid has a reputation to maintain, relay is restricted to registered users who must authenticate(even on port 25), they do not store email, merely forward it to another MTA which could be delivered on a different port like 25.","title":"Outbound Traffic (On the Right)"},{"location":"config/security/understanding-the-ports/#explicit-vs-implicit-tls","text":"","title":"Explicit vs Implicit TLS"},{"location":"config/security/understanding-the-ports/#explicit-tls-aka-opportunistic-tls-opt-in-encryption","text":"Communication on these ports begin in cleartext , indicating support for STARTTLS . If both client and server support STARTTLS the connection will be secured over TLS, otherwise the connection is unable to use encryption to secure it. By default, docker-mailserver is configured to reject connections that fail to establish a secure connection when authentication is required, rather than allow an insecure connection ( Port 25 will allow receiving unencrypted deliveries which doesn't require authentication ). Support for STARTTLS is not always implemented correctly, which can lead to leaking credentials(client sending too early) prior to a TLS connection being established. Third-parties such as some ISPs have also been known to intercept the STARTTLS exchange, modifying network traffic to prevent establishing a secure connection. Due to these security concerns, RFC 8314 (Section 4.1) encourages you to prefer Implicit TLS ports where possible .","title":"Explicit TLS (aka Opportunistic TLS) - Opt-in Encryption"},{"location":"config/security/understanding-the-ports/#implicit-tls-enforced-encryption","text":"Communication is always encrypted, avoiding the above mentioned issues with Explicit TLS. You may know of these ports as SMTPS, POP3S, IMAPS , which indicate the protocol in combination with a TLS connection. However, Explicit TLS ports provide the same benefit when STARTTLS is successfully negotiated; Implicit TLS better communicates the improved security to all three protocols (SMTP/POP3/IMAP over Implicit TLS). Additionally, referring to port 465 as SMTPS would be incorrect, as it is a submissions port requiring authentication to proceed via ESMTP , whereas ESMTPS has a different meaning(STARTTLS supported). Port 25 may lack Implicit TLS, but can be configured to be more secure between trusted parties via MTA-STS, STARTTLS Policy List, DNSSEC and DANE.","title":"Implicit TLS - Enforced Encryption"},{"location":"config/security/understanding-the-ports/#security","text":"Todo This section should provide any related configuration advice, and probably expand on and link to resources about DANE, DNSSEC, MTA-STS and STARTTLS Policy list, with advice on how to configure/setup these added security layers. Todo A related section or page on ciphers used may be useful, although less important for users to be concerned about.","title":"Security"},{"location":"config/security/understanding-the-ports/#tls-connections-for-a-mail-server-compared-to-web-browsers","text":"Unlike with HTTP where a web browser client communicates directly with the server providing a website, a secure TLS connection as discussed below is not the equivalent safety that HTTPS provides when the transit of email (receiving or sending) is sent through third-parties, as the secure connection is only between two machines, any additional machines (MTAs) between the MUA and the MDA depends on them establishing secure connections between one another successfully. Other machines that facilitate a connection that generally aren't taken into account can exist between a client and server, such as those where your connection passes through your ISP provider are capable of compromising a cleartext connection through interception.","title":"TLS connections for a Mail-Server, compared to web browsers"},{"location":"config/troubleshooting/debugging/","text":"Contributions Welcome! Please contribute your solutions to help the community Enable Verbose Debugging Output You may find it useful to set LOG_LEVEL environment variable. Invalid Username or Password Shell into the container: docker exec -it bash Check log files in /var/log/mail could not find any mention of incorrect logins here neither in the dovecot logs. Check the supervisors logs in /var/log/supervisor . You can find the logs for startup of fetchmail, postfix and others here - they might indicate problems during startup. Make sure you set your hostname to mail or whatever you specified in your docker-compose.yml file or else your FQDN will be wrong. Installation Errors During setup, if you get errors trying to edit files inside of the container, you likely need to install vi : sudo su docker exec -it apt-get install -y vim Testing Connection I spent HOURS trying to debug \"Connection Refused\" and \"Connection closed by foreign host\" errors when trying to use telnet to troubleshoot my connection. I was also trying to connect from my email client (macOS mail) around the same time. Telnet had also worked earlier, so I was extremely confused as to why it suddenly stopped working. I stumbled upon fail2ban.log in my container. In short, when trying to get my macOS client working, I exceeded the number of failed login attempts and fail2ban put dovecot and postfix in jail! I got around it by whitelisting my ipaddresses (my ec2 instance and my local computer) sudo su docker exec -it mailserver bash cd /var/log cat fail2ban.log | grep dovecot # Whitelist IP addresses: fail2ban-client set dovecot addignoreip # Server fail2ban-client set postfix addignoreip fail2ban-client set dovecot addignoreip # Client fail2ban-client set postfix addignoreip # This will delete the jails entirely - nuclear option fail2ban-client stop dovecot fail2ban-client stop postfix Sent email is never received Some hosting provides have a stealth block on port 25. Make sure to check with your hosting provider that traffic on port 25 is allowed Common hosting providers known to have this issue: Azure AWS EC2","title":"Debugging"},{"location":"config/troubleshooting/debugging/#enable-verbose-debugging-output","text":"You may find it useful to set LOG_LEVEL environment variable.","title":"Enable Verbose Debugging Output"},{"location":"config/troubleshooting/debugging/#invalid-username-or-password","text":"Shell into the container: docker exec -it bash Check log files in /var/log/mail could not find any mention of incorrect logins here neither in the dovecot logs. Check the supervisors logs in /var/log/supervisor . You can find the logs for startup of fetchmail, postfix and others here - they might indicate problems during startup. Make sure you set your hostname to mail or whatever you specified in your docker-compose.yml file or else your FQDN will be wrong.","title":"Invalid Username or Password"},{"location":"config/troubleshooting/debugging/#installation-errors","text":"During setup, if you get errors trying to edit files inside of the container, you likely need to install vi : sudo su docker exec -it apt-get install -y vim","title":"Installation Errors"},{"location":"config/troubleshooting/debugging/#testing-connection","text":"I spent HOURS trying to debug \"Connection Refused\" and \"Connection closed by foreign host\" errors when trying to use telnet to troubleshoot my connection. I was also trying to connect from my email client (macOS mail) around the same time. Telnet had also worked earlier, so I was extremely confused as to why it suddenly stopped working. I stumbled upon fail2ban.log in my container. In short, when trying to get my macOS client working, I exceeded the number of failed login attempts and fail2ban put dovecot and postfix in jail! I got around it by whitelisting my ipaddresses (my ec2 instance and my local computer) sudo su docker exec -it mailserver bash cd /var/log cat fail2ban.log | grep dovecot # Whitelist IP addresses: fail2ban-client set dovecot addignoreip # Server fail2ban-client set postfix addignoreip fail2ban-client set dovecot addignoreip # Client fail2ban-client set postfix addignoreip # This will delete the jails entirely - nuclear option fail2ban-client stop dovecot fail2ban-client stop postfix","title":"Testing Connection"},{"location":"config/troubleshooting/debugging/#sent-email-is-never-received","text":"Some hosting provides have a stealth block on port 25. Make sure to check with your hosting provider that traffic on port 25 is allowed Common hosting providers known to have this issue: Azure AWS EC2","title":"Sent email is never received"},{"location":"config/user-management/accounts/","text":"Adding a New Account Users (email accounts) are managed in /tmp/docker-mailserver/postfix-accounts.cf . The best way to manage accounts is to use the reliable setup.sh script . Or you may directly add the full email address and its encrypted password, separated by a pipe: user1@example.com|{SHA512-CRYPT}$6$2YpW1nYtPBs2yLYS$z.5PGH1OEzsHHNhl3gJrc3D.YMZkvKw/vp.r5WIiwya6z7P/CQ9GDEJDr2G2V0cAfjDFeAQPUoopsuWPXLk3u1 user2@not-example.com|{SHA512-CRYPT}$6$2YpW1nYtPBs2yLYS$z.5PGH1OEzsHHNhl3gJrc3D.YMZkvKw/vp.r5WIiwya6z7P/CQ9GDEJDr2G2V0cAfjDFeAQPUoopsuWPXLk3u1 In the example above, we've added 2 mail accounts for 2 different domains. Consequently, the mail-server will automatically be configured for multi-domains. Therefore, to generate a new mail account data, directly from your docker host, you could for example run the following: docker run --rm \\ -e MAIL_USER = user1@example.com \\ -e MAIL_PASS = mypassword \\ -it mailserver/docker-mailserver:latest \\ /bin/sh -c 'echo \"$MAIL_USER|$(doveadm pw -s SHA512-CRYPT -u $MAIL_USER -p $MAIL_PASS)\"' >> docker-data/dms/config/postfix-accounts.cf You will then be asked for a password, and be given back the data for a new account entry, as text. To actually add this new account, just copy all the output text in docker-data/dms/config/postfix-accounts.cf file of your running container. Note doveadm pw command lets you choose between several encryption schemes for the password. Use doveadm pw -l to get a list of the currently supported encryption schemes. Note Changes to the accounts list require a restart of the container, using supervisord . See #552 . Notes imap-quota is enabled and allow clients to query their mailbox usage. When the mailbox is deleted, the quota directive is deleted as well. Dovecot quotas support LDAP, but it's not implemented ( PRs are welcome! ).","title":"Accounts"},{"location":"config/user-management/accounts/#adding-a-new-account","text":"Users (email accounts) are managed in /tmp/docker-mailserver/postfix-accounts.cf . The best way to manage accounts is to use the reliable setup.sh script . Or you may directly add the full email address and its encrypted password, separated by a pipe: user1@example.com|{SHA512-CRYPT}$6$2YpW1nYtPBs2yLYS$z.5PGH1OEzsHHNhl3gJrc3D.YMZkvKw/vp.r5WIiwya6z7P/CQ9GDEJDr2G2V0cAfjDFeAQPUoopsuWPXLk3u1 user2@not-example.com|{SHA512-CRYPT}$6$2YpW1nYtPBs2yLYS$z.5PGH1OEzsHHNhl3gJrc3D.YMZkvKw/vp.r5WIiwya6z7P/CQ9GDEJDr2G2V0cAfjDFeAQPUoopsuWPXLk3u1 In the example above, we've added 2 mail accounts for 2 different domains. Consequently, the mail-server will automatically be configured for multi-domains. Therefore, to generate a new mail account data, directly from your docker host, you could for example run the following: docker run --rm \\ -e MAIL_USER = user1@example.com \\ -e MAIL_PASS = mypassword \\ -it mailserver/docker-mailserver:latest \\ /bin/sh -c 'echo \"$MAIL_USER|$(doveadm pw -s SHA512-CRYPT -u $MAIL_USER -p $MAIL_PASS)\"' >> docker-data/dms/config/postfix-accounts.cf You will then be asked for a password, and be given back the data for a new account entry, as text. To actually add this new account, just copy all the output text in docker-data/dms/config/postfix-accounts.cf file of your running container. Note doveadm pw command lets you choose between several encryption schemes for the password. Use doveadm pw -l to get a list of the currently supported encryption schemes. Note Changes to the accounts list require a restart of the container, using supervisord . See #552 .","title":"Adding a New Account"},{"location":"config/user-management/accounts/#notes","text":"imap-quota is enabled and allow clients to query their mailbox usage. When the mailbox is deleted, the quota directive is deleted as well. Dovecot quotas support LDAP, but it's not implemented ( PRs are welcome! ).","title":"Notes"},{"location":"config/user-management/aliases/","text":"Please read the Postfix documentation on virtual aliases first. You can use setup.sh instead of creating and editing files manually. Aliases are managed in /tmp/docker-mailserver/postfix-virtual.cf . An alias is a full email address that will either be: delivered to an existing account registered in /tmp/docker-mailserver/postfix-accounts.cf redirected to one or more other email addresses Alias and target are space separated. An example on a server with example.com as its domain: # Alias delivered to an existing account alias1@example.com user1@example.com # Alias forwarded to an external email address alias2@example.com external-account@gmail.com Configuring RegExp Aliases Additional regexp aliases can be configured by placing them into docker-data/dms/config/postfix-regexp.cf . The regexp aliases get evaluated after the virtual aliases (container path: /tmp/docker-mailserver/postfix-virtual.cf ). For example, the following docker-data/dms/config/postfix-regexp.cf causes all email sent to \"test\" users to be delivered to qa@example.com instead: /^test[0-9][0-9]*@example.com/ qa@example.com Address Tags (Extension Delimiters) an Alternative to Aliases Postfix supports so-called address tags, in the form of plus (+) tags - i.e. address+tag@example.com will end up at address@example.com . This is configured by default and the (configurable !) separator is set to + . For more info, see the official documentation . Note If you do decide to change the configurable separator, you must add the same line to both docker-data/dms/config/postfix-main.cf and docker-data/dms/config/dovecot.cf , because Dovecot is acting as the delivery agent. For example, to switch to - , add: recipient_delimiter = -","title":"Aliases"},{"location":"config/user-management/aliases/#configuring-regexp-aliases","text":"Additional regexp aliases can be configured by placing them into docker-data/dms/config/postfix-regexp.cf . The regexp aliases get evaluated after the virtual aliases (container path: /tmp/docker-mailserver/postfix-virtual.cf ). For example, the following docker-data/dms/config/postfix-regexp.cf causes all email sent to \"test\" users to be delivered to qa@example.com instead: /^test[0-9][0-9]*@example.com/ qa@example.com","title":"Configuring RegExp Aliases"},{"location":"config/user-management/aliases/#address-tags-extension-delimiters-an-alternative-to-aliases","text":"Postfix supports so-called address tags, in the form of plus (+) tags - i.e. address+tag@example.com will end up at address@example.com . This is configured by default and the (configurable !) separator is set to + . For more info, see the official documentation . Note If you do decide to change the configurable separator, you must add the same line to both docker-data/dms/config/postfix-main.cf and docker-data/dms/config/dovecot.cf , because Dovecot is acting as the delivery agent. For example, to switch to - , add: recipient_delimiter = -","title":"Address Tags (Extension Delimiters) an Alternative to Aliases"},{"location":"contributing/general/","text":"Coding Style When refactoring, writing or altering scripts or other files, adhere to these rules: Adjust your style of coding to the style that is already present ! Even if you do not like it, this is due to consistency. There was a lot of work involved in making all scripts consistent. Use shellcheck to check your scripts ! Your contributions are checked by GitHub Actions too, so you will need to do this. You can lint your work with make lint to check against all targets. Use the provided .editorconfig file. Use /bin/bash instead of /bin/sh in scripts Documentation You will need to have Docker installed. Navigate into the docs/ directory. Then run: docker run --rm -it -p 8000 :8000 -v \" ${ PWD } :/docs\" squidfunk/mkdocs-material This serves the documentation on your local machine on port 8000 . Each change will be hot-reloaded onto the page you view, just edit, save and look at the result.","title":"General Information"},{"location":"contributing/general/#coding-style","text":"When refactoring, writing or altering scripts or other files, adhere to these rules: Adjust your style of coding to the style that is already present ! Even if you do not like it, this is due to consistency. There was a lot of work involved in making all scripts consistent. Use shellcheck to check your scripts ! Your contributions are checked by GitHub Actions too, so you will need to do this. You can lint your work with make lint to check against all targets. Use the provided .editorconfig file. Use /bin/bash instead of /bin/sh in scripts","title":"Coding Style"},{"location":"contributing/general/#documentation","text":"You will need to have Docker installed. Navigate into the docs/ directory. Then run: docker run --rm -it -p 8000 :8000 -v \" ${ PWD } :/docs\" squidfunk/mkdocs-material This serves the documentation on your local machine on port 8000 . Each change will be hot-reloaded onto the page you view, just edit, save and look at the result.","title":"Documentation"},{"location":"contributing/issues-and-pull-requests/","text":"This project is Open Source. That means that you can contribute on enhancements, bug fixing or improving the documentation. Opening an Issue Attention Before opening an issue , read the README carefully, study the documentation , the Postfix/Dovecot documentation and your search engine you trust. The issue tracker is not meant to be used for unrelated questions! When opening an issue, please provide details use case to let the community reproduce your problem. Please start docker-mailserver with the environment variable LOG_LEVEL set to debug or trace and paste the output into the issue. Attention Use the issue templates to provide the necessary information. Issues which do not use these templates are not worked on and closed. By raising issues, I agree to these terms and I understand, that the rules set for the issue tracker will help both maintainers as well as everyone to find a solution. Maintainers take the time to improve on this project and help by solving issues together. It is therefore expected from others to make an effort and comply with the rules . Pull Requests Motivation You want to add a feature? Feel free to start creating an issue explaining what you want to do and how you're thinking doing it. Other users may have the same need and collaboration may lead to better results. Submit a Pull-Request The development workflow is the following: Fork the project and clone your fork with git clone --recurse-submodules ... or run git submodule update --init --recursive after you cloned your fork Write the code that is needed :D Add integration tests if necessary Prepare your environment and run linting and tests Document your improvements if necessary (e.g. if you introduced new environment variables, describe those in the ENV documentation ) and add your changes the changelog under the \"Unreleased\" section Commit (and sign your commit ), push and create a pull-request to merge into master . Please use the pull-request template to provide a minimum of contextual information and make sure to meet the requirements of the checklist. Pull requests are automatically tested against the CI and will be reviewed when tests pass. When your changes are validated, your branch is merged. CI builds the new :edge image immediately and your changes will be includes in the next version release.","title":"Issues and Pull Requests"},{"location":"contributing/issues-and-pull-requests/#opening-an-issue","text":"Attention Before opening an issue , read the README carefully, study the documentation , the Postfix/Dovecot documentation and your search engine you trust. The issue tracker is not meant to be used for unrelated questions! When opening an issue, please provide details use case to let the community reproduce your problem. Please start docker-mailserver with the environment variable LOG_LEVEL set to debug or trace and paste the output into the issue. Attention Use the issue templates to provide the necessary information. Issues which do not use these templates are not worked on and closed. By raising issues, I agree to these terms and I understand, that the rules set for the issue tracker will help both maintainers as well as everyone to find a solution. Maintainers take the time to improve on this project and help by solving issues together. It is therefore expected from others to make an effort and comply with the rules .","title":"Opening an Issue"},{"location":"contributing/issues-and-pull-requests/#pull-requests","text":"Motivation You want to add a feature? Feel free to start creating an issue explaining what you want to do and how you're thinking doing it. Other users may have the same need and collaboration may lead to better results.","title":"Pull Requests"},{"location":"contributing/issues-and-pull-requests/#submit-a-pull-request","text":"The development workflow is the following: Fork the project and clone your fork with git clone --recurse-submodules ... or run git submodule update --init --recursive after you cloned your fork Write the code that is needed :D Add integration tests if necessary Prepare your environment and run linting and tests Document your improvements if necessary (e.g. if you introduced new environment variables, describe those in the ENV documentation ) and add your changes the changelog under the \"Unreleased\" section Commit (and sign your commit ), push and create a pull-request to merge into master . Please use the pull-request template to provide a minimum of contextual information and make sure to meet the requirements of the checklist. Pull requests are automatically tested against the CI and will be reviewed when tests pass. When your changes are validated, your branch is merged. CI builds the new :edge image immediately and your changes will be includes in the next version release.","title":"Submit a Pull-Request"},{"location":"contributing/tests/","text":"Program testing can be used to show the presence of bugs, but never to show their absence! \u2013 Edsger Wybe Dijkstra Introduction DMS employs a variety of unit and integration tests. All tests and associated configuration is stored in the test/ directory. If you want to change existing functionality or integrate a new feature into DMS, you will probably need to work with our test suite. Can I use macOS? We do not support running linting, tests, etc. on macOS at this time. Please use a Linux VM, Debian/Ubuntu is recommended. About We use BATS ( Bash Automated Testing System ) and additional support libraries. BATS is very similar to Bash, and one can easily and quickly get an understanding of how tests in a single file are run. A test file template provides a minimal working example for newcomers to look at. Structure The test/ directory contains multiple directories. Among them is the bats/ directory ( which is the BATS git submodule ) and the helper/ directory. The latter is especially interesting because it contains common support functionality used in almost every test. Actual tests are located in test/tests/ . Test suite undergoing refactoring We are currently in the process of restructuring all of our tests. Tests will be moved into test/tests/parallel/ and new tests should be placed there as well. Using Our Helper Functions There are many functions that aid in writing tests. We urge you to use them! They will not only ease writing a test but they will also do their best to ensure there are no race conditions or other unwanted side effects. To learn about the functions we provide, you can: look into existing tests for helper functions we already used look into the test/helper/ directory which contains all files that can (and will) be loaded in test files We encourage you to try both of the approaches mentioned above. To make understanding and using the helper functions easy, every function contains detailed documentation comments. Read them carefully! How Are Tests Run? Tests are split into two categories: test/tests/parallel/ : Multiple test files are run concurrently to reduce the required time to complete the test suite. A test file will presently run it's own defined test-cases in a sequential order. test/tests/serial/ : Each test file is queued up to run sequentially. Tests that are unable to support running concurrently belong here. Parallel tests are further partitioned into smaller sets. If your system has the resources to support running more than one of those sets at a time this is perfectly ok ( our CI runs tests by distributing the sets across multiple test runners ). Care must be taken not to mix running the serial tests while a parallel set is also running; this is handled for you when using make tests . Running Tests Prerequisites To run the test suite, you will need to: Install Docker Install jq and (GNU) parallel (under Ubuntu, use sudo apt-get -y install jq parallel ) Execute git submodule update --init --recursive if you haven't already initialized the git submodules Executing Test(s) We use make to run commands. You will first need to build the container image via make build . You can then: Run all tests: make clean tests Run a single test: make clean generate-accounts test/ Run multiple unrelated tests: make clean generate-accounts test/, (just add a , and then immediately write the new test name) Run a whole set or all serial tests: make clean generate-accounts tests/parallel/setX where X is the number of the set or make clean generate-accounts tests/serial Setting the Degree of Parallelization for Tests If your machine is capable, you can increase the amount of tests that are run simultaneously by prepending the make clean all command with BATS_PARALLEL_JOBS=X (i.e. BATS_PARALLEL_JOBS=X make clean all ). This wil speed up the test procedure. You can also run all tests in serial by setting BATS_PARALLEL_JOBS=1 this way. The default value of BATS_PARALLEL_JOBS is 2. This can be increased if your system has the resources spare to support running more jobs at once to complete the test suite sooner. Test Output when Running in Parallel When running tests in parallel ( with make clean generate-accounts tests/parallel/setX ), BATS will delay outputting the results until completing all test cases within a file. This likewise delays the reporting of test-case failures. When troubleshooting parallel set tests, you may prefer to run specific tests you're working on serially ( as demonstrated in the example below ). When writing tests, ensure that parallel set tests still pass when run in parallel. You need to account for other tests running in parallel that may interfere with your own tests logic. An Example In this example, you've made a change to the Rspamd feature support ( or adjusted it's tests ). First verify no regressions have been introduced by running it's specific test file: $ make clean generate-accounts test/rspamd rspamd.bats \u2713 [Rspamd] Postfix's main.cf was adjusted [12] \u2713 [Rspamd] normal mail passes fine [44] \u2713 [Rspamd] detects and rejects spam [122] \u2713 [Rspamd] detects and rejects virus [189] As your feature work progresses your change for Rspamd also affects ClamAV. As your change now spans more than just the Rspamd test file, you could run multiple test files serially: $ make clean generate-accounts test/rspamd,clamav rspamd.bats \u2713 [Rspamd] Postfix's main.cf was adjusted [12] \u2713 [Rspamd] normal mail passes fine [44] \u2713 [Rspamd] detects and rejects spam [122] \u2713 [Rspamd] detects and rejects virus [189] clamav.bats \u2713 [ClamAV] log files exist at /var/log/mail directory [68] \u2713 [ClamAV] should be identified by Amavis [67] \u2713 [ClamAV] freshclam cron is enabled [76] \u2713 [ClamAV] env CLAMAV_MESSAGE_SIZE_LIMIT is set correctly [63] \u2713 [ClamAV] rejects virus [60] You're almost finished with your change before submitting it as a PR. It's a good idea to run the full parallel set those individual tests belong to ( especially if you've modified any tests ): $ make clean generate-accounts tests/parallel/set1 default_relay_host.bats \u2713 [Relay] (ENV) 'DEFAULT_RELAY_HOST' should configure 'main.cf:relayhost' [88] spam_virus/amavis.bats \u2713 [Amavis] SpamAssassin integration should be active [1165] spam_virus/clamav.bats \u2713 [ClamAV] log files exist at /var/log/mail directory [73] \u2713 [ClamAV] should be identified by Amavis [67] \u2713 [ClamAV] freshclam cron is enabled [76] ... Even better, before opening a PR run the full test suite: $ make clean tests ...","title":"Tests"},{"location":"contributing/tests/#introduction","text":"DMS employs a variety of unit and integration tests. All tests and associated configuration is stored in the test/ directory. If you want to change existing functionality or integrate a new feature into DMS, you will probably need to work with our test suite. Can I use macOS? We do not support running linting, tests, etc. on macOS at this time. Please use a Linux VM, Debian/Ubuntu is recommended.","title":"Introduction"},{"location":"contributing/tests/#about","text":"We use BATS ( Bash Automated Testing System ) and additional support libraries. BATS is very similar to Bash, and one can easily and quickly get an understanding of how tests in a single file are run. A test file template provides a minimal working example for newcomers to look at.","title":"About"},{"location":"contributing/tests/#structure","text":"The test/ directory contains multiple directories. Among them is the bats/ directory ( which is the BATS git submodule ) and the helper/ directory. The latter is especially interesting because it contains common support functionality used in almost every test. Actual tests are located in test/tests/ . Test suite undergoing refactoring We are currently in the process of restructuring all of our tests. Tests will be moved into test/tests/parallel/ and new tests should be placed there as well.","title":"Structure"},{"location":"contributing/tests/#using-our-helper-functions","text":"There are many functions that aid in writing tests. We urge you to use them! They will not only ease writing a test but they will also do their best to ensure there are no race conditions or other unwanted side effects. To learn about the functions we provide, you can: look into existing tests for helper functions we already used look into the test/helper/ directory which contains all files that can (and will) be loaded in test files We encourage you to try both of the approaches mentioned above. To make understanding and using the helper functions easy, every function contains detailed documentation comments. Read them carefully!","title":"Using Our Helper Functions"},{"location":"contributing/tests/#how-are-tests-run","text":"Tests are split into two categories: test/tests/parallel/ : Multiple test files are run concurrently to reduce the required time to complete the test suite. A test file will presently run it's own defined test-cases in a sequential order. test/tests/serial/ : Each test file is queued up to run sequentially. Tests that are unable to support running concurrently belong here. Parallel tests are further partitioned into smaller sets. If your system has the resources to support running more than one of those sets at a time this is perfectly ok ( our CI runs tests by distributing the sets across multiple test runners ). Care must be taken not to mix running the serial tests while a parallel set is also running; this is handled for you when using make tests .","title":"How Are Tests Run?"},{"location":"contributing/tests/#running-tests","text":"","title":"Running Tests"},{"location":"contributing/tests/#prerequisites","text":"To run the test suite, you will need to: Install Docker Install jq and (GNU) parallel (under Ubuntu, use sudo apt-get -y install jq parallel ) Execute git submodule update --init --recursive if you haven't already initialized the git submodules","title":"Prerequisites"},{"location":"contributing/tests/#executing-tests","text":"We use make to run commands. You will first need to build the container image via make build . You can then: Run all tests: make clean tests Run a single test: make clean generate-accounts test/ Run multiple unrelated tests: make clean generate-accounts test/, (just add a , and then immediately write the new test name) Run a whole set or all serial tests: make clean generate-accounts tests/parallel/setX where X is the number of the set or make clean generate-accounts tests/serial Setting the Degree of Parallelization for Tests If your machine is capable, you can increase the amount of tests that are run simultaneously by prepending the make clean all command with BATS_PARALLEL_JOBS=X (i.e. BATS_PARALLEL_JOBS=X make clean all ). This wil speed up the test procedure. You can also run all tests in serial by setting BATS_PARALLEL_JOBS=1 this way. The default value of BATS_PARALLEL_JOBS is 2. This can be increased if your system has the resources spare to support running more jobs at once to complete the test suite sooner. Test Output when Running in Parallel When running tests in parallel ( with make clean generate-accounts tests/parallel/setX ), BATS will delay outputting the results until completing all test cases within a file. This likewise delays the reporting of test-case failures. When troubleshooting parallel set tests, you may prefer to run specific tests you're working on serially ( as demonstrated in the example below ). When writing tests, ensure that parallel set tests still pass when run in parallel. You need to account for other tests running in parallel that may interfere with your own tests logic.","title":"Executing Test(s)"},{"location":"contributing/tests/#an-example","text":"In this example, you've made a change to the Rspamd feature support ( or adjusted it's tests ). First verify no regressions have been introduced by running it's specific test file: $ make clean generate-accounts test/rspamd rspamd.bats \u2713 [Rspamd] Postfix's main.cf was adjusted [12] \u2713 [Rspamd] normal mail passes fine [44] \u2713 [Rspamd] detects and rejects spam [122] \u2713 [Rspamd] detects and rejects virus [189] As your feature work progresses your change for Rspamd also affects ClamAV. As your change now spans more than just the Rspamd test file, you could run multiple test files serially: $ make clean generate-accounts test/rspamd,clamav rspamd.bats \u2713 [Rspamd] Postfix's main.cf was adjusted [12] \u2713 [Rspamd] normal mail passes fine [44] \u2713 [Rspamd] detects and rejects spam [122] \u2713 [Rspamd] detects and rejects virus [189] clamav.bats \u2713 [ClamAV] log files exist at /var/log/mail directory [68] \u2713 [ClamAV] should be identified by Amavis [67] \u2713 [ClamAV] freshclam cron is enabled [76] \u2713 [ClamAV] env CLAMAV_MESSAGE_SIZE_LIMIT is set correctly [63] \u2713 [ClamAV] rejects virus [60] You're almost finished with your change before submitting it as a PR. It's a good idea to run the full parallel set those individual tests belong to ( especially if you've modified any tests ): $ make clean generate-accounts tests/parallel/set1 default_relay_host.bats \u2713 [Relay] (ENV) 'DEFAULT_RELAY_HOST' should configure 'main.cf:relayhost' [88] spam_virus/amavis.bats \u2713 [Amavis] SpamAssassin integration should be active [1165] spam_virus/clamav.bats \u2713 [ClamAV] log files exist at /var/log/mail directory [73] \u2713 [ClamAV] should be identified by Amavis [67] \u2713 [ClamAV] freshclam cron is enabled [76] ... Even better, before opening a PR run the full test suite: $ make clean tests ...","title":"An Example"},{"location":"examples/tutorials/basic-installation/","text":"A Basic Example 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 chapter . services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver # Provide the FQDN of your mail server here (Your DNS MX record should point to this value) hostname : mail.example.com ports : - \"25:25\" - \"587:587\" - \"993:993\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - /etc/localtime:/etc/localtime:ro environment : - ENABLE_SPAMASSASSIN=1 - SPAMASSASSIN_SPAM_TO_INBOX=1 - ENABLE_CLAMAV=1 - ENABLE_FAIL2BAN=1 - ENABLE_POSTGREY=1 cap_add : - NET_ADMIN # For Fail2Ban to work restart : always A Basic LDAP Setup Note There are currently no LDAP maintainers. If you encounter issues, please raise them in the issue tracker, but be aware that the core maintainers team will most likely not be able to help you. We would appreciate and we encourage everyone to actively participate in maintaining LDAP-related code by becoming a maintainer! services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver # Provide the FQDN of your mail server here (Your DNS MX record should point to this value) hostname : mail.example.com ports : - \"25:25\" - \"587:587\" - \"993:993\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - /etc/localtime:/etc/localtime:ro environment : - ENABLE_SPAMASSASSIN=1 - SPAMASSASSIN_SPAM_TO_INBOX=1 - ENABLE_CLAMAV=1 - ENABLE_FAIL2BAN=1 - ENABLE_POSTGREY=1 - ACCOUNT_PROVISIONER=LDAP - 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 cap_add : - NET_ADMIN # For Fail2Ban to work restart : always A Detailed Example Note This is a community contributed guide. Please let us know via a Github Issue if you're having any difficulty following the guide so that we can update it. This guide is focused on only using SMTP ports (not POP3 and IMAP) with the intent to send received mail to another MTA service such as Gmail . It is not intended to have a MUA client ( eg: Thunderbird ) to retrieve mail directly from docker-mailserver via POP3/IMAP. In this setup docker-mailserver is not intended to receive email externally, so no anti-spam or anti-virus software is needed, making the service lighter to run. Open Relays Adding the docker network's gateway to the list of trusted hosts ( eg: using the network or connected-networks option ), can create an open relay . For instance if IPv6 is enabled on the host machine, but not in Docker . If you're running a version of docker-mailserver earlier than v10.2, you'll need to get setup.sh . Otherwise you can substitute ./setup.sh with docker exec mailserver setup . Pull the docker image: docker pull docker.io/mailserver/docker-mailserver:latest . Create the file docker-compose.yml with a content like this: Example services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver # Provide the FQDN of your mail server here (Your DNS MX record should point to this value) hostname : mail.example.com ports : - \"25:25\" - \"587:587\" - \"465:465\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - /etc/localtime:/etc/localtime:ro environment : - ENABLE_FAIL2BAN=1 # Using letsencrypt for SSL/TLS certificates - SSL_TYPE=letsencrypt # Allow sending emails from other docker containers # Beware creating an Open Relay: https://docker-mailserver.github.io/docker-mailserver/edge/config/environment/#permit_docker - PERMIT_DOCKER=network # You may want to enable this: https://docker-mailserver.github.io/docker-mailserver/edge/config/environment/#spoof_protection # See step 8 below, which demonstrates setup with enabled/disabled SPOOF_PROTECTION: - SPOOF_PROTECTION=0 cap_add : - NET_ADMIN # For Fail2Ban to work restart : always The docs have a detailed page on Environment Variables for reference. Firewalled ports You may need to open ports 25 , 587 and 465 on the firewall. For example, with the firewall ufw , run: ufw allow 25 ufw allow 587 ufw allow 465 Configure your DNS service to use an MX record for the hostname (eg: mail ) you configured in the previous step and add the SPF TXT record. If you manually manage the DNS zone file for the domain, it would look something like this: mail IN A 10.11.12.13 ; mail-server for example.com 3600 IN MX 1 mail.example.com. ; Add SPF record IN TXT \"v=spf1 mx ~all\" Then don't forget to change the serial number and to restart the service. Generate DKIM keys for your domain via ./setup.sh config dkim . Copy the content of the file docker-data/dms/config/opendkim/keys/example.com/mail.txt and add it to your DNS records as a TXT like SPF was handled above. I use bind9 for managing my domains, so I just paste it on example.com.db : mail._domainkey IN TXT ( \"v=DKIM1; h=sha256; k=rsa; \" \"p=MIIBIjANBgkqhkiG9w0BAQEFACAQ8AMIIBCgKCAQEAaH5KuPYPSF3Ppkt466BDMAFGOA4mgqn4oPjZ5BbFlYA9l5jU3bgzRj3l6/Q1n5a9lQs5fNZ7A/HtY0aMvs3nGE4oi+LTejt1jblMhV/OfJyRCunQBIGp0s8G9kIUBzyKJpDayk2+KJSJt/lxL9Iiy0DE5hIv62ZPP6AaTdHBAsJosLFeAzuLFHQ6USyQRojefqFQtgYqWQ2JiZQ3\" \"iqq3bD/BVlwKRp5gH6TEYEmx8EBJUuDxrJhkWRUk2VDl1fqhVBy8A9O7Ah+85nMrlOHIFsTaYo9o6+cDJ6t1i6G1gu+bZD0d3/3bqGLPBQV9LyEL1Rona5V7TJBGg099NQkTz1IwIDAQAB\" ) ; ----- DKIM key mail for example.com Get an SSL certificate, we have a guide for you here ( Let's Encrypt is a popular service to get free SSL certificates). Start docker-mailserver and check the terminal output for any errors: docker-compose up . Create email accounts and aliases: With SPOOF_PROTECTION=0 ./setup.sh email add admin@example.com passwd123 ./setup.sh email add info@example.com passwd123 ./setup.sh alias add admin@example.com external-account@gmail.com ./setup.sh alias add info@example.com external-account@gmail.com ./setup.sh email list ./setup.sh alias list Aliases make sure that any email that comes to these accounts is forwarded to your third-party email address ( external-account@gmail.com ), where they are retrieved ( eg: via third-party web or mobile app ), instead of connecting directly to docker-mailserer with POP3 / IMAP. With SPOOF_PROTECTION=1 ./setup.sh email add admin.gmail@example.com passwd123 ./setup.sh email add info.gmail@example.com passwd123 ./setup.sh alias add admin@example.com admin.gmail@example.com ./setup.sh alias add info@example.com info.gmail@example.com ./setup.sh alias add admin.gmail@example.com external-account@gmail.com ./setup.sh alias add info.gmail@example.com external-account@gmail.com ./setup.sh email list ./setup.sh alias list This extra step is required to avoid the 553 5.7.1 Sender address rejected: not owned by user error ( the accounts used for submitting mail to Gmail are admin.gmail@example.com and info.gmail@example.com ) Send some test emails to these addresses and make other tests. Once everything is working well, stop the container with ctrl+c and start it again as a daemon: docker-compose up -d .","title":"Basic Installation"},{"location":"examples/tutorials/basic-installation/#a-basic-example-with-relevant-environmental-variables","text":"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 chapter . services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver # Provide the FQDN of your mail server here (Your DNS MX record should point to this value) hostname : mail.example.com ports : - \"25:25\" - \"587:587\" - \"993:993\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - /etc/localtime:/etc/localtime:ro environment : - ENABLE_SPAMASSASSIN=1 - SPAMASSASSIN_SPAM_TO_INBOX=1 - ENABLE_CLAMAV=1 - ENABLE_FAIL2BAN=1 - ENABLE_POSTGREY=1 cap_add : - NET_ADMIN # For Fail2Ban to work restart : always","title":"A Basic Example With Relevant Environmental Variables"},{"location":"examples/tutorials/basic-installation/#a-basic-ldap-setup","text":"Note There are currently no LDAP maintainers. If you encounter issues, please raise them in the issue tracker, but be aware that the core maintainers team will most likely not be able to help you. We would appreciate and we encourage everyone to actively participate in maintaining LDAP-related code by becoming a maintainer! services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver # Provide the FQDN of your mail server here (Your DNS MX record should point to this value) hostname : mail.example.com ports : - \"25:25\" - \"587:587\" - \"993:993\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - /etc/localtime:/etc/localtime:ro environment : - ENABLE_SPAMASSASSIN=1 - SPAMASSASSIN_SPAM_TO_INBOX=1 - ENABLE_CLAMAV=1 - ENABLE_FAIL2BAN=1 - ENABLE_POSTGREY=1 - ACCOUNT_PROVISIONER=LDAP - 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 cap_add : - NET_ADMIN # For Fail2Ban to work restart : always","title":"A Basic LDAP Setup"},{"location":"examples/tutorials/basic-installation/#a-detailed-example","text":"Note This is a community contributed guide. Please let us know via a Github Issue if you're having any difficulty following the guide so that we can update it. This guide is focused on only using SMTP ports (not POP3 and IMAP) with the intent to send received mail to another MTA service such as Gmail . It is not intended to have a MUA client ( eg: Thunderbird ) to retrieve mail directly from docker-mailserver via POP3/IMAP. In this setup docker-mailserver is not intended to receive email externally, so no anti-spam or anti-virus software is needed, making the service lighter to run. Open Relays Adding the docker network's gateway to the list of trusted hosts ( eg: using the network or connected-networks option ), can create an open relay . For instance if IPv6 is enabled on the host machine, but not in Docker . If you're running a version of docker-mailserver earlier than v10.2, you'll need to get setup.sh . Otherwise you can substitute ./setup.sh with docker exec mailserver setup . Pull the docker image: docker pull docker.io/mailserver/docker-mailserver:latest . Create the file docker-compose.yml with a content like this: Example services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver # Provide the FQDN of your mail server here (Your DNS MX record should point to this value) hostname : mail.example.com ports : - \"25:25\" - \"587:587\" - \"465:465\" volumes : - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ - ./docker-data/dms/mail-logs/:/var/log/mail/ - ./docker-data/dms/config/:/tmp/docker-mailserver/ - /etc/localtime:/etc/localtime:ro environment : - ENABLE_FAIL2BAN=1 # Using letsencrypt for SSL/TLS certificates - SSL_TYPE=letsencrypt # Allow sending emails from other docker containers # Beware creating an Open Relay: https://docker-mailserver.github.io/docker-mailserver/edge/config/environment/#permit_docker - PERMIT_DOCKER=network # You may want to enable this: https://docker-mailserver.github.io/docker-mailserver/edge/config/environment/#spoof_protection # See step 8 below, which demonstrates setup with enabled/disabled SPOOF_PROTECTION: - SPOOF_PROTECTION=0 cap_add : - NET_ADMIN # For Fail2Ban to work restart : always The docs have a detailed page on Environment Variables for reference. Firewalled ports You may need to open ports 25 , 587 and 465 on the firewall. For example, with the firewall ufw , run: ufw allow 25 ufw allow 587 ufw allow 465 Configure your DNS service to use an MX record for the hostname (eg: mail ) you configured in the previous step and add the SPF TXT record. If you manually manage the DNS zone file for the domain, it would look something like this: mail IN A 10.11.12.13 ; mail-server for example.com 3600 IN MX 1 mail.example.com. ; Add SPF record IN TXT \"v=spf1 mx ~all\" Then don't forget to change the serial number and to restart the service. Generate DKIM keys for your domain via ./setup.sh config dkim . Copy the content of the file docker-data/dms/config/opendkim/keys/example.com/mail.txt and add it to your DNS records as a TXT like SPF was handled above. I use bind9 for managing my domains, so I just paste it on example.com.db : mail._domainkey IN TXT ( \"v=DKIM1; h=sha256; k=rsa; \" \"p=MIIBIjANBgkqhkiG9w0BAQEFACAQ8AMIIBCgKCAQEAaH5KuPYPSF3Ppkt466BDMAFGOA4mgqn4oPjZ5BbFlYA9l5jU3bgzRj3l6/Q1n5a9lQs5fNZ7A/HtY0aMvs3nGE4oi+LTejt1jblMhV/OfJyRCunQBIGp0s8G9kIUBzyKJpDayk2+KJSJt/lxL9Iiy0DE5hIv62ZPP6AaTdHBAsJosLFeAzuLFHQ6USyQRojefqFQtgYqWQ2JiZQ3\" \"iqq3bD/BVlwKRp5gH6TEYEmx8EBJUuDxrJhkWRUk2VDl1fqhVBy8A9O7Ah+85nMrlOHIFsTaYo9o6+cDJ6t1i6G1gu+bZD0d3/3bqGLPBQV9LyEL1Rona5V7TJBGg099NQkTz1IwIDAQAB\" ) ; ----- DKIM key mail for example.com Get an SSL certificate, we have a guide for you here ( Let's Encrypt is a popular service to get free SSL certificates). Start docker-mailserver and check the terminal output for any errors: docker-compose up . Create email accounts and aliases: With SPOOF_PROTECTION=0 ./setup.sh email add admin@example.com passwd123 ./setup.sh email add info@example.com passwd123 ./setup.sh alias add admin@example.com external-account@gmail.com ./setup.sh alias add info@example.com external-account@gmail.com ./setup.sh email list ./setup.sh alias list Aliases make sure that any email that comes to these accounts is forwarded to your third-party email address ( external-account@gmail.com ), where they are retrieved ( eg: via third-party web or mobile app ), instead of connecting directly to docker-mailserer with POP3 / IMAP. With SPOOF_PROTECTION=1 ./setup.sh email add admin.gmail@example.com passwd123 ./setup.sh email add info.gmail@example.com passwd123 ./setup.sh alias add admin@example.com admin.gmail@example.com ./setup.sh alias add info@example.com info.gmail@example.com ./setup.sh alias add admin.gmail@example.com external-account@gmail.com ./setup.sh alias add info.gmail@example.com external-account@gmail.com ./setup.sh email list ./setup.sh alias list This extra step is required to avoid the 553 5.7.1 Sender address rejected: not owned by user error ( the accounts used for submitting mail to Gmail are admin.gmail@example.com and info.gmail@example.com ) Send some test emails to these addresses and make other tests. Once everything is working well, stop the container with ctrl+c and start it again as a daemon: docker-compose up -d .","title":"A Detailed Example"},{"location":"examples/tutorials/blog-posts/","text":"This site lists blog entries that write about the project. If you blogged about docker-mailserver let us know so we can add it here! Installing docker-mailserver by @andrewlow Self hosted mail-server by @matrixes Docker-mailserver on kubernetes by @ErikEngerd","title":"Blog Posts"},{"location":"examples/tutorials/docker-build/","text":"Building your own Docker image Submodules You'll need to retrieve the git submodules prior to building your own Docker image. From within your copy of the git repo run the following to retrieve the submodules and build the Docker image: git submodule update --init --recursive docker build -t mailserver/docker-mailserver . Or, you can clone and retrieve the submodules in one command: git clone --recurse-submodules https://github.com/docker-mailserver/docker-mailserver About Docker Version We make use of build-features that require a recent version of Docker. Depending on your distribution, please have a look at the official installation documentation for Docker to get the latest version. Otherwise, you may encounter issues, for example with the --link flag for a COPY command. Environment If you are not using make to build the image, note that you will need to provide DOCKER_BUILDKIT=1 to the docker build command for the build to succeed. Build Arguments The Dockerfile takes additional, so-called build arguments. These are VCS_VERSION : the image version (default = edge) VCS_REVISION : the image revision (default = unknown) When using make to build the image, these are filled with proper values. You can build the image without supplying these arguments just fine though.","title":"Building your own Docker image"},{"location":"examples/tutorials/docker-build/#building-your-own-docker-image","text":"","title":"Building your own Docker image"},{"location":"examples/tutorials/docker-build/#submodules","text":"You'll need to retrieve the git submodules prior to building your own Docker image. From within your copy of the git repo run the following to retrieve the submodules and build the Docker image: git submodule update --init --recursive docker build -t mailserver/docker-mailserver . Or, you can clone and retrieve the submodules in one command: git clone --recurse-submodules https://github.com/docker-mailserver/docker-mailserver","title":"Submodules"},{"location":"examples/tutorials/docker-build/#about-docker","text":"","title":"About Docker"},{"location":"examples/tutorials/docker-build/#version","text":"We make use of build-features that require a recent version of Docker. Depending on your distribution, please have a look at the official installation documentation for Docker to get the latest version. Otherwise, you may encounter issues, for example with the --link flag for a COPY command.","title":"Version"},{"location":"examples/tutorials/docker-build/#environment","text":"If you are not using make to build the image, note that you will need to provide DOCKER_BUILDKIT=1 to the docker build command for the build to succeed.","title":"Environment"},{"location":"examples/tutorials/docker-build/#build-arguments","text":"The Dockerfile takes additional, so-called build arguments. These are VCS_VERSION : the image version (default = edge) VCS_REVISION : the image revision (default = unknown) When using make to build the image, these are filled with proper values. You can build the image without supplying these arguments just fine though.","title":"Build Arguments"},{"location":"examples/tutorials/mailserver-behind-proxy/","text":"Using docker-mailserver behind a Proxy Information If you are hiding your container behind a proxy service you might have discovered that the proxied requests from now on contain the proxy IP as the request origin. Whilst this behavior is technical correct it produces certain problems on the containers behind the proxy as they cannot distinguish the real origin of the requests anymore. To solve this problem on TCP connections we can make use of the proxy protocol . Compared to other workarounds that exist ( X-Forwarded-For which only works for HTTP requests or Tproxy that requires you to recompile your kernel) the proxy protocol: It is protocol agnostic (can work with any layer 7 protocols, even when encrypted). It does not require any infrastructure changes. NAT-ing firewalls have no impact it. It is scalable. There is only one condition: both endpoints of the connection MUST be compatible with proxy protocol. Luckily dovecot and postfix are both Proxy-Protocol ready softwares so it depends only on your used reverse-proxy / loadbalancer. Configuration of the used Proxy Software The configuration depends on the used proxy system. I will provide the configuration examples of traefik v2 using IMAP and SMTP with implicit TLS. Feel free to add your configuration if you achieved the same goal using different proxy software below: Traefik v2 Truncated configuration of traefik itself: version : '3.8' services : reverse-proxy : image : docker.io/traefik:latest # v2.5 container_name : docker-traefik restart : always command : - \"--providers.docker\" - \"--providers.docker.exposedbydefault=false\" - \"--providers.docker.network=proxy\" - \"--entrypoints.web.address=:80\" - \"--entryPoints.websecure.address=:443\" - \"--entryPoints.smtp.address=:25\" - \"--entryPoints.smtp-ssl.address=:465\" - \"--entryPoints.imap-ssl.address=:993\" - \"--entryPoints.sieve.address=:4190\" ports : - \"25:25\" - \"465:465\" - \"993:993\" - \"4190:4190\" [ ... ] Truncated list of necessary labels on the docker-mailserver container: version : '3.8' services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail domainname : example.com restart : always networks : - proxy labels : - \"traefik.enable=true\" - \"traefik.tcp.routers.smtp.rule=HostSNI(`*`)\" - \"traefik.tcp.routers.smtp.entrypoints=smtp\" - \"traefik.tcp.routers.smtp.service=smtp\" - \"traefik.tcp.services.smtp.loadbalancer.server.port=25\" - \"traefik.tcp.services.smtp.loadbalancer.proxyProtocol.version=1\" - \"traefik.tcp.routers.smtp-ssl.rule=HostSNI(`*`)\" - \"traefik.tcp.routers.smtp-ssl.tls=false\" - \"traefik.tcp.routers.smtp-ssl.entrypoints=smtp-ssl\" - \"traefik.tcp.routers.smtp-ssl.service=smtp-ssl\" - \"traefik.tcp.services.smtp-ssl.loadbalancer.server.port=465\" - \"traefik.tcp.services.smtp-ssl.loadbalancer.proxyProtocol.version=1\" - \"traefik.tcp.routers.imap-ssl.rule=HostSNI(`*`)\" - \"traefik.tcp.routers.imap-ssl.entrypoints=imap-ssl\" - \"traefik.tcp.routers.imap-ssl.service=imap-ssl\" - \"traefik.tcp.services.imap-ssl.loadbalancer.server.port=10993\" - \"traefik.tcp.services.imap-ssl.loadbalancer.proxyProtocol.version=2\" - \"traefik.tcp.routers.sieve.rule=HostSNI(`*`)\" - \"traefik.tcp.routers.sieve.entrypoints=sieve\" - \"traefik.tcp.routers.sieve.service=sieve\" - \"traefik.tcp.services.sieve.loadbalancer.server.port=4190\" [ ... ] Keep in mind that it is necessary to use port 10993 here. More information below at dovecot configuration. Configuration of the Backend ( dovecot and postfix ) The following changes can be achieved completely by adding the content to the appropriate files by using the projects function to overwrite config files . Changes for postfix can be applied by adding the following content to docker-data/dms/config/postfix-main.cf : postscreen_upstream_proxy_protocol = haproxy and to docker-data/dms/config/postfix-master.cf : submission/inet/smtpd_upstream_proxy_protocol = haproxy smtps/inet/smtpd_upstream_proxy_protocol = haproxy Changes for dovecot can be applied by adding the following content to docker-data/dms/config/dovecot.cf : haproxy_trusted_networks = , haproxy_timeout = 3 secs service imap-login { inet_listener imaps { haproxy = yes ssl = yes port = 10993 } } Note Port 10993 is used here to avoid conflicts with internal systems like postscreen and amavis as they will exchange messages on the default port and obviously have a different origin then compared to the proxy.","title":"Mailserver behind Proxy"},{"location":"examples/tutorials/mailserver-behind-proxy/#using-docker-mailserver-behind-a-proxy","text":"","title":"Using docker-mailserver behind a Proxy"},{"location":"examples/tutorials/mailserver-behind-proxy/#information","text":"If you are hiding your container behind a proxy service you might have discovered that the proxied requests from now on contain the proxy IP as the request origin. Whilst this behavior is technical correct it produces certain problems on the containers behind the proxy as they cannot distinguish the real origin of the requests anymore. To solve this problem on TCP connections we can make use of the proxy protocol . Compared to other workarounds that exist ( X-Forwarded-For which only works for HTTP requests or Tproxy that requires you to recompile your kernel) the proxy protocol: It is protocol agnostic (can work with any layer 7 protocols, even when encrypted). It does not require any infrastructure changes. NAT-ing firewalls have no impact it. It is scalable. There is only one condition: both endpoints of the connection MUST be compatible with proxy protocol. Luckily dovecot and postfix are both Proxy-Protocol ready softwares so it depends only on your used reverse-proxy / loadbalancer.","title":"Information"},{"location":"examples/tutorials/mailserver-behind-proxy/#configuration-of-the-used-proxy-software","text":"The configuration depends on the used proxy system. I will provide the configuration examples of traefik v2 using IMAP and SMTP with implicit TLS. Feel free to add your configuration if you achieved the same goal using different proxy software below: Traefik v2 Truncated configuration of traefik itself: version : '3.8' services : reverse-proxy : image : docker.io/traefik:latest # v2.5 container_name : docker-traefik restart : always command : - \"--providers.docker\" - \"--providers.docker.exposedbydefault=false\" - \"--providers.docker.network=proxy\" - \"--entrypoints.web.address=:80\" - \"--entryPoints.websecure.address=:443\" - \"--entryPoints.smtp.address=:25\" - \"--entryPoints.smtp-ssl.address=:465\" - \"--entryPoints.imap-ssl.address=:993\" - \"--entryPoints.sieve.address=:4190\" ports : - \"25:25\" - \"465:465\" - \"993:993\" - \"4190:4190\" [ ... ] Truncated list of necessary labels on the docker-mailserver container: version : '3.8' services : mailserver : image : docker.io/mailserver/docker-mailserver:latest container_name : mailserver hostname : mail domainname : example.com restart : always networks : - proxy labels : - \"traefik.enable=true\" - \"traefik.tcp.routers.smtp.rule=HostSNI(`*`)\" - \"traefik.tcp.routers.smtp.entrypoints=smtp\" - \"traefik.tcp.routers.smtp.service=smtp\" - \"traefik.tcp.services.smtp.loadbalancer.server.port=25\" - \"traefik.tcp.services.smtp.loadbalancer.proxyProtocol.version=1\" - \"traefik.tcp.routers.smtp-ssl.rule=HostSNI(`*`)\" - \"traefik.tcp.routers.smtp-ssl.tls=false\" - \"traefik.tcp.routers.smtp-ssl.entrypoints=smtp-ssl\" - \"traefik.tcp.routers.smtp-ssl.service=smtp-ssl\" - \"traefik.tcp.services.smtp-ssl.loadbalancer.server.port=465\" - \"traefik.tcp.services.smtp-ssl.loadbalancer.proxyProtocol.version=1\" - \"traefik.tcp.routers.imap-ssl.rule=HostSNI(`*`)\" - \"traefik.tcp.routers.imap-ssl.entrypoints=imap-ssl\" - \"traefik.tcp.routers.imap-ssl.service=imap-ssl\" - \"traefik.tcp.services.imap-ssl.loadbalancer.server.port=10993\" - \"traefik.tcp.services.imap-ssl.loadbalancer.proxyProtocol.version=2\" - \"traefik.tcp.routers.sieve.rule=HostSNI(`*`)\" - \"traefik.tcp.routers.sieve.entrypoints=sieve\" - \"traefik.tcp.routers.sieve.service=sieve\" - \"traefik.tcp.services.sieve.loadbalancer.server.port=4190\" [ ... ] Keep in mind that it is necessary to use port 10993 here. More information below at dovecot configuration.","title":"Configuration of the used Proxy Software"},{"location":"examples/tutorials/mailserver-behind-proxy/#configuration-of-the-backend-dovecot-and-postfix","text":"The following changes can be achieved completely by adding the content to the appropriate files by using the projects function to overwrite config files . Changes for postfix can be applied by adding the following content to docker-data/dms/config/postfix-main.cf : postscreen_upstream_proxy_protocol = haproxy and to docker-data/dms/config/postfix-master.cf : submission/inet/smtpd_upstream_proxy_protocol = haproxy smtps/inet/smtpd_upstream_proxy_protocol = haproxy Changes for dovecot can be applied by adding the following content to docker-data/dms/config/dovecot.cf : haproxy_trusted_networks = , haproxy_timeout = 3 secs service imap-login { inet_listener imaps { haproxy = yes ssl = yes port = 10993 } } Note Port 10993 is used here to avoid conflicts with internal systems like postscreen and amavis as they will exchange messages on the default port and obviously have a different origin then compared to the proxy.","title":"Configuration of the Backend (dovecot and postfix)"},{"location":"examples/use-cases/forward-only-mailserver-with-ldap-authentication/","text":"Building a Forward-Only Mail-Server A forward-only mail-server does not have any local mailboxes. Instead, it has only aliases that forward emails to external email accounts (for example to a Gmail account). You can also send email from the localhost (the computer where docker-mailserver is installed), using as sender any of the alias addresses. The important settings for this setup (on mailserver.env ) are these: PERMIT_DOCKER = host ENABLE_POP3 = ENABLE_CLAMAV = 0 SMTP_ONLY = 1 ENABLE_SPAMASSASSIN = 0 ENABLE_FETCHMAIL = 0 Since there are no local mailboxes, we use SMTP_ONLY=1 to disable dovecot . We disable as well the other services that are related to local mailboxes ( POP3 , ClamAV , SpamAssassin , etc.) We can create aliases with ./setup.sh , like this: ./setup.sh alias add Authenticating with LDAP If you want to send emails from outside the mail-server you have to authenticate somehow (with a username and password). One way of doing it is described in this discussion . However if there are many user accounts, it is better to use authentication with LDAP. The settings for this on mailserver.env are: ENABLE_LDAP = 1 # with the :edge tag, use ACCOUNT_PROVISIONER ACCOUNT_PROVISIONER = LDAP LDAP_START_TLS = yes LDAP_SERVER_HOST = ldap.example.org LDAP_SEARCH_BASE = ou=users,dc=example,dc=org LDAP_BIND_DN = cn=mailserver,dc=example,dc=org LDAP_BIND_PW = pass1234 ENABLE_SASLAUTHD = 1 SASLAUTHD_MECHANISMS = ldap SASLAUTHD_LDAP_SERVER = ldap.example.org SASLAUTHD_LDAP_START_TLS = yes SASLAUTHD_LDAP_BIND_DN = cn=mailserver,dc=example,dc=org SASLAUTHD_LDAP_PASSWORD = pass1234 SASLAUTHD_LDAP_SEARCH_BASE = ou=users,dc=example,dc=org SASLAUTHD_LDAP_FILTER = (&(uid=%U)(objectClass=inetOrgPerson)) My LDAP data structure is very basic, containing only the username, password, and the external email address where to forward emails for this user. An entry looks like this: add uid = username,ou=users,dc=example,dc=org uid : username objectClass : inetOrgPerson sn : username cn : username userPassword : {SSHA}abcdefghi123456789 email : external-account@gmail.com This structure is different from what is expected/assumed from the configuration scripts of docker-mailserver , so it doesn't work just by using the LDAP_QUERY_FILTER_... settings. Instead, I had to use a custom configuration ( via user-patches.sh ). I created the script docker-data/dms/config/user-patches.sh , with content like this: #!/bin/bash rm -f /etc/postfix/ { ldap-groups.cf,ldap-domains.cf } postconf \\ \"virtual_mailbox_domains = /etc/postfix/vhost\" \\ \"virtual_alias_maps = ldap:/etc/postfix/ldap-aliases.cf texthash:/etc/postfix/virtual\" \\ \"smtpd_sender_login_maps = ldap:/etc/postfix/ldap-users.cf\" sed -i /etc/postfix/ldap-users.cf \\ -e '/query_filter/d' \\ -e '/result_attribute/d' \\ -e '/result_format/d' cat <> /etc/postfix/ldap-users.cf query_filter = (uid=%u) result_attribute = uid result_format = %s@example.org EOF sed -i /etc/postfix/ldap-aliases.cf \\ -e '/domain/d' \\ -e '/query_filter/d' \\ -e '/result_attribute/d' cat <> /etc/postfix/ldap-aliases.cf domain = example.org query_filter = (uid=%u) result_attribute = mail EOF postfix reload You see that besides query_filter , I had to customize as well result_attribute and result_format . See also For more details about using LDAP see: LDAP managed mail-server with Postfix and Dovecot for multiple domains Note Another solution that serves as a forward-only mail-server is this .","title":"Forward-Only Mail-Server with LDAP"},{"location":"examples/use-cases/forward-only-mailserver-with-ldap-authentication/#building-a-forward-only-mail-server","text":"A forward-only mail-server does not have any local mailboxes. Instead, it has only aliases that forward emails to external email accounts (for example to a Gmail account). You can also send email from the localhost (the computer where docker-mailserver is installed), using as sender any of the alias addresses. The important settings for this setup (on mailserver.env ) are these: PERMIT_DOCKER = host ENABLE_POP3 = ENABLE_CLAMAV = 0 SMTP_ONLY = 1 ENABLE_SPAMASSASSIN = 0 ENABLE_FETCHMAIL = 0 Since there are no local mailboxes, we use SMTP_ONLY=1 to disable dovecot . We disable as well the other services that are related to local mailboxes ( POP3 , ClamAV , SpamAssassin , etc.) We can create aliases with ./setup.sh , like this: ./setup.sh alias add ","title":"Building a Forward-Only Mail-Server"},{"location":"examples/use-cases/forward-only-mailserver-with-ldap-authentication/#authenticating-with-ldap","text":"If you want to send emails from outside the mail-server you have to authenticate somehow (with a username and password). One way of doing it is described in this discussion . However if there are many user accounts, it is better to use authentication with LDAP. The settings for this on mailserver.env are: ENABLE_LDAP = 1 # with the :edge tag, use ACCOUNT_PROVISIONER ACCOUNT_PROVISIONER = LDAP LDAP_START_TLS = yes LDAP_SERVER_HOST = ldap.example.org LDAP_SEARCH_BASE = ou=users,dc=example,dc=org LDAP_BIND_DN = cn=mailserver,dc=example,dc=org LDAP_BIND_PW = pass1234 ENABLE_SASLAUTHD = 1 SASLAUTHD_MECHANISMS = ldap SASLAUTHD_LDAP_SERVER = ldap.example.org SASLAUTHD_LDAP_START_TLS = yes SASLAUTHD_LDAP_BIND_DN = cn=mailserver,dc=example,dc=org SASLAUTHD_LDAP_PASSWORD = pass1234 SASLAUTHD_LDAP_SEARCH_BASE = ou=users,dc=example,dc=org SASLAUTHD_LDAP_FILTER = (&(uid=%U)(objectClass=inetOrgPerson)) My LDAP data structure is very basic, containing only the username, password, and the external email address where to forward emails for this user. An entry looks like this: add uid = username,ou=users,dc=example,dc=org uid : username objectClass : inetOrgPerson sn : username cn : username userPassword : {SSHA}abcdefghi123456789 email : external-account@gmail.com This structure is different from what is expected/assumed from the configuration scripts of docker-mailserver , so it doesn't work just by using the LDAP_QUERY_FILTER_... settings. Instead, I had to use a custom configuration ( via user-patches.sh ). I created the script docker-data/dms/config/user-patches.sh , with content like this: #!/bin/bash rm -f /etc/postfix/ { ldap-groups.cf,ldap-domains.cf } postconf \\ \"virtual_mailbox_domains = /etc/postfix/vhost\" \\ \"virtual_alias_maps = ldap:/etc/postfix/ldap-aliases.cf texthash:/etc/postfix/virtual\" \\ \"smtpd_sender_login_maps = ldap:/etc/postfix/ldap-users.cf\" sed -i /etc/postfix/ldap-users.cf \\ -e '/query_filter/d' \\ -e '/result_attribute/d' \\ -e '/result_format/d' cat <> /etc/postfix/ldap-users.cf query_filter = (uid=%u) result_attribute = uid result_format = %s@example.org EOF sed -i /etc/postfix/ldap-aliases.cf \\ -e '/domain/d' \\ -e '/query_filter/d' \\ -e '/result_attribute/d' cat <> /etc/postfix/ldap-aliases.cf domain = example.org query_filter = (uid=%u) result_attribute = mail EOF postfix reload You see that besides query_filter , I had to customize as well result_attribute and result_format . See also For more details about using LDAP see: LDAP managed mail-server with Postfix and Dovecot for multiple domains Note Another solution that serves as a forward-only mail-server is this .","title":"Authenticating with LDAP"},{"location":"examples/use-cases/imap-folders/","text":"Mailboxes ( aka IMAP Folders ) INBOX is setup as the private inbox namespace . By default target/dovecot/15-mailboxes.conf configures the special IMAP folders Drafts , Sent , Junk and Trash to be automatically created and subscribed. They are all assigned to the private inbox namespace ( which implicitly provides the INBOX folder ). These IMAP folders are considered special because they add a \"SPECIAL-USE\" attribute , which is a standardized way to communicate to mail clients that the folder serves a purpose like storing spam/junk mail ( \\Junk ) or deleted mail ( \\Trash ). This differentiates them from regular mail folders that you may use for organizing. Adding a mailbox folder See target/dovecot/15-mailboxes.conf for existing mailbox folders which you can modify or uncomment to enable some other common mailboxes. For more information try the official Dovecot documentation . The Archive special IMAP folder may be useful to enable. To do so, make a copy of target/dovecot/15-mailboxes.conf and uncomment the Archive mailbox definition. Mail clients should understand that this folder is intended for archiving mail due to the \\Archive \"SPECIAL-USE\" attribute . With the provided docker-compose.yml example, a volume bind mounts the host directory docker-data/dms/config/ to the container location /tmp/docker-mailserver/ . Config file overrides should instead be mounted to a different location as described in Overriding Configuration for Dovecot : volumes : - ./docker-data/dms/config/dovecot/15-mailboxes.conf:/etc/dovecot/conf.d/15-mailboxes.conf:ro Caution Adding folders to an existing setup Handling of newly added mailbox folders can be inconsistent across mail clients: Users may experience issues such as archived emails only being available locally. Users may need to migrate emails manually between two folders. Support for SPECIAL-USE attributes Not all mail clients support the SPECIAL-USE attribute for mailboxes ( defined in RFC 6154 ). These clients will treat the mailbox folder as any other, using the name assigned to it instead. Some clients may still know to treat these folders for their intended purpose if the mailbox name matches the common names that the SPECIAL-USE attributes represent ( eg Sent as the mailbox name for \\Sent ). Internationalization (i18n) Usually the mail client will know via context such as the SPECIAL-USE attribute or common English mailbox names, to provide a localized label for the users preferred language. Take care to test localized names work well as well. Email Clients Support If a new mail account is added without the SPECIAL-USE attribute enabled for archives: Thunderbird suggests and may create an Archives folder on the server. Outlook for Android archives to a local folder. Spark for Android archives to server folder named Archive . If a new mail account is added after the SPECIAL-USE attribute is enabled for archives: Thunderbird , Outlook for Android and Spark for Android will use the mailbox folder name assigned. Windows Mail Windows Mail has been said to ignore SPECIAL-USE attribute and look only at the mailbox folder name assigned. Needs citation This information is provided by the community. It presently lacks references to confirm the behaviour. If any information is incorrect please let us know!","title":"Customize IMAP Folders"},{"location":"examples/use-cases/imap-folders/#mailboxes-aka-imap-folders","text":"INBOX is setup as the private inbox namespace . By default target/dovecot/15-mailboxes.conf configures the special IMAP folders Drafts , Sent , Junk and Trash to be automatically created and subscribed. They are all assigned to the private inbox namespace ( which implicitly provides the INBOX folder ). These IMAP folders are considered special because they add a \"SPECIAL-USE\" attribute , which is a standardized way to communicate to mail clients that the folder serves a purpose like storing spam/junk mail ( \\Junk ) or deleted mail ( \\Trash ). This differentiates them from regular mail folders that you may use for organizing.","title":"Mailboxes (aka IMAP Folders)"},{"location":"examples/use-cases/imap-folders/#adding-a-mailbox-folder","text":"See target/dovecot/15-mailboxes.conf for existing mailbox folders which you can modify or uncomment to enable some other common mailboxes. For more information try the official Dovecot documentation . The Archive special IMAP folder may be useful to enable. To do so, make a copy of target/dovecot/15-mailboxes.conf and uncomment the Archive mailbox definition. Mail clients should understand that this folder is intended for archiving mail due to the \\Archive \"SPECIAL-USE\" attribute . With the provided docker-compose.yml example, a volume bind mounts the host directory docker-data/dms/config/ to the container location /tmp/docker-mailserver/ . Config file overrides should instead be mounted to a different location as described in Overriding Configuration for Dovecot : volumes : - ./docker-data/dms/config/dovecot/15-mailboxes.conf:/etc/dovecot/conf.d/15-mailboxes.conf:ro","title":"Adding a mailbox folder"},{"location":"examples/use-cases/imap-folders/#caution","text":"","title":"Caution"},{"location":"examples/use-cases/imap-folders/#adding-folders-to-an-existing-setup","text":"Handling of newly added mailbox folders can be inconsistent across mail clients: Users may experience issues such as archived emails only being available locally. Users may need to migrate emails manually between two folders.","title":"Adding folders to an existing setup"},{"location":"examples/use-cases/imap-folders/#support-for-special-use-attributes","text":"Not all mail clients support the SPECIAL-USE attribute for mailboxes ( defined in RFC 6154 ). These clients will treat the mailbox folder as any other, using the name assigned to it instead. Some clients may still know to treat these folders for their intended purpose if the mailbox name matches the common names that the SPECIAL-USE attributes represent ( eg Sent as the mailbox name for \\Sent ).","title":"Support for SPECIAL-USE attributes"},{"location":"examples/use-cases/imap-folders/#internationalization-i18n","text":"Usually the mail client will know via context such as the SPECIAL-USE attribute or common English mailbox names, to provide a localized label for the users preferred language. Take care to test localized names work well as well.","title":"Internationalization (i18n)"},{"location":"examples/use-cases/imap-folders/#email-clients-support","text":"If a new mail account is added without the SPECIAL-USE attribute enabled for archives: Thunderbird suggests and may create an Archives folder on the server. Outlook for Android archives to a local folder. Spark for Android archives to server folder named Archive . If a new mail account is added after the SPECIAL-USE attribute is enabled for archives: Thunderbird , Outlook for Android and Spark for Android will use the mailbox folder name assigned. Windows Mail Windows Mail has been said to ignore SPECIAL-USE attribute and look only at the mailbox folder name assigned. Needs citation This information is provided by the community. It presently lacks references to confirm the behaviour. If any information is incorrect please let us know!","title":"Email Clients Support"}]} \ No newline at end of file diff --git a/edge/usage/index.html b/edge/usage/index.html index 87de31cc..239a5574 100644 --- a/edge/usage/index.html +++ b/edge/usage/index.html @@ -1728,6 +1728,7 @@ chmod a+x ./setup.sh

Using the Correct Commands For Stopping and Starting DMS

Use docker compose up / down, not docker compose start / stop. Otherwise, the container is not properly destroyed and you may experience problems during startup because of inconsistent state.

+

Using Ctrl+C is not supported either!

You are able to get a full overview of how the configuration works by either running: