Setting Up Postfix for Outgoing Mail Only
My servers never receive mail, but they send plenty: cron output, monitoring alerts, password resets, backup reports. I once left a "temporary" full mail server running on a web box and never want to do that again. What you want is a send-only setup (a null client, or satellite system): Postfix on localhost, accepting mail from local processes and relaying it through an authenticated smarthost.
I'll do it on Ubuntu 18.04 with the relay option, since most cloud providers block or throttle outbound port 25 and a relay through a real provider keeps you out of spam folders. It works on Debian 9 too, and CentOS 7 uses the same main.cf settings with different package commands. You need SMTP credentials for a provider accepting submission on 587 with STARTTLS, and a hostname with a valid A record (some receivers reject a HELO name that doesn't resolve).
Install
sudo apt update
sudo apt install -y postfix mailutils libsasl2-modules
The installer asks for a configuration type. Choose Satellite system if you will relay, or Internet Site if you will deliver directly, and enter the server's fully qualified hostname as the mail name. Either choice can be corrected afterwards in main.cf.
Lock it to localhost
Edit /etc/postfix/main.cf and make sure these settings are present. postconf -e edits the file safely and is easier than hunting for the lines:
sudo postconf -e 'inet_interfaces = loopback-only'
sudo postconf -e 'inet_protocols = ipv4'
sudo postconf -e 'mydestination ='
sudo postconf -e 'myhostname = app01.example.com'
sudo postconf -e 'myorigin = $myhostname'
sudo postconf -e 'mynetworks = 127.0.0.0/8'
sudo postconf -e 'local_transport = error:local delivery is disabled'
inet_interfaces = loopback-only is the important one: Postfix binds to 127.0.0.1 only, so no outside host can connect to port 25. An empty mydestination tells Postfix it is not the final destination for any domain, and the local_transport line makes attempts at local delivery bounce with a clear error instead of piling up in /var/mail.
Relay host and credentials
sudo postconf -e 'relayhost = [smtp.mailprovider.example]:587'
sudo postconf -e 'smtp_sasl_auth_enable = yes'
sudo postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
sudo postconf -e 'smtp_sasl_security_options = noanonymous'
sudo postconf -e 'smtp_tls_security_level = encrypt'
sudo postconf -e 'smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt'
The square brackets around the relay hostname disable MX lookup, which is what you want for a submission server. smtp_tls_security_level = encrypt refuses to send if STARTTLS is unavailable.
Create the credentials file and build its hash map:
sudo nano /etc/postfix/sasl_passwd
[smtp.mailprovider.example]:587 username:password
sudo postmap /etc/postfix/sasl_passwd
sudo chmod 600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db
Sender addresses and root's mail
Local processes send mail as root@app01.example.com or www-data@app01.example.com, which many relays reject unless the domain is verified. A canonical map rewrites the envelope and header sender to an address the provider accepts:
sudo postconf -e 'sender_canonical_maps = regexp:/etc/postfix/sender_canonical'
sudo postconf -e 'sender_canonical_classes = envelope_sender, header_sender'
sudo nano /etc/postfix/sender_canonical
/.+/ noreply@example.com
Also make sure root's mail goes somewhere a human reads by editing /etc/aliases:
root: admin@example.com
sudo newaliases
sudo systemctl restart postfix
Send a test
echo "Test from $(hostname)" | mail -s "Postfix relay test" you@example.com
sudo tail -f /var/log/mail.log
A successful relay logs a line ending in status=sent (250 2.0.0 OK ...) with relay=smtp.mailprovider.example[...]:587. Then confirm nothing is listening externally:
sudo ss -tlnp | grep :25
The only entry should be 127.0.0.1:25. From another host, nc -zv app01.example.com 25 must fail.
When mail doesn't leave
SASL authentication failed; server smtp.mailprovider.example said: 535 5.7.8 Authentication failed. Wrong credentials, or thesasl_passwdkey does not matchrelayhostexactly (brackets and port included). Rerunpostmapafter every edit; Postfix reads the.dbfile, not the text.warning: SASL authentication failure: No worthy mechs found. Thelibsasl2-modulespackage is missing, so Postfix has no PLAIN or LOGIN mechanism. Install it and restart Postfix.status=deferred (connect to smtp.mailprovider.example[203.0.113.25]:587: Connection timed out). Outbound 587 is blocked by a firewall or security group.nc -zv smtp.mailprovider.example 587from the server confirms it.Certificate verification failed for smtp.mailprovider.example.smtp_tls_CAfileis wrong or theca-certificatespackage is outdated.apt install --reinstall ca-certificatesand confirm the path exists.
Mail sits in /var/spool/postfix while deferred and retries for five days by default; mailq lists what is waiting and postqueue -f retries immediately after you fix a configuration problem. I check mailq first whenever an alert goes missing.