--- title: "How to Send Email Alerts with mailx and Postfix on Ubuntu" description: "Set up mailx with a send-only Postfix relay on Ubuntu 24.04 so scripts, cron jobs and root mail reach your inbox through an authenticated SMTP provider." category: "Monitoring Logging" language: en source: https://cubepath.com/docs/monitoring-logging/email-alerts-configuration-with-mailx updated: 2026-09-25 --- # How to Send Email Alerts with mailx and Postfix on Ubuntu The `mail` command (mailx) lets scripts and cron jobs send email with a single line, which makes it the simplest way to get alerts out of a server. On its own it only hands the message to a local mail transfer agent, so you also need a correctly configured Postfix that relays mail through an authenticated SMTP provider. In this tutorial you will install mailx and a send-only Postfix relay on Ubuntu 24.04, route system mail for `root` to your inbox, and use it from a script and from cron. ## Prerequisites To follow this guide you need: - A server running Ubuntu 24.04 LTS, for example a CubePath VPS. Debian 12 uses the same packages. - A non-root user with `sudo` privileges. - SMTP credentials from an email provider or transactional email service: host name, port (usually `587`), user name and password or API key. Examples: `smtp.gmail.com` with a Google app password, or `smtp.sendgrid.net` with the user `apikey` and an API key. - A sender address on a domain your provider allows you to send from, referred to as `alerts@your_domain`. > Note: Sending directly to recipients on port 25 from a server IP usually ends up in spam or is blocked. Relaying through an authenticated provider on port 587 avoids both problems. ## Step 1 - Installing Postfix as a send-only relay Install Postfix and the SASL modules it needs to authenticate against the provider: ```bash sudo apt update sudo apt install postfix libsasl2-modules ``` The installer opens a configuration dialog: 1. For **General mail configuration type**, choose **Satellite system**. 2. For **System mail name**, keep the server host name (for example `web01.your_domain`). 3. For **SMTP relay host**, enter your provider in the form `[smtp.example.com]:587`. The square brackets tell Postfix to connect to that host directly instead of looking up MX records. 4. Accept the defaults for the remaining questions. If you skipped the dialog, run `sudo dpkg-reconfigure postfix` to open it again. Check that Postfix is running: ```bash systemctl is-active postfix ``` ```output active ``` ## Step 2 - Configuring SMTP authentication and TLS Store the provider credentials in a map file. The host and port must match the `relayhost` value exactly, including the brackets: ```bash sudo nano /etc/postfix/sasl_passwd ``` ```text [smtp.example.com]:587 your_smtp_user:your_smtp_password ``` Protect the file and compile it into the lookup table Postfix reads: ```bash sudo chmod 0600 /etc/postfix/sasl_passwd sudo postmap /etc/postfix/sasl_passwd ``` `postmap` creates `/etc/postfix/sasl_passwd.db`, which inherits the restricted permissions. Now enable authentication, require TLS for the connection to the relay, and make sure Postfix only listens on localhost so the server can never act as an open relay: ```bash sudo postconf -e \ 'relayhost = [smtp.example.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' \ 'inet_interfaces = loopback-only' ``` Changing `inet_interfaces` requires a full restart, not a reload: ```bash sudo systemctl restart postfix ``` Confirm the effective settings: ```bash postconf relayhost smtp_tls_security_level inet_interfaces ``` ```output relayhost = [smtp.example.com]:587 smtp_tls_security_level = encrypt inet_interfaces = loopback-only ``` ## Step 3 - Rewriting the sender address By default, mail sent by `root` goes out as `root@web01.your_domain`, which most providers reject or mark as spam because it is not a verified sender. A generic map rewrites local addresses to your real sender address when mail leaves the server: ```bash sudo nano /etc/postfix/generic ``` Replace `web01.your_domain` with the system mail name you set in Step 1: ```text @web01.your_domain alerts@your_domain ``` Compile the map and enable it: ```bash sudo postmap /etc/postfix/generic sudo postconf -e 'smtp_generic_maps = hash:/etc/postfix/generic' sudo systemctl reload postfix ``` ## Step 4 - Installing mailx and sending a test message Ubuntu offers several `mail` implementations. `bsd-mailx` is small and provides both `mail` and `mailx`: ```bash sudo apt install bsd-mailx ``` Send a test message. Replace `you@example.com` with the address that should receive alerts: ```bash echo "Test message from $(hostname) at $(date)" | mail -s "Test from $(hostname)" you@example.com ``` `mail` returns immediately because Postfix queues the message. Check the Postfix log to confirm delivery to the relay: ```bash sudo journalctl -t postfix/smtp -n 5 --no-pager ``` ```output Sep 25 15:10:42 web01 postfix/smtp[5123]: 4XyZ1c2d3e: to=, relay=smtp.example.com[203.0.113.25]:587, delay=1.2, delays=0.02/0.01/0.6/0.57, dsn=2.0.0, status=sent (250 2.0.0 OK) ``` `status=sent` means the provider accepted the message. Check your inbox, including the spam folder the first time. If the status is `deferred` or `bounced`, the text in parentheses explains why; see the Troubleshooting section. To set a display name or reply address, add headers with `-a`: ```bash echo "Disk check passed" | mail -s "Daily report" -a "From: Server Alerts " you@example.com ``` ## Step 5 - Forwarding mail for root Many system tools (cron, unattended-upgrades, smartd, mdadm) send their warnings to `root`. Forward that mail to a real mailbox by editing the aliases file: ```bash sudo nano /etc/aliases ``` Add or change the `root` line: ```text postmaster: root root: you@example.com ``` Rebuild the aliases database: ```bash sudo newaliases ``` Test it by writing to `root`: ```bash echo "Root alias test" | mail -s "Root alias test" root ``` The message should arrive at `you@example.com`, and the log shows `to=, orig_to=`. ## Step 6 - Sending alerts from scripts and cron With mail working, any script can send an alert by piping text to `mail`. The following example checks the root filesystem and sends an email when usage passes 90 percent: ```bash sudo nano /usr/local/sbin/disk-alert ``` ```bash #!/usr/bin/env bash set -euo pipefail LIMIT=90 RECIPIENT="you@example.com" used="$(df --output=pcent / | tail -n 1 | tr -dc '0-9')" if (( used >= LIMIT )); then { echo "Root filesystem on $(hostname) is ${used}% full (limit ${LIMIT}%)." echo df -h / echo echo "Largest directories under /var:" du -xh --max-depth=1 /var 2>/dev/null | sort -rh | head -n 10 } | mail -s "[ALERT] $(hostname): disk ${used}% full" "$RECIPIENT" fi ``` Make it executable and schedule it hourly in root's crontab: ```bash sudo chmod 0755 /usr/local/sbin/disk-alert sudo crontab -e ``` ```text MAILTO=you@example.com 0 * * * * /usr/local/sbin/disk-alert ``` The `MAILTO` line is useful on its own: cron emails any output or error a job prints to that address. A script that stays silent on success and writes to stderr on failure gets alerting for free, without calling `mail` at all. To test the script without filling the disk, run it once with a lower limit by temporarily editing `LIMIT=1`, then set it back. ## Troubleshooting Inspect queued messages with `mailq` and retry them after fixing the cause with `sudo postqueue -f`. The most common errors in `journalctl -t postfix/smtp` are: **`SASL authentication failed` or `535 Authentication failed`**: the user or password in `/etc/postfix/sasl_passwd` is wrong, or the provider requires an app password or API key. Fix the file, run `sudo postmap /etc/postfix/sasl_passwd` again and reload Postfix. **`no mechanism available`**: the `libsasl2-modules` package is missing. Install it and restart Postfix. **`Connection timed out` to the relay**: outbound traffic on port 587 is blocked by a firewall. Test with `nc -vz smtp.example.com 587`. **`550 Sender address rejected` or `553`**: the provider does not accept the sender. Check that `/etc/postfix/generic` maps to an address or domain verified in your provider account. **`mail: command not found`**: install `bsd-mailx` as in Step 4. ## Conclusion Your server now relays mail through an authenticated SMTP provider over TLS, rewrites the sender to a verified address, forwards system mail for `root`, and lets any script or cron job send alerts with `mail`. As next steps, publish SPF and DKIM records for `your_domain` as your provider describes to improve deliverability, add `MAILTO` to existing cron jobs, and consider a webhook or chat channel for alerts that need faster attention than email.