Configure and Verify Postfix tlsproxy for STARTTLS
tlsproxy is the internal Postfix service that actually terminates STARTTLS, and it is not something you start by hand or hand a listening port to. This guide checks what certificate and policy it will use, sets it deliberately, reloads Postfix, and tests STARTTLS from a real client.
The route
Jump straight to the step you need, or tick off Done means at the end.
Uses Postfix 3.8.6 from the installed package. Allow about twenty minutes, plus a maintenance window on a production mail service. You need root or equivalent privilege for configuration and reload, a certificate and private key already deployed on the host, and a hostname and port where Postfix accepts SMTP.
Security boundary
Tlsproxy handles untrusted network clients and private key material. Do not paste private keys into a shell, loosen their permissions to make a test pass, or reload a production MTA without checking the proposed values first.
1. Confirm the installed service and version
Start read-only. Neither of these needs sudo:
$ command -v postconf
/usr/sbin/postconf
$ postconf mail_version
mail_version = 3.8.6
$ postconf -M | rg '^tlsproxy' || echo 'no tlsproxy entry in active master.cf'
no tlsproxy entry in active master.cf
The service line depends on the local master.cf. This host has no active tlsproxy entry, hence the message. If your configuration is meant to use tlsproxy, inspect the package's master.cf and the active config_directory before changing anything. Postfix master normally owns the tlsproxy process; you should not.
Checkpoint
Record the reported version and whether the service is enabled. Do not infer a running configuration from a different Postfix installation or a copied manpage.
2. Inspect the values tlsproxy inherits
Server-side tlsproxy settings clone the SMTP server settings. On a fresh configuration, the certificate, private key and TLS security level commonly resolve through $smtpd_... parameters. Ask for both the effective value and the built-in default:
$ for p in tlsproxy_tls_security_level tlsproxy_tls_cert_file tlsproxy_tls_key_file tlsproxy_watchdog_timeout; do
> printf '%s = ' "$p"
> postconf -h "$p"
> done
tlsproxy_tls_security_level = $smtpd_tls_security_level
tlsproxy_tls_cert_file = $smtpd_tls_cert_file
tlsproxy_tls_key_file = $smtpd_tls_key_file
tlsproxy_watchdog_timeout = 10s
Those are parameter expressions, not proof the referenced SMTP settings point at usable files. Resolve the chain explicitly:
$ postconf -h smtpd_tls_security_level smtpd_tls_cert_file smtpd_tls_key_file
<your configured security level>
<your certificate path>
<your private-key path>
Your output is host-specific; on this machine the paths point to the deployed certificate for server.example.com. An empty certificate path is not a reason to invent one: find where your certificate deployment actually puts the files, and whether the Postfix service account can read them once the master process has loaded them.
3. Choose the server TLS policy deliberately
Set only the tlsproxy-specific values that need to differ from the SMTP server. This gives the proxy an explicit certificate, key and mandatory TLS policy:
# sudo postconf -e 'tlsproxy_tls_cert_file=/etc/postfix/tls/mail.example.test.fullchain.pem'
# sudo postconf -e 'tlsproxy_tls_key_file=/etc/postfix/tls/mail.example.test.key.pem'
# sudo postconf -e 'tlsproxy_tls_security_level=encrypt'
encrypt makes TLS mandatory for the tlsproxy server role. If your deployment intentionally uses opportunistic encryption, use that policy instead and check it against your organisation's requirements. Do not copy encrypt onto a live system without considering clients that cannot do STARTTLS.
Postfix can load the key before dropping privileges, so keep it readable only by root:
# sudo stat -c '%U:%G %a %n' /etc/postfix/tls/mail.example.test.key.pem
root:root 600 /etc/postfix/tls/mail.example.test.key.pem
If another service manages the certificate and key, use its own renewal and permission mechanism. Do not replace a key manually while a renewal process might be writing the same path.
4. Validate before reloading
Check the resulting values and ask Postfix to validate its configuration. None of this opens a listener or alters the running service:
$ postconf -h tlsproxy_tls_security_level tlsproxy_tls_cert_file tlsproxy_tls_key_file
encrypt
/etc/postfix/tls/mail.example.test.fullchain.pem
/etc/postfix/tls/mail.example.test.key.pem
# sudo postfix check
# echo $?
0
postfix check can report missing files, ownership problems and other configuration errors. Treat any non-zero result as a full stop: check path, format and permissions, then rerun. Do not solve a permission error with chmod 644 on a private key.
Recovery before reload: changed a parameter and decided against it? Remove the override and let the inherited value apply again:
# sudo postconf -X tlsproxy_tls_security_level
# sudo postconf -X tlsproxy_tls_cert_file
# sudo postconf -X tlsproxy_tls_key_file
Only use postconf -X on parameters you deliberately set. Save the previous output first if you need to restore an intentional local value rather than remove it.
5. Reload Postfix and check the logs
Reloading affects the running mail service. Once the checks pass, apply it:
# sudo postfix reload
tlsproxy processes can live for a while under load, so a main.cf change is not necessarily visible to every existing process right away; the manpage recommends postfix reload to speed adoption up. Then watch the log using whatever facility your host uses:
$ sudo journalctl -u postfix --since '5 minutes ago' --no-pager
# or, on a syslog-based host:
$ sudo tail -n 50 /var/log/mail.log
Look for certificate-loading errors, TLS handshake failures and permission errors. tlsproxy reports through syslogd or postlogd; the exact destination is host-dependent.
6. Test the live SMTP STARTTLS path
Run this from a client that can reach the SMTP listener. It is an ordinary client test, but it opens a real network connection to the mail service:
$ openssl s_client -starttls smtp \
> -connect mail.example.test:25 \
> -servername mail.example.test \
> -crlf
CONNECTED(00000003)
...
New, TLSv1.3, Cipher is ...
...
250 ...
Do not treat that output as exact: OpenSSL, the negotiated protocol and the server greeting all vary. What matters is that the connection reaches the expected host, STARTTLS completes, and the certificate names the hostname you used. Need certificate verification? Add the CA options for your trust store rather than ignoring verification errors.
A successful handshake does not prove every Postfix SMTP policy is correct. Confirm the effective values, check the logs, and test from the network paths your clients actually use. If a test fails after a reload, compare configured paths with postconf -h, verify the key and certificate match, and read the first relevant log error rather than reloading repeatedly.
Done means
- Version confirmed: Postfix reports the expected version and a managed
tlsproxyservice. - Policy checked: the effective certificate, key and TLS policy were verified with
postconf -h. - Config validated:
postfix checkreturned status 0 before the reload. - Key stays protected: the private key was never made world-readable for troubleshooting.
- Reload clean: Postfix was reloaded deliberately, and its logs show no new tlsproxy or TLS errors.
- Live test passed: a real SMTP client completed STARTTLS and got the expected certificate.