# Docker Mailserver: a Postfix and Dovecot stack in one container

> Docker Mailserver bundles Postfix, Dovecot, Rspamd, ClamAV and Fail2ban into a single image configured by files instead of a database. It suits engineers who want a self-hosted MX they can version, and it is the wrong tool for anyone who wants a web admin panel.

**docker-mailserver/docker-mailserver** — Production-ready fullstack but simple mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.) running inside a container.

- Repository: https://github.com/docker-mailserver/docker-mailserver
- Website: https://docker-mailserver.github.io/docker-mailserver/latest/
- Stars: 18,885 · Forks: 2,048
- Language: Shell
- License: MIT
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/docker-mailserver-docker-mailserver

## The problem Docker Mailserver solves: a full mail stack without a database

Running your own mail server means assembling Postfix, Dovecot, an antispam layer, an antivirus scanner, DKIM signing, DMARC checking, a greylister and a ban daemon, then keeping their configuration consistent across upgrades. Docker Mailserver ships that assembly as one container image and makes the whole configuration file-based. The README states the design goal plainly: "Only configuration files, no SQL database. Keep it simple and versioned. Easy to deploy and upgrade." That single sentence is the reason to pick it over a database-backed control panel.

The audience is narrow and worth naming. This is for engineers who already understand what an MX record, a SASL mechanism and a DKIM selector are, and who would rather commit a directory of config files than click through a web UI. The project is maintained by volunteers, and the README says so directly: it was originally created by @tomav and "is now maintained by volunteers since January 2021". It is not archived, and the last push to the default branch was on 2026-09-14, with v16.0.1 released on 2026-09-04. The README also warns that the issue tracker is for issues, not personal support, which is a fair signal about where the project draws its boundary.

## What is actually inside the container

The README lists the included services by name: Postfix for SMTP with SMTP or LDAP authentication, Dovecot with SASL, IMAP, POP3, LDAP, basic Sieve support and quotas, Rspamd, Amavis, SpamAssassin with custom rules, ClamAV with automatic updates, OpenDKIM and OpenDMARC, Fail2ban, Fetchmail, Getmail6, Postscreen and Postgrey. Certificates can come from LetsEncrypt, from manual files or be self-signed. SASLauthd handles LDAP authentication, and OAuth2 is supported through the XOAUTH2 or OAUTHBEARER SASL mechanisms.

That is a lot of daemons in one container, and the image build reflects it. The Dockerfile defines three stages, stage-base, stage-main and stage-final, and comments that this is "in preparation for more granular stages (eg ClamAV and Fail2Ban split into their own)". ClamAV signature data is copied from the official ClamAV image rather than downloaded during the build, because the Dockerfile notes that running freshclam at build time "would require an extra memory of 500MB+". A cron entry in the image refreshes signatures later with freshclam.

The practical consequence is that the container is not small and its memory floor is set by whichever scanners you enable. The Makefile shows how the maintainers run a lightweight instance for testing, and the environment variables it disables are a good map of what you can turn off: ENABLE_CLAMAV=0, ENABLE_AMAVIS=0, ENABLE_RSPAMD=0, ENABLE_OPENDKIM=0, ENABLE_OPENDMARC=0, ENABLE_POLICYD_SPF=0 and ENABLE_SPAMASSASSIN=0. A mail server with all of those off is still a working SMTP and IMAP server, which is worth remembering when you are sizing a host.

## Docker Mailserver setup: compose file, volumes and the first account

The repository ships a compose.yaml at the top level. It uses the image ghcr.io/docker-mailserver/docker-mailserver:latest, sets container_name to mailserver, and reads environment variables from mailserver.env, which is also in the repository root. The hostname is the value you must change first, and the comment in the file is explicit about why: "Provide the FQDN of your mail server here (Your DNS MX record should point to this value)".

```yaml
services:
  mailserver:
    image: ghcr.io/docker-mailserver/docker-mailserver:latest
    container_name: mailserver
    hostname: mail.example.com
    env_file: mailserver.env
    ports:
      - "25:25"    # SMTP  (explicit TLS => STARTTLS, Authentication is DISABLED => use port 465/587 instead)
      - "143:143"  # IMAP4 (explicit TLS => STARTTLS)
      - "465:465"  # ESMTP (implicit TLS)
      - "587:587"  # ESMTP (explicit TLS => STARTTLS)
      - "993:993"  # IMAP4 (implicit TLS)
```

The port comments carry a security decision that people miss. Port 25 accepts mail from other servers and does not offer authentication, so clients must use 465 or 587. Ports 143 and 993 are IMAP, with 993 being the implicit TLS variant. The compose file also mounts five volumes, and the split matters: /var/mail holds mail data, /var/mail-state holds state such as the Fail2ban database, /var/log/mail holds logs, and /tmp/docker-mailserver is where your configuration and accounts live. Losing the last one loses your accounts.

```yaml
    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
```

Fail2ban needs the NET_ADMIN capability, and the compose file leaves that line commented out next to the note "Uncomment if using ENABLE_FAIL2BAN=1". The image defines a HEALTHCHECK internally, and the compose file notes that Docker Compose should default to using dms-healthcheck.

Once the container is up, account management goes through setup.sh in the repository root. The Makefile shows the invocation used for a local test instance, which adds a postmaster account with a password:

```bash
./setup.sh email add postmaster@example.test 123
```

Run setup.sh against the running container; it is the documented interface for adding accounts and other maintenance, and the README links a dedicated documentation page for it. If you would rather not expose the setup script, the same configuration directory mounted at /tmp/docker-mailserver can be edited directly, since the project's whole premise is that configuration is files.

## Where Docker Mailserver is the wrong choice

There is no web administration interface. Nothing in the README lists one, and the related searches asking for a "docker mailserver gui" have no answer inside this project. If your team needs a non-engineer to create mailboxes or reset passwords through a browser, this is the wrong tool and no amount of documentation reading will change that.

Webmail is also absent. The container provides IMAP and POP3 endpoints, not a mail client. You supply Roundcube, SnappyMail or a desktop client yourself, and you are then responsible for that second service's TLS, updates and authentication path.

Fail2ban is a subtler case. It is listed as an included service, but the compose file ships with the NET_ADMIN capability commented out, so a default deployment that enables Fail2ban without uncommenting those lines will not have the capability it needs. That is a configuration trap rather than a bug, but it is the kind of thing you only notice when you go looking for bans that never happened.

Finally, the volunteer maintenance model is a real constraint. The README asks you to search the documentation for your version before opening an issue, and to make sure the documentation version matches your image version. Upgrades are documented in the FAQ under updating, but the README does not document rollback, so plan your volume backups before you pull a new tag rather than after.

## Docker Mailserver compared with Mailcow, Mailu and Stalwart

The comparison that matters is architectural, not feature-count. Docker Mailserver is a single container whose state lives in mounted directories and whose configuration is files. Mailcow, which appears in the search data as the most common comparison, takes the opposite approach: it is a multi-container stack with a database-backed web UI. The practical difference is that Mailcow gives you a browser console and Docker Mailserver gives you a git repository. Neither is better in the abstract; they fail different people.

Mailu sits between the two, also multi-container, and is the second comparison people search for. Stalwart is a different kind of alternative again: rather than wrapping Postfix and Dovecot, it is a mail server written as its own implementation. If you want to avoid the Postfix configuration model entirely, that is the relevant difference, though it also means the configuration knowledge you carry over from Postfix does not transfer.

One thing Docker Mailserver does that a database-backed stack does not: because accounts live in files under /tmp/docker-mailserver, you can diff them, review them in a pull request, and restore them from a backup without a database dump. If your team already treats infrastructure as versioned text, that property is worth more than a UI.

## Licence and the cost of staying current

Docker Mailserver is MIT licensed, and the LICENSE file sits at the repository root. MIT is permissive, so you can run it commercially and modify it, but the image bundles third-party components including Postfix, Dovecot, ClamAV, SpamAssassin and Rspamd, each under its own licence. Those are separate obligations from the MIT grant covering this project's own code, and if you redistribute the image you should check them individually. This is not legal advice; read the bundled licences yourself if redistribution is part of your plan.

The upgrade cost is mostly yours, not the project's. Configuration is mounted from the host, so a new image tag does not silently rewrite your settings, which is the good news. The bad news is that the documentation is versioned and the README insists you match it to your image version, which means an upgrade is a documentation-reading exercise as much as a docker pull. The release cadence visible in the changelog shows a large jump from v15.1.0 in August 2025 to v16.0.0 in August 2026, so major versions do arrive and are worth reading about before you move. Back up the config, mail-state and mail-data volumes, then change the image tag. The README does not describe a rollback path, so the backup is your rollback.

## Conclusion

Adopt Docker Mailserver if you are comfortable with Postfix concepts, want configuration under version control, and can run it on a host with a static IP and a reverse DNS record you control. Do not adopt it if you need a browser admin panel or per-mailbox webmail out of the box; the project ships neither, and the README points at its documentation for everything beyond the container itself. Verify three things before you commit: that your provider allows inbound port 25, that you have set the hostname to the FQDN matching your MX record, and that you understand which ports in compose.yaml are meant to be public.

## FAQ

### What are the key differences between Mailcow and Docker Mailserver?

Docker Mailserver runs as a single container configured entirely by files, with no SQL database, as its README states. Mailcow is the database-backed, multi-container alternative that people most often compare it against, so the choice comes down to whether you want a versioned config directory or a web console.

### How do I install Docker Mailserver?

The repository provides compose.yaml and mailserver.env at the top level. You set hostname to the FQDN your MX record points at, keep the volume mounts, and start the service with the image ghcr.io/docker-mailserver/docker-mailserver:latest. The README directs you to the documentation for initial setup guidance.

### How do I add a user to Docker Mailserver?

The setup.sh script in the repository root handles account management. The Makefile shows the form ./setup.sh email add postmaster@example.test 123 for adding an account with a password, run against the running container.

### Can I set up a self-hosted email server using Docker?

Yes, that is what Docker Mailserver is for. It packages Postfix, Dovecot, Rspamd, ClamAV and the other services listed in the README into one container, and the shipped compose.yaml mounts mail data, state, logs and configuration on the host so they survive container replacement.

### What is the best free mail server?

That depends on your constraints, and only one option is described here. Docker Mailserver is MIT licensed and free to run, but it has no web admin panel or webmail, so if those are requirements you would be comparing it against a database-backed stack such as Mailcow or Mailu.

## Sources

- [docker-mailserver/docker-mailserver on GitHub](https://github.com/docker-mailserver/docker-mailserver)
- [License: MIT](https://github.com/docker-mailserver/docker-mailserver/blob/master/LICENSE)
- [Project website](https://docker-mailserver.github.io/docker-mailserver/latest/)
- [README](https://github.com/docker-mailserver/docker-mailserver/blob/master/README.md)
- [Releases](https://github.com/docker-mailserver/docker-mailserver/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/docker-mailserver-docker-mailserver
