In May 1998, Wietse Venema released the first public alpha of a mailer he’d been writing inside IBM Research and originally called Vmailer. Sendmail was the dominant MTA, its monolithic sendmail.cf looked like a regex got into a fight with a Lisp interpreter, and security advisories arrived faster than dependencies could be patched. Postfix was the response to that mess: a multi-process design where each daemon does one small job with the smallest possible privileges. Twenty-seven years later, it still runs on roughly a third of the public SMTP servers on the open internet, and the new 3.11 series shipped on 5 March 2026 with post-quantum TLS, structured TLS reporting, and a fresh round of crypto-knob spring-cleaning.

This is a tour of what’s actually new in Postfix 3.11, why post-quantum key exchange in SMTP is not just security theatre, and how the deb.myguard.nl Postfix package and the matching eilandert/postfix container build it. There is config you can paste, history you can blame, and one moderately bad joke about Sendmail. Buckle in.
A very short history of why Postfix exists
Sendmail, written by Eric Allman in 1981, was the canonical Unix MTA for fifteen years. It also had an exploit surface that read like a recipe book: setuid root, monolithic, with a configuration file generated from m4 macros that no human could review by eye. Through the mid-1990s, CERT advisories naming Sendmail averaged one every few months. Wietse Venema, who had a track record of writing security-grade software (the original tcp_wrappers shipped with most Linux distributions), spent his sabbatical at IBM Research building an MTA that started from a different first principle: nothing runs as root that doesn’t have to.
The Postfix architecture is a bunch of little daemons under the master process, each chrooted, each running as the unprivileged postfix user, each talking to its neighbours over a private socket. The SMTP server (smtpd) accepts mail. The cleanup daemon rewrites headers. The queue manager (qmgr) decides what gets delivered next. The SMTP client (smtp) ships mail outbound. If any one of them gets pwned, the blast radius is the privileges that one daemon had — which is almost nothing. Compare that to a monolithic MTA where a parser bug means root.
The trade-off is that Postfix talks to itself a lot. A single inbound message touches at minimum five processes. On modern hardware this is invisible. On a Pentium II in 1999 it was a real concern, and the design choice paid off only because Venema is paranoid about memcpy. Performance matters, but not as much as not getting owned by a malformed SMTP envelope.
What is actually new in Postfix 3.11
Postfix’s release cadence is glacial by modern software standards, and that’s a feature. Wietse ships a new stable train roughly once a year. The 3.10 line landed in February 2025 and introduced TLSRPT (more on that below). 3.11.0 went out on 5 March 2026, with 3.11.1, 3.11.2 and 3.11.3 following over the next two months. The headline items:
- Default
smtp_tls_security_level = may. Whencompatibility_levelis 3.11 or higher and Postfix was built with TLS support, outbound mail attempts opportunistic TLS by default. For decades the default was “no TLS unless you set it”, which made sense in 2002 and was embarrassing by 2025. New compatibility level, new default. - Deletion of
smtpd_tls_eecdh_gradeand friends. The old “strong/ultra” curve-grade settings were superseded bytls_eecdh_auto_curvesin Postfix 3.6 and quietly ignored ever since. 3.11 deletes them outright. If you still havesmtpd_tls_eecdh_grade = ultrainmain.cf,postconfnow warns and tells you to remove it. - Post-quantum TLS via OpenSSL 3.5. The infrastructure landed in the 3.10 cycle, but 3.11 is the first stable release where it sees actual use. You list hybrid KEM groups in
tls_eecdh_auto_curvesand the SMTP TLS handshake negotiates X25519MLKEM768 when both ends support it. - Berkeley DB phase-out. Debian and several other distributions are removing Berkeley DB. Postfix 3.11 ships migration helpers — a stand-alone reindexer service, automatic
$default_database_typesubstitution inalias_maps, and a longNON_BERKELEYDB_READMEappendix on the migration. The package on deb.myguard.nl defaults tocdbfor lookups andlmdbfor caches, which dodges the issue entirely. - SQLite quoting safety. The SQLite map client now logs a warning when a query uses double-quoted string literals instead of single quotes. Double-quoting in SQLite is a footgun that bypasses SQL injection protection; getting a loud warning beats a silent compromise.
- TLSRPT support hardened. The 3.10 line introduced it. 3.11 added routine logging of TLSRPT success and failure events, an option to suppress reports for reused TLS sessions, and a library API version check so a mismatched
libtlsrptwon’t silently misbehave. - Lots of small portability and bugfix work. Three Bugfix entries credit “Claude Opus 4.6” as the finder, which is the first time I’ve seen an LLM credited in upstream Postfix history. The robots are reading
proxymap.c. Make of that what you will.
Post-quantum TLS in SMTP: why now?
Here’s the threat model in one sentence. A sufficiently large quantum computer with enough qubits can run Shor’s algorithm against the discrete log problem that ECDHE and RSA-KEX rely on, and recover the symmetric session key from a recorded handshake. That computer does not yet exist. But state-level adversaries are recording encrypted traffic today on the bet that the computer will exist within fifteen years, and at that point everything captured today gets decrypted. The industry calls this “harvest now, decrypt later”, and it’s the reason every cryptography body on Earth started shipping post-quantum algorithms in late 2024.
The standardised winner from NIST is ML-KEM (Module-Lattice-Based Key Encapsulation Mechanism), formerly known as Kyber, formally specified in FIPS 203 in August 2024. ML-KEM-768 (the medium security parameter set) is the version that ended up everywhere — Cloudflare, Google, Apple, OpenSSH 9.9, and OpenSSL 3.5. Crucially, no one runs ML-KEM alone. Lattice cryptography is young, and a bug in the lattice could break ML-KEM the way the lattice CRYSTALS-Dilithium signature scheme had a near-miss last year. So every deployment uses a hybrid: classical X25519 + ML-KEM-768 in parallel, and the session key is derived from both. The handshake breaks only if both schemes do.
In TLS 1.3, the hybrid groups have IANA-registered names. The two that matter for Postfix are X25519MLKEM768 (code point 0x11EC) and SecP256r1MLKEM768 (0x11EB). Both ship in OpenSSL 3.5 (April 2025) and are picked up automatically by Postfix’s TLS layer once you list them in tls_eecdh_auto_curves. The classical curves go after them as a fallback, in case the remote MTA is still running an OpenSSL old enough to lack PQ support.

The Postfix configuration to enable it is two lines:
tls_eecdh_auto_curves = X25519MLKEM768 SecP256r1MLKEM768 X25519 prime256v1 secp384r1 tls_ffdhe_auto_groups = ffdhe3072 ffdhe4096
Test the handshake with a one-liner against your own MTA. -groups X25519MLKEM768 forces the client to offer only that group. If the server picks it, you get Negotiated TLS1.3 group: X25519MLKEM768 in the output. If the server does not support it, the handshake fails outright (which is the point of the test — you want a hard signal, not a silent downgrade):
openssl s_client -connect mail.example.com:25 -starttls smtp \ -groups X25519MLKEM768 2>&1 | grep Negotiated
One important caveat. SMTP is opportunistic. Two MTAs that don’t share a single PQ group will still negotiate a classical curve and the mail still flows. That’s the right behaviour for an MTA — better to deliver mail on a classical curve than reject it because the recipient hasn’t upgraded yet. The PQ benefit is therefore population-wide and gradual: every server that adds the groups raises the percentage of traffic that’s recorded-but-uncrackable. We’re maybe 4% of inter-MTA traffic right now. By 2028, it’ll probably be most of it.
Anti-spam in Postfix: postscreen, access restrictions, and where rspamd takes over
Postfix has three layers of spam defence that run before the message even hits the queue. Each rejects a different category, and each is cheap because it happens before any disk write.
Postscreen: the bouncer at the front door
postscreen is a separate daemon that listens on port 25 instead of smtpd, and its only job is to decide whether the connecting IP deserves to talk to a real SMTP server at all. It scores the connection against weighted DNSBLs, watches for protocol violations (pre-greet pipelining, bare newlines, malformed commands), and either drops the connection or hands it off to smtpd. The deciding rule is the postscreen_dnsbl_threshold score — anything at or above the threshold gets a 521 disconnect.
The trick is choosing DNSBLs that are alive and not noisy. The mail filter landscape has thinned dramatically since 2020. SORBS shut down in June 2024 when Proofpoint pulled the funding. WPBL went away around 2021. The Lashback UBL is gone. Several SpamRats zones return wildcards now. If your postscreen_dnsbl_sites still lists any of those, you’re paying DNS lookup latency for zero benefit.
A defensible 2026 postscreen list, scoped tight to “really worst offenders only”:
postscreen_dnsbl_threshold = 3 postscreen_dnsbl_sites = zen.spamhaus.org*3, dnsbl.dronebl.org*2, truncate.gbudb.net*2, z.mailspike.net=127.0.0.2*100, list.dnswl.org=127.0.[0..255].[1]*-4, list.dnswl.org=127.0.[0..255].[2]*-6, list.dnswl.org=127.0.[0..255].[3]*-9
Spamhaus Zen combines SBL (the manually curated naughty list, which includes the EDROP feed of hijacked netblocks), XBL (botnet C2), and PBL (dynamic IP ranges that should never be sending mail directly). Dronebl is botnet-only. Truncate.gbudb.net is extremely conservative — it lists only IPs with a substantial pattern of repeat abuse. Mailspike’s z. zone gets weight 100 because a single hit is dispositive. And the negative-weight dnswl entries can rescue a borderline-suspect-but-legitimate sender.
If you already run a heavy content filter behind Postfix — and you should — you can drop Spamhaus from the postscreen list entirely. The rspamd RBL module queries Zen at content-scan time anyway. Letting rspamd handle reputation lookups centralises policy and avoids redundant DNS. See the deep dive on rspamd for what it can do that postscreen cannot.
smtpd access restrictions: envelope sanity
Once a connection has cleared postscreen, smtpd‘s access restrictions kick in. These are Postfix’s terminology for what the rest of the world sometimes calls “anti-spoofing” rules. They look at the SMTP envelope — the HELO argument, the MAIL FROM, the RCPT TO — and reject obvious forgeries before the DATA phase. The big ones:
reject_invalid_helo_hostnameandreject_non_fqdn_helo_hostname— drops clients that sayHELO fooinstead of their actual FQDN. Real MTAs always send a proper FQDN.reject_unknown_sender_domain— looks up the sender domain in DNS. If it has no A, MX or AAAA record, the address can’t possibly receive a bounce. Drop the message.reject_non_fqdn_senderandreject_non_fqdn_recipient— refusesMAIL FROM:<bob@local>and similar bareword addresses.reject_unauth_destination— the rule that makes you not an open relay. If we are not the final destination for the recipient and the sender isn’t inmynetworksor SASL-authenticated, refuse to relay. This single line is the difference between “mail server” and “spammer’s tool”.reject_unauth_pipelining— clients that send multiple SMTP commands in one TCP packet before negotiatingPIPELININGare almost certainly spam bots trying to dump payload faster than the server can refuse it.disable_vrfy_commandandstrict_rfc821_envelopes— close off the username-enumeration vector and reject envelopes that don’t use angle brackets properly.
Together these eight rules reject something like a fifth of all spam attempts before DATA ever runs, which is the cheapest spam to refuse. None of them involve content inspection. None require DKIM verification. They’re pure envelope sanity, and they’re free.

TLSRPT: SMTP finally gets a delivery receipt for crypto
TLSRPT (TLS Reporting, RFC 8460) is the answer to a question nobody could previously answer: are remote MTAs actually using TLS when they deliver mail to me? For two decades the answer was “look at the Received: headers and hope”. TLSRPT publishes a TXT record at _smtp._tls.example.com with an email or HTTPS endpoint, and remote MTAs send daily JSON reports listing every TLS handshake (and every failed one) for traffic destined to your domain. You get aggregated visibility into who’s downgrading, who’s tripping over expired certs, and who’s still on TLS 1.0.
Postfix 3.10 was the first stable release with TLSRPT support, built against Wietse’s own libtlsrpt. 3.11 added the smtp_tlsrpt_skip_reused_handshakes knob (default yes) so you don’t drown in reports for connection-reused sessions that don’t have new TLS data to report. The library API version is now checked at startup, which catches the case where Postfix was built against one libtlsrpt and is running against a newer incompatible one.
If you’re the recipient publishing the TXT record, the report ingestion problem is yours, not Postfix’s — Postfix is the sending side that generates reports. Tools like parsedmarc handle the aggregation side. Worth it if you operate a domain that actually receives mail at volume; mostly noise if you’re a personal server.
Milters: the surgical content-filter API
Sendmail introduced milters (mail filters) in 2002 as a stable plugin ABI for content scanning. The protocol speaks over a Unix or TCP socket; the MTA streams envelope and headers and body to the milter, and the milter responds with verdicts (accept, reject, quarantine, modify headers, rewrite body). Postfix implemented the same protocol in version 2.3 (2006). The result is that anything that speaks milter — and a lot does — works with both MTAs.
The big four in modern deployments:
- OpenDKIM — signs outbound mail with your DKIM key, verifies inbound DKIM signatures. Postfix asks it for a verdict per message; OpenDKIM hands back a verified-or-not result that ends up in the
Authentication-Results:header. - OpenDMARC — applies the DMARC policy (your domain’s
_dmarcTXT record) on inbound mail. Quarantine, reject, or pass depending on what the sender’s DNS says. - rspamd — connects as a milter on port 11332 and runs the entire content-filter pipeline (Bayesian, neural net, RBL, URIBL, OLEFY, DCC, Razor, Pyzor, neural) before
smtpdtakes theDATAphase. See the rspamd article for what’s actually inside that pipeline. - Amavis / ClamAV — virus and macro-payload scanning. Slower than rspamd, but it scans MIME attachments deeply and catches malware that pure-text reputation systems miss.
Postfix lets you chain milters in a comma-separated list, with order-of-execution semantics. Common practice: OpenDKIM and OpenDMARC first (they’re cheap header checks), rspamd second (the heavy lifter), Amavis last (slowest, and you don’t want to scan with ClamAV mail that rspamd already rejected). The configuration on a typical mailscreen:
smtpd_milters = inet:127.0.0.1:8891, inet:127.0.0.1:8893, inet:rspamd:11332 non_smtpd_milters = $smtpd_milters milter_default_action = tempfail milter_protocol = 6
milter_default_action = tempfail is important. If a milter goes down, you want Postfix to issue a 4xx temporary failure so the remote MTA queues and retries, not a 5xx hard bounce. Setting it to accept means broken milters silently let everything through, which is exactly the security regression you’d expect.
How the deb.myguard.nl Postfix package is built
The postfix binary on deb.myguard.nl is built from upstream 3.11.3 source, on Debian trixie and Ubuntu resolute, with a hardened toolchain configuration that’s worth describing because it shows what “secure compilation” actually means in 2026:
DEB_BUILD_MAINT_OPTIONS = hardening=+all optimize=+lto future=+lfs DEB_CPPFLAGS_MAINT_APPEND = -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 DEB_CFLAGS_MAINT_APPEND = -O3 -fno-plt -fstack-clash-protection -fcf-protection=full DEB_LDFLAGS_MAINT_APPEND = -Wl,-z,now -Wl,-z,relro -Wl,-z,noexecstack -Wl,-O1 -Wl,--as-needed
What that buys you, in plain terms:
FORTIFY_SOURCE=3— the compiler emits bounds-checking wrappers aroundmemcpy,strcpy,sprintfand friends. Level 3 catches more than level 2 by using whole-program object-size analysis.-fstack-clash-protection— prevents stack-overflow attacks that jump past the guard page.-fcf-protection=full— Intel CET (Control-flow Enforcement Technology) and AMD SHSTK markers. Hardware-enforced shadow stack, where the CPU itself refuses returns to a hijacked address. Requires Tiger Lake / Zen 3 or newer to actually fire, but the binaries are tagged for it on older CPUs and ignored.-z now -z relro -z noexecstack— fully bind all dynamic symbols at load time, mark the GOT read-only after relocation, and forbid stack execution. The trifecta that turns most write-what-where bugs into mere crashes.-O3 -fno-plt— aggressive optimization plus no procedure-linkage-table indirection. Skip the PLT and let the linker resolve calls directly through the GOT; smaller dispatch path, marginal perf win.
Beyond compilation, the package ships a hardened default main.cf with the TLS settings from this article already in place (TLSv1.2+ floor, PFS-only cipher list, X25519MLKEM768 hybrid PQ, NO_COMPRESSION / NO_RENEGOTIATION / NO_TICKET, all the smtpd access restrictions). Install on Debian trixie or Ubuntu resolute with:
echo "deb [trusted=yes] https://deb.myguard.nl trixie main" \ > /etc/apt/sources.list.d/myguard.list apt update apt install postfix postfix-ldap postfix-mysql postfix-pcre postfix-pgsql postfix-sqlite
The eilandert/postfix Docker image
For people who don’t want to install Postfix on the host, the eilandert/postfix image on Docker Hub (Debian and Ubuntu variants) wraps the same package in a container that boots in about 400 milliseconds and weighs 277 MB. The Dockerfile installs the myguard Postfix plus its map modules, populates /etc/postfix.orig with the hardened defaults, and runs a bootstrap.sh that copies them into the volume-mounted /etc/postfix on first run. After that, the container respects whatever you put in the volume.
The compose file is essentially:
services:
postfix:
image: eilandert/postfix:debian
container_name: postfix
restart: always
ports:
- 25:25/tcp
- 587:587/tcp
volumes:
- ./config/postfix:/etc/postfix:rw
- ./data/postfix:/var/lib/postfix:rw
- /etc/letsencrypt:/etc/letsencrypt:ro
environment:
- TZ=Europe/Amsterdam
A few container-specific touches. The image preloads libmimalloc-secure.so via LD_PRELOAD for the security-hardened allocator (you can switch to jemalloc or none via the MALLOC env var). It runs postfix reload in a daily loop in the background so renewed Let’s Encrypt certs get picked up automatically. And it forwards logs either to stdout (which Docker captures) or to a remote syslog if you set SYSLOG_HOST. The whole thing is on the dockerized monorepo at github.com/eilandert/dockerized.
For the full mail-server stack — Postfix plus Dovecot for IMAP, rspamd for filtering, DKIM keys, DMARC reporting — see the Postfix + Dovecot setup guide. It walks the whole pipeline end to end. This article was mostly about what’s new in 3.11 itself; that one is about how to make it actually deliver mail.
Frequently Asked Questions
Does Postfix 3.11 actually require OpenSSL 3.5 for post-quantum TLS?
For X25519MLKEM768 and SecP256r1MLKEM768, yes. OpenSSL 3.5 (April 2025) is the first stable release with hybrid ML-KEM groups in TLS 1.3. Postfix doesn’t carry its own crypto; it asks OpenSSL to negotiate. If the system OpenSSL is older, the PQ group names in tls_eecdh_auto_curves are silently ignored and the handshake falls through to whatever classical curves are left in the list. That’s why the recommended setting puts PQ groups first and classical X25519 / P-256 / P-384 second — older peers degrade gracefully.
Is opportunistic TLS in SMTP secure against active attackers?
No. Opportunistic STARTTLS is downgrade-attackable by anyone who can inject TCP — they strip the STARTTLS response from the EHLO answer and the client falls back to plaintext. The fix is DANE (DNSSEC + TLSA records) or MTA-STS (a published HTTPS policy file). DANE is technically stronger because the TLSA record is signed; MTA-STS is easier to deploy because you don’t need DNSSEC. Postfix supports both. For a public-facing MTA, you want at least one of them active.
What’s the difference between postscreen and smtpd access restrictions?
Postscreen is the front door — it decides whether the connecting IP gets to talk to a real SMTP server at all, based on DNSBL scoring and protocol-violation heuristics. It does all this in one daemon, before smtpd is even involved. Smtpd access restrictions run later, once the connection has cleared postscreen, and operate on the SMTP envelope itself (HELO name, MAIL FROM, RCPT TO). Postscreen is cheaper because it never spins up a per-connection smtpd process for connections it kills; smtpd restrictions can do things postscreen can’t, like reject based on the recipient address. Use both.
Is the eilandert/postfix Docker image production-ready?
It’s what’s running the production mailscreen at myguard.nl, so by that definition yes. It’s a small wrapper around the deb.myguard.nl Postfix package with a bootstrap script that respects volume-mounted config. The image is rebuilt automatically when the underlying package version moves. The main things you bring to it are a real cert (mount /etc/letsencrypt read-only), a real main.cf (mount your config dir), and a milter setup for content filtering (rspamd typically). Don’t run it without those.
Do I need to enable TLSRPT to use the new Postfix?
No. TLSRPT is opt-in for both the sender and the receiver. As a sending MTA, Postfix only emits reports when it sees a recipient domain that publishes a TLSRPT TXT record. As a receiver, you only see reports if you publish the record and run an ingestion pipeline. Most operators ignore TLSRPT entirely and the world keeps spinning. It’s most useful if you’re operating a domain that receives substantial mail volume and you want visibility into how remote MTAs are actually delivering it.
Should I disable Berkeley DB on my Postfix install?
If you’re on Debian trixie or later, the answer is “Debian is going to do it for you”. The Postfix package on deb.myguard.nl already defaults to cdb for lookup tables and lmdb for caches, so the migration is essentially done by the time you install. If you have an existing install with hash: or btree: tables in your main.cf, the Postfix 3.11 NON_BERKELEYDB_README walks the conversion. The short version: run postmap -F cdb:/etc/postfix/transport to rebuild each table in the new format, then update the type prefix in main.cf.
Further reading
- Postfix + Dovecot Mail Server Setup on Debian 12 and 13 — the full stack walkthrough, with DKIM, SPF, DMARC, virtual mailboxes and a 10/10 mail-tester score.
- Rspamd Explained: How Modern Spam Filtering Actually Works — what the rspamd milter is doing under the hood (Bayesian, neural, RBLs, URIBLs, OLEFY).
- Docker Hardening: Rootless, Read-only, Distroless — what to do to the eilandert/postfix container if you want to lock it down further.
- postconf(5) — the upstream reference for every parameter mentioned in this post.
- RFC 8460 (TLSRPT) and FIPS 203 (ML-KEM) if you want to read the standards instead of trusting my paraphrases.