Everything else in this repo shows how to send mail from a script or app.
This page is different: it’s for making your whole Debian/Ubuntu system
— cron jobs, mail/sendmail, monitoring tools like Monit/logwatch,
apt/unattended-upgrades failure notices — relay through ZeroSMTP instead
of trying (and usually failing) to deliver mail directly.
Get your credentials first: register and activate an account at
msgwing.com, then copy your @msgwing.com login and
password.
Postfix is Debian’s and most Ubuntu servers’ default MTA, so this is almost
always the right starting point if Postfix is already installed
(dpkg -l postfix).
sudo apt install -y postfix mailutils libsasl2-modules
During install, choose “Satellite system” when debconf asks (or run
sudo dpkg-reconfigure postfix afterwards to change it) — this tells
Postfix “don’t try to deliver mail yourself, send everything to a relay.”
Create the credentials file:
sudo tee /etc/postfix/sasl_passwd > /dev/null <<'EOF'
[mx.msgwing.com]:587 your-username@msgwing.com:your-password
EOF
sudo chmod 600 /etc/postfix/sasl_passwd
sudo postmap /etc/postfix/sasl_passwd
Before editing main.cf, check which lookup table type your system
actually uses — this changed across Debian/Ubuntu releases, and using the
wrong one will make Postfix fail to start:
postconf default_database_type
Add to /etc/postfix/main.cf (replace hash below with whatever
default_database_type printed, e.g. lmdb on newer releases):
relayhost = [mx.msgwing.com]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = encrypt
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
For port 465 (implicit SSL/TLS) instead of 587 (STARTTLS), use
relayhost = [mx.msgwing.com]:465 and add
smtp_tls_wrappermode = yes (port 465 needs TLS immediately, not a
STARTTLS upgrade — smtp_tls_security_level = encrypt alone isn’t enough
on that port).
Apply and test:
sudo systemctl restart postfix
echo "Test from Postfix satellite" | mail -s "ZeroSMTP test" you@example.com
tail -f /var/log/mail.log
Good if you don’t want a full MTA — just a way for cron/scripts to send
mail via the standard sendmail command.
sudo apt install -y msmtp msmtp-mta mailutils
/etc/msmtprc (or ~/.msmtprc for a single user):
defaults
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile /var/log/msmtp.log
account zerosmtp
host mx.msgwing.com
port 587
tls_starttls on
auth on
user your-username@msgwing.com
password your-password
from your-username@msgwing.com
account default : zerosmtp
For port 465, set port 465 and tls_starttls off (implicit TLS starts
immediately on connect, no STARTTLS upgrade). Restrict permissions since
the password is in plain text: sudo chmod 600 /etc/msmtprc.
msmtp-mta provides /usr/sbin/sendmail, so anything that already calls
sendmail/mail (cron, logwatch, etc.) picks this up automatically.
Do not use ssmtp for this — it’s unmaintained (orphaned since 2019,
removed from Debian testing in 2024); msmtp is its official replacement.
If your system already runs Exim4 instead of Postfix:
sudo apt install -y exim4
sudo dpkg-reconfigure exim4-config
In the wizard, choose “mail sent by smarthost; no local mail”, and set
the smarthost to mx.msgwing.com::587 (the double colon forces that exact
port). Then add credentials:
sudo tee -a /etc/exim4/passwd.client > /dev/null <<'EOF'
mx.msgwing.com:your-username@msgwing.com:your-password
EOF
sudo chmod 640 /etc/exim4/passwd.client
sudo update-exim4.conf
sudo systemctl restart exim4
Once system mail routes through ZeroSMTP (via the Postfix/msmtp/Exim4 setup
above), you can wire any systemd unit to send an email the moment it fails,
using systemd’s own OnFailure= directive — no extra monitoring agent
needed.
/etc/systemd/system/notify-failure@.service (a reusable template unit):
[Unit]
Description=Email notification for failed unit %i
[Service]
Type=oneshot
ExecStart=/usr/bin/mail -s "Unit %i failed on %H" you@example.com
Then add OnFailure= to whichever unit you want watched, e.g.
/etc/systemd/system/my-backup.service:
[Unit]
Description=My backup job
OnFailure=notify-failure@%n.service
sudo systemctl daemon-reload
Test it by forcing a failure (sudo systemctl start --job-mode=fail
my-nonexistent.service or a script that exits non-zero) and confirming the
notification email arrives.
Rolling this out to more than a handful of servers by hand doesn’t scale — here’s a minimal Ansible playbook for the Postfix satellite setup above:
---
- hosts: all
become: true
vars:
zerosmtp_username: "your-username@msgwing.com"
# Keep the real password in ansible-vault or your inventory's secrets
# file — never commit it in plain text alongside the playbook.
zerosmtp_password: ""
tasks:
- name: Install Postfix and dependencies
apt:
name: [postfix, mailutils, libsasl2-modules]
state: present
- name: Configure Postfix as a satellite system
lineinfile:
path: /etc/postfix/main.cf
regexp: '^relayhost ='
line: "relayhost = [mx.msgwing.com]:587"
create: true
notify: Restart postfix
- name: Set SASL auth options
blockinfile:
path: /etc/postfix/main.cf
block: |
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = encrypt
notify: Restart postfix
- name: Deploy SASL credentials
copy:
content: "[mx.msgwing.com]:587 :\n"
dest: /etc/postfix/sasl_passwd
mode: '0600'
notify:
- Postmap sasl_passwd
- Restart postfix
handlers:
- name: Postmap sasl_passwd
command: postmap /etc/postfix/sasl_passwd
- name: Restart postfix
service:
name: postfix
state: restarted
Run with ansible-playbook -i inventory.ini zerosmtp-relay.yml --ask-vault-pass
(or however your vault secret is normally supplied).
Use check-connection.sh first to confirm the
network path to mx.msgwing.com is open before debugging the MTA config
itself — see TROUBLESHOOTING.md if it isn’t.