A flexible and modern Docker image for a Postfix mail server, based on Debian Trixie.
POSTFIX_* environment variables — no config files to edit.submission (587) and smtps (465) with TLS and SASL authentication out of the box.postfix-ldap and postfix-mysql for user lookups, virtual mailboxes and aliases.docker-entrypoint.d/ to extend the startup sequence.postfix check and postfix upgrade-configuration on every start.HEALTHCHECK, handles signals for graceful shutdown.docker run -d --name postfix \
-e POSTFIX_myhostname=mail.example.com \
-e POSTFIX_mydomain=example.com \
-e POSTFIX_mynetworks="127.0.0.0/8" \
-p 25:25 \
-p 587:587 \
-p 465:465 \
ricariel/postfix
On container start, the entrypoint executes all scripts inside /docker-entrypoint.d/
in lexicographic order via run-parts, then starts Postfix:
| Script | Purpose |
|---|---|
10-copy | Copies LDAP .cf files from /config/ldap/ into /etc/postfix/ldap/ |
20-setup-master | Uncomments and enables submission (587) and smtps (465) in master.cf |
25-postconf | Translates all POSTFIX_* environment variables into main.cf settings |
30-maintenance | Clears stale PID files, runs postfix check and postfix upgrade-configuration |
99-end | Signals the end of initialisation |
Custom scripts added via derived images are sorted into this sequence by their filename prefix.
ENTRYPOINT_QUIET_LOGSSuppresses the startup log output of the entrypoint script itself. Postfix's own logs (via maillog_file) are unaffected.
-e ENTRYPOINT_QUIET_LOGS=1
POSTFIX_* — Postfix main.cf settingsEvery environment variable with the POSTFIX_ prefix is applied to /etc/postfix/main.cf
at startup. The prefix is stripped; the remainder is used as the Postfix parameter name (case-sensitive).
Set a value:
-e POSTFIX_myhostname=mail.example.com
-e POSTFIX_mynetworks="127.0.0.0/8 10.0.0.0/8"
Delete / disable a setting (empty value comments it out in main.cf):
-e POSTFIX_relayhost=
POSTFIX_* variablesCore identity
| Variable | Postfix parameter | Example value |
|---|---|---|
POSTFIX_myhostname | myhostname | smtp.example.com |
POSTFIX_mydomain | mydomain | example.com |
POSTFIX_mydestination | mydestination | $myhostname |
POSTFIX_myorigin | myorigin | $mydomain |
Network
| Variable | Postfix parameter | Example value |
|---|---|---|
POSTFIX_inet_interfaces | inet_interfaces | all |
POSTFIX_inet_protocols | inet_protocols | all |
POSTFIX_mynetworks | mynetworks | 127.0.0.0/8 10.0.0.0/8 |
Security note: Keep
mynetworksas narrow as possible. Hosts listed here can relay mail without authentication.
Relay
| Variable | Postfix parameter | Example value |
|---|---|---|
POSTFIX_relayhost | relayhost | [smtp.relay.com]:587 |
POSTFIX_virtual_transport | virtual_transport | lmtp:dovecot:24 |
Virtual domains and mailboxes
| Variable | Postfix parameter | Description |
|---|---|---|
POSTFIX_virtual_mailbox_domains | virtual_mailbox_domains | Domains hosted on this server |
POSTFIX_virtual_mailbox_maps | virtual_mailbox_maps | Map addresses to mailbox paths |
POSTFIX_virtual_alias_maps | virtual_alias_maps | Forwarding and alias map |
TLS — inbound (smtpd)
| Variable | Postfix parameter | Example value |
|---|---|---|
POSTFIX_smtpd_tls_cert_file | smtpd_tls_cert_file | /etc/postfix/certs/tls.crt |
POSTFIX_smtpd_tls_key_file | smtpd_tls_key_file | /etc/postfix/certs/tls.key |
POSTFIX_smtpd_tls_security_level | smtpd_tls_security_level | may |
POSTFIX_smtpd_tls_protocols | smtpd_tls_protocols | >=TLSv1.2 |
POSTFIX_smtpd_tls_mandatory_protocols | smtpd_tls_mandatory_protocols | >=TLSv1.2 |
POSTFIX_smtpd_tls_ciphers | smtpd_tls_ciphers | high |
POSTFIX_smtpd_tls_loglevel | smtpd_tls_loglevel | 1 |
POSTFIX_smtpd_tls_session_cache_database | smtpd_tls_session_cache_database | btree:/var/spool/postfix/smtpd_scache |
TLS — outbound (smtp)
| Variable | Postfix parameter | Example value |
|---|---|---|
POSTFIX_smtp_tls_cert_file | smtp_tls_cert_file | /etc/postfix/certs/tls.crt |
POSTFIX_smtp_tls_key_file | smtp_tls_key_file | /etc/postfix/certs/tls.key |
POSTFIX_smtp_tls_security_level | smtp_tls_security_level | dane |
POSTFIX_smtp_tls_protocols | smtp_tls_protocols | >=TLSv1.2 |
POSTFIX_smtp_tls_loglevel | smtp_tls_loglevel | 1 |
POSTFIX_smtp_tls_policy_maps | smtp_tls_policy_maps | MTA-STS socketmap URI |
POSTFIX_smtp_dns_support_level | smtp_dns_support_level | dnssec (required for DANE) |
POSTFIX_smtp_tls_session_cache_database | smtp_tls_session_cache_database | btree:/var/spool/postfix/smtp_scache |
SASL authentication via Dovecot
| Variable | Postfix parameter | Example value |
|---|---|---|
POSTFIX_smtpd_sasl_type | smtpd_sasl_type | dovecot |
POSTFIX_smtpd_sasl_path | smtpd_sasl_path | inet:dovecot:8745 |
POSTFIX_smtpd_sasl_auth_enable | smtpd_sasl_auth_enable | yes |
POSTFIX_smtpd_tls_auth_only | smtpd_tls_auth_only | yes |
Milter integration (Rspamd, ClamAV-milter, etc.)
| Variable | Postfix parameter | Example value |
|---|---|---|
POSTFIX_smtpd_milters | smtpd_milters | inet:rspamd:11332 |
POSTFIX_non_smtpd_milters | non_smtpd_milters | inet:rspamd:11332 |
POSTFIX_milter_default_action | milter_default_action | accept |
Rate limiting
| Variable | Postfix parameter | Example value |
|---|---|---|
POSTFIX_smtpd_client_connection_rate_limit | smtpd_client_connection_rate_limit | 50 |
POSTFIX_smtpd_client_message_rate_limit | smtpd_client_message_rate_limit | 100 |
POSTFIX_smtpd_client_recipient_rate_limit | smtpd_client_recipient_rate_limit | 50 |
POSTFIX_anvil_rate_time_unit | anvil_rate_time_unit | 60s |
Miscellaneous
| Variable | Postfix parameter | Example value |
|---|---|---|
POSTFIX_message_size_limit | message_size_limit | 52428800 (50 MB) |
POSTFIX_maillog_file | maillog_file | /dev/stdout |
POSTFIX_masquerade_domains | masquerade_domains | example.com |
For the full list of Postfix parameters, see the Postfix documentation.
| Port | Protocol | Purpose |
|---|---|---|
25/tcp | SMTP | Server-to-server mail transfer (inbound MX) |
587/tcp | Submission | Authenticated client mail submission (STARTTLS + SASL) |
465/tcp | SMTPS | Legacy wrapper-mode TLS for older clients (SASL) |
/config/ldap/Mount a directory containing Postfix LDAP lookup table configuration files (.cf files).
They are copied into /etc/postfix/ldap/ at startup by 10-copy.
docker run -d --name postfix \
-v /path/to/ldap-configs:/config/ldap:ro \
...
ricariel/postfix
Example main.cf references:
virtual_mailbox_maps = ldap:/etc/postfix/ldap/virtual_mailbox.cf
virtual_alias_maps = ldap:/etc/postfix/ldap/aliases.cf
smtpd_sender_login_maps = ldap:/etc/postfix/ldap/sender_login.cf
A minimal LDAP lookup file looks like this:
server_host = ldaps://ldap.example.com:636
bind = yes
bind_dn = cn=postfix,ou=services,dc=example,dc=com
bind_pw = secret
search_base = ou=users,dc=example,dc=com
query_filter = (mail=%s)
result_attribute = mail
Mount your certificate and key files and point Postfix to them via environment variables:
docker run -d --name postfix \
-v /etc/letsencrypt/live/smtp.example.com:/etc/postfix/certs:ro \
-e POSTFIX_smtpd_tls_cert_file=/etc/postfix/certs/fullchain.pem \
-e POSTFIX_smtpd_tls_key_file=/etc/postfix/certs/privkey.pem \
...
ricariel/postfix
Postfix stores its mail queue under /var/spool/postfix/. For persistence across container
restarts (e.g. to avoid losing queued messages on restart), mount a volume:
-v postfix-spool:/var/spool/postfix
In addition to LDAP, postfix-mysql is included for MySQL/MariaDB-based setups.
See example_sql_map.md for example database schemas and query configurations
covering virtual mailboxes, aliases, forwarding, and catch-all rules.
Any executable file placed in /docker-entrypoint.d/ of a derived image is automatically
executed at startup. Use the numeric prefix to control execution order relative to the
built-in scripts (10, 20, 25, 30, 99).
FROM ricariel/postfix
# Runs between 25-postconf and 30-maintenance
COPY --chmod=755 28-my-custom-setup.sh /docker-entrypoint.d/
The ONBUILD instruction in this image's Dockerfile automatically copies
docker-entrypoint.d/* from the child image's build context, so no explicit
COPY is needed if you place the scripts in the same directory.
The default CMD is postfix start-fg. You can override it for debugging:
docker run --rm -it ricariel/postfix bash
services:
postfix:
image: ricariel/postfix
restart: unless-stopped
ports:
- "25:25"
- "587:587"
- "465:465"
volumes:
- ./ldap:/config/ldap:ro
- ./certs:/etc/postfix/certs:ro
- postfix-spool:/var/spool/postfix
environment:
POSTFIX_myhostname: smtp.example.com
POSTFIX_mydomain: example.com
POSTFIX_mynetworks: "127.0.0.0/8"
POSTFIX_virtual_mailbox_domains: "example.com"
POSTFIX_virtual_transport: "lmtp:dovecot:24"
POSTFIX_smtpd_tls_cert_file: /etc/postfix/certs/tls.crt
POSTFIX_smtpd_tls_key_file: /etc/postfix/certs/tls.key
POSTFIX_smtpd_tls_security_level: may
POSTFIX_smtpd_sasl_type: dovecot
POSTFIX_smtpd_sasl_path: "inet:dovecot:8745"
POSTFIX_smtpd_sasl_auth_enable: "yes"
POSTFIX_smtpd_milters: "inet:rspamd:11332"
POSTFIX_non_smtpd_milters: "inet:rspamd:11332"
POSTFIX_maillog_file: /dev/stdout
volumes:
postfix-spool:
This image ships Postfix 3.10.x (Debian Trixie). If you are migrating from a Bookworm-based setup (Postfix 3.7.x), be aware of the following behavioral changes introduced across releases 3.8–3.10:
| Change | Impact |
|---|---|
permit_naked_ip_address, check_relay_domains, reject_maps_rbl removed | Postfix refuses to start if any of these appear in smtpd_*_restrictions. Remove them from your config. |
/usr/lib/postfix/configure-instance.sh is now internal-only | The script exits with code 1 when called directly. Postfix invokes it automatically as part of start-fg. Do not call it from entrypoint scripts. |
smtpd_forbid_unauth_pipelining = yes now default | Clients that pipeline SMTP commands without RFC 2920 authorization are immediately disconnected. Affects broken senders. |
| Bare newline / SMTP smuggling defenses enabled by default | Non-$mynetworks clients that do not send proper <CR><LF>.<CR><LF> will stall. |
MySQL charset changed from latin1 → utf8mb4 | Existing postfix-mysql lookup tables may need charset conversion. |
postconf now warns if a # comment appears on the same line as a parameter value — move
inline comments to their own line.This project is licensed under the MIT License. See the LICENSE file for details.
Content type
Image
Digest
sha256:061f7574b…
Size
96.7 MB
Last updated
about 11 hours ago
docker pull ricariel/postfix