Go to file
Brennan Kinney 1024e0ccf2
tests: Extract some test cases out from tests.bats (#2980)
While working on tests, I noticed that some of the configs being mounted were adding a few seconds to the start-up time of each container. Notably `postfix-*` and `dovecot.conf` config files, which have been extracted out into their own tests with those files moved into a separate config folder.

`tests.bats` has been adapted to the common setup helper, and removed ENV no longer required to run those tests. Future PRs will extract out more tests.

Review may be easier via individual commit diffs and their associated commit messages describing relevant changes.

<details>
<summary>Commit message history for reference</summary>

```markdown
tests(chore): `tests.bats` - Remove redundant config
===
- ONEDIR volume support no longer relevant, this should have been dropped.
- ClamAV ENV no longer relevant as related tests have been extracted already.
- Same with the some of the SpamAssassin ENV config.
- `VIRUSMAILS_DELETE_DELAY` is tested in the file, but doesn't use this ENV at all? (runs a separate instance to test the ENV instead)
- Hostname updated in preparation for migrating to new test helpers. Relevant test lines referencing the hostname have likewise been updated.
```

```markdown
tests(chore): `tests.bats` - Convert to common setup
===
ENV remains the same, but required adding `ENABLE_AMAVIS=1` to bring that back, while the following became redundant as they're now defaulting to explicitly disabled in the helper method:

- `ENABLE_CLAMAV=0`
- `LOG_LEVEL=debug`
- `ENABLE_UPDATE_CHECK=0`
- `--hostname` + `--tty` + standard `--volume` lines
- `-e` option expanded to long-name `--env`, and all `\` dropped as no longer necessary.

`wait_for_finished_setup_in_container` is now redundant thanks to `common_container_setup`.
```

```markdown
tests(refactor): `tests.bats` - Extract out Dovecot Sieve tests
===
Sieve test files relocated into `test/config/dovecot-sieve/` for better isolation.

`dovecot.sieve` was not using the `reject` import, and we should not encourage it? (docs still do):
https://support.tigertech.net/sieve#the-sieve-reject-jmp
```

```markdown
tests: `tests.bats` - Extract out `checking smtp` tests
===
Migrated to the standard template and copied over the original test cases with `_run_in_container` adjustment only.

Identified minimum required ENV along with which mail is required for each test case.
```

```markdown
tests(refactor): `smtp-delivery.bats`
===
- Disabled `ENABLE_SRS=1`, not necessary for these tests.
- Added a SpamAssassin related test (X-SPAM headers) which requires `SA_TAG` to properly pass (or `ENABLE_SRS=1` to deliver into inbox).
- Many lines with double quotes changed to single quote wrapping, and moving out `grep` filters into `assert_output --partial` lines instead.
- Instead of `wc -l` making failures less helpful, switch to the helper method `_should_output_number_of_lines`
- x2 `assert_output` with different EOF style of usage was not actually failing on tests when it should. Changed to assert partial output of each expected line, and count the number of lines instead.
- Added additional comments related to the test cases with a `TODO` note about `SPAMASSASSIN_SPAM_TO_INBOX=1`.
- Revised test case names, including using the common prefix var.
- `tests.bats` no longer needs to send all these emails, no other test cases require them. This affects a test checking a `/mail` folder exists which has been corrected, and a quotas test case adjusted to expect an empty quota size output.
```

```markdown
tests: `tests.bats` - Extract out test cases for config overrides
===
Slight improvement by additionally matching `postconf` output to verify the setting is properly applied.
```

```markdown
tests: `tests.bats` - Extract out Amavis SpamAssassin test case
===
Removes the need for SpamAssassin ENV in `tests.bats`.
```

</details>
2023-01-07 11:36:20 +13:00
.github chore(deps): Bump actions/stale from 6 to 7 (#2960) 2022-12-26 23:01:08 +01:00
config-examples Remove unusual space from shebang line (#2834) 2022-10-17 10:40:09 +02:00
docs docs: Revise contributing advice for tests 2023-01-06 16:50:09 +13:00
target feature: provide initial Rspamd support (#2902) 2023-01-05 08:39:00 +01:00
test tests: Extract some test cases out from tests.bats (#2980) 2023-01-07 11:36:20 +13:00
.all-contributorsrc Update contributors (#2143) 2021-08-28 15:23:11 +02:00
.dockerignore Update check (#1951) 2021-05-19 21:18:06 +02:00
.editorconfig cleaned up >/dev/nulls in Dockerfile and replaced em dashes with normal dashes (#2024) 2021-06-08 13:20:20 +12:00
.gitignore Add docker-data/ (#2982) 2023-01-06 09:09:58 +01:00
.gitmodules tests: Update submodules for bats (#2715) 2022-08-12 11:09:17 +12:00
CHANGELOG.md chore: Update changelog and version (#2944) 2022-12-22 23:27:40 +01:00
CODE_OF_CONDUCT.md docs(fix): Update wiki references to the new docs url 2021-03-25 11:49:24 +13:00
CONTRIBUTORS.md docs(CONTRIBUTORS): update contributors (#2969) 2023-01-01 15:17:35 +01:00
docker-compose.yml Add basic container healthcheck (#2625) 2022-06-07 11:54:58 +02:00
Dockerfile feature: provide initial Rspamd support (#2902) 2023-01-05 08:39:00 +01:00
LICENSE Final Migration Step (#6) 2021-01-16 10:16:05 +01:00
mailserver.env feature: provide initial Rspamd support (#2902) 2023-01-05 08:39:00 +01:00
Makefile tests: Adjusted files not directly related to tests 2023-01-06 16:50:09 +13:00
README.md docs(fix): README - Update CI status badge URL (#2951) 2022-12-24 02:32:06 +13:00
setup.sh setup.sh: Remove __err function (#2876) 2022-10-31 10:46:00 +01:00
VERSION chore: Update changelog and version (#2944) 2022-12-22 23:27:40 +01:00

Docker Mailserver

ci::status docker::pulls documentation::badge

A production-ready fullstack but simple mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.). Only configuration files, no SQL database. Keep it simple and versioned. Easy to deploy and upgrade. Documentation via MkDocs.

Originally created by @tomav, docker-mailserver is now maintained by volunteers since January 2021.

If you have issues, read the full README and the documentation for your version (default is edge) first before opening an issue. The issue tracker is for issues, not for personal support.

  1. Included Services
  2. Issues and Contributing
  3. Requirements
  4. Usage
  5. Examples
  6. Environment Variables
  7. Documentation
  8. Release Notes

Included Services

Requirements

Recommended:

  • 1 Core
  • 2GB RAM
  • Swap enabled for the container

Minimum:

  • 1 vCore
  • 512MB RAM
  • You'll need to deactivate some services like ClamAV to be able to run on a host with 512MB of RAM. Even with 1G RAM you may run into problems without swap, see FAQ.

Usage

Available Images / Tags - Tagging Convention

CI/CD will automatically build, test and push new images to container registries. Currently, the following registries are supported:

  1. DockerHub
  2. GitHub Container Registry

All workflows are using the tagging convention listed below. It is subsequently applied to all images.

Event Image Tags
push on master edge
push tag 1.2.3, 1.2, 1, latest

Get the Tools

Since Docker Mailserver 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 <CONTAINER NAME> setup <COMMAND> instead. If you're new to docker-mailserver, it is recommended to use the script setup.sh for convenience.

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"
wget "${DMS_GITHUB_URL}/setup.sh"
chmod a+x ./setup.sh

Create a docker-compose Environment

  1. Install the latest docker-compose
  2. Edit docker-compose.yml to your liking
    • substitute mail (hostname) and example.com (domainname) according to your FQDN
    • if you want to use SELinux for the ./docker-data/dms/config/:/tmp/docker-mailserver/ mount, append -z or -Z
  3. 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 🚫)

Note: If you're using podman, make sure to read the related documentation

Get up and running

First Things First

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:

  1. ./setup.sh help which includes the options of setup.sh.
  2. 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.

If no docker-mailserver 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:

  1. Use setup.sh: ./setup.sh email add <user@domain>
  2. Run the command directly in the container: docker exec -ti <CONTAINER NAME> setup email add <user@domain>

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 <user@domain> [<password>]

# and configure aliases, DKIM and more
./setup.sh [-Z] alias add postmaster@<domain> <user@domain>

Miscellaneous

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 '<domain.tld>[,<domain2.tld>]'

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 the documentation for a detailed explanation.

Updating docker-mailserver

Make sure to read the CHANGELOG before updating to new versions, to be prepared for possible breaking changes.

docker-compose pull
docker-compose down
docker-compose up -d mailserver

You should see the new version number on startup, for example: [ TASKLOG ] Welcome to docker-mailserver 10.1.2.

You're done! And don't forget to have a look at the remaining functions of the setup.sh script with ./setup.sh help.

Supported Operating Systems

We are currently providing support for Linux. Windows is not supported and is known to cause problems. Similarly, macOS is not officially supported - but you may get it to work there. In the end, Linux should be your preferred operating system for this image, especially when using this mail-server in production.

Bare Domains

If you want to use a bare domain (hostname == domainname), see FAQ.

Support for Multiple Domains

docker-mailserver supports multiple domains out of the box, so you can do this:

./setup.sh email add user1@docker.example.com
./setup.sh email add user1@mail.example.de
./setup.sh email add user1@server.example.org

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.

Ports

See the documentation for further details and best practice advice, especially regarding security concerns.

Mailboxes (aka IMAP Folders)

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.

Examples

With Relevant Environmental Variables

This example provides you only with a basic example of what a minimal setup could look like. We strongly recommend that you go through the configuration file yourself and adjust everything to your needs. The default docker-compose.yml can be used for the purpose out-of-the-box, see the usage section.

version: '3.8'

services:
  mailserver:
    image: docker.io/mailserver/docker-mailserver:latest
    container_name: mailserver
    hostname: mail
    domainname: 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
      - SPAMASSASSIN_SPAM_TO_INBOX=1
      - ENABLE_CLAMAV=1
      - ENABLE_FAIL2BAN=1
      - ENABLE_POSTGREY=1
      - ENABLE_SASLAUTHD=0
      - ONE_DIR=1
    cap_add:
      - NET_ADMIN
    restart: always

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!

version: '3.8'

services:
  mailserver:
    image: docker.io/mailserver/docker-mailserver:latest
    container_name: mailserver
    hostname: mail
    domainname: 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
      - SPAMASSASSIN_SPAM_TO_INBOX=1
      - ENABLE_CLAMAV=1
      - ENABLE_FAIL2BAN=1
      - ENABLE_POSTGREY=1
      - ONE_DIR=1
      - ENABLE_LDAP=1 # with the :edge tag, use ACCOUNT_PROVISIONER
      - 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
      - POSTFIX_MESSAGE_SIZE_LIMIT=100000000
    cap_add:
      - NET_ADMIN
    restart: always