Skip to content

Send Email Alerts from Bash

emailalertmailx
5 min read

Quick Answer

The mail command sends email from the command line when a mail transfer agent (MTA) is configured on the system. The pattern is: echo "message body" | mail -s "Subject line" recipient@example.com. This works with any monitoring script — disk checks, uptime monitors, backup confirmations — by piping the alert text into mail and specifying the subject and recipient. On Ubuntu and Debian, install the tools with sudo apt install mailutils. For servers that block outbound SMTP (most cloud VPS providers do), configure msmtp to relay through an external SMTP service like Gmail or SendGrid using an app password. The rate-limiting pattern — a lockfile that prevents duplicate alerts within a cooldown window — stops your inbox from filling up when a problem persists across multiple cron runs. The mail command is the one dependency here: install mailutils on Ubuntu and Debian or mailx on Fedora before the script will send.

I had a disk check that worked perfectly. It read the usage, compared it to a threshold, decided things were getting tight — and then did absolutely nothing with that conclusion except append a line to a log file that no human, me very much included, was ever going to open. So when the disk did fill, the script had technically known for hours and had told no one. A monitor that monitors in silence isn't a monitor; it's a diary.

That's the gap this snippet closes, and it's more common than it sounds. Writing the detection logic feels like the hard part, so you stop there, and the alerting — the bit that actually changes your behavior — never gets built. The fix is small: pipe the message into mail so a threshold breach lands in your inbox instead of a logfile. The catch that bites people is environment. Under cron the PATH is minimal and mail may resolve to nothing, or deliver only to a local mailbox you never check, so an alert you "sent" never arrives. The script below sends a real one — with the host, the time, and the top disk consumers in the body — so the next time something goes red, I find out from my inbox, not from a crash. Finding out from a crash instead of from an inbox was a lesson I only needed once.

The alert script that finally got my attention

Save as disk-email-alert.sh (or any name). Set EMAIL and THRESHOLD, install mailutils, then run or cron it.

bash
#!/bin/bash CHECK="✓" CROSS="✗" # --- Configuration --- THRESHOLD=80 EMAIL="you@example.com" HOSTNAME=$(hostname) DATE=$(date '+%Y-%m-%d %H:%M:%S') # --- Check disk usage on root partition --- USAGE=$(df / | awk 'NR==2{print $5}' | tr -d '%') echo "[$DATE] Disk usage: ${USAGE}%" if [ "$USAGE" -gt "$THRESHOLD" ]; then echo "$CROSS Disk at ${USAGE}% — sending alert to $EMAIL" MESSAGE="[DISK ALERT] ${HOSTNAME} Disk usage on / is at ${USAGE}% as of ${DATE}. Threshold: ${THRESHOLD}% Top disk consumers: $(du -sh /* 2>/dev/null | sort -rh | head -5) -- BashSnippets monitor" echo "$MESSAGE" | mail -s "[ALERT] Disk Space on ${HOSTNAME}" "$EMAIL" echo "$CHECK Alert sent to $EMAIL" else echo "$CHECK Disk OK at ${USAGE}%" fi

When the threshold trips

The script logs the percentage, fills MESSAGE with host, time, and du output, sends one email via mail -s …, and prints confirmation. Below is how each piece fits together.

How the message gets built and sent

HOSTNAME=$(hostname) runs the hostname command once and stores the machine name in HOSTNAME, so subject lines and the body clearly say which server had the problem (especially useful if you run the same script on several VPS hosts).

The multi-line MESSAGE variable uses double quotes so Bash expands variables like ${USAGE} and embedded command substitution ($(du …)) when the assignment runs. Literal newlines inside the quoted string appear as real line breaks in the email body.

du -sh /* estimates size per top-level directory (human-readable sizes with -h). Piping through sort -rh and head -5 surfaces the worst offenders inside the alert.

echo "$MESSAGE" | mail -s "[ALERT] Disk Space on ${HOSTNAME}" "$EMAIL" sends standard input as the body, -s sets the subject, and the last argument is the recipient. That is the common mailutils pattern on Ubuntu when local/SMTP delivery is configured.

Important notes

You must have mailutils installed: sudo apt install mailutils

Getting mail working end to end

Step 1 — Install mailutils

bash
sudo apt update && sudo apt install mailutils

When the installer asks, choose Internet Site and use your server hostname. Test delivery: echo "test" | mail -s "test" you@example.com

Step 2 — Create the script

bash
nano disk-email-alert.sh

Paste the script, then Ctrl+X → Y → Enter to save.

Step 3 — Set your email and threshold

Edit EMAIL="you@example.com" and THRESHOLD=80 to match your inbox and risk tolerance.

Step 4 — Make it executable

bash
chmod +x disk-email-alert.sh

Step 5 — Run it once

bash
./disk-email-alert.sh

Schedule It with Cron

Automation is the point: let the script run while you sleep.

Open your crontab

bash
crontab -e

Example schedules

bash
# Hourly disk email check 0 * * * * /home/user/disk-email-alert.sh # Daily at 6:15 15 6 * * * /home/user/disk-email-alert.sh

Cron errors by mail

To capture stderr from a cron line: append 2>&1 | mail -s 'Cron Error' you@example.com so failures land in your inbox.

Gmail relay, combined checks, and rate-limiting

Use Gmail SMTP via msmtp (config example)

Install msmtp, then configure a relay. Example snippet for ~/.msmtprc — use an App Password, not your normal Gmail password:

bash
defaults auth on tls on tls_trust_file /etc/ssl/certs/ca-certificates.crt logfile ~/.msmtp.log account gmail host smtp.gmail.com port 587 from youraddress@gmail.com user youraddress@gmail.com password YOUR_APP_PASSWORD_HERE account default : gmail

Point mail at msmtp per your distro docs, or call msmtp directly from the script.

One email for multiple conditions (disk + RAM + CPU)

Collect flags in variables, build a single BODY, and send once if anything is red:

bash
#!/bin/bash EMAIL="you@example.com" DISK=$(df / | awk 'NR==2{print $5}' | tr -d '%') LOAD=$(awk -v OFMT='%.0f' '{print $1*100}' /proc/loadavg) WARN= [[ "$DISK" -gt 80 ]] && WARN+=$(printf 'Disk %s%%\n' "$DISK") # add free -m / top checks here… [[ ${#WARN} -gt 0 ]] && printf '%s' "$WARN" | mail -s "[ALERT] $(hostname)" "$EMAIL"

Send at most one alert per 24 hours (lockfile)

Use a sentinel file so a flapping condition does not hammer your inbox:

bash
LOCK=/var/tmp/disk-alert.sent if [[ "$USAGE" -gt "$THRESHOLD" ]]; then ALLOW=0 [[ ! -e "$LOCK" ]] && ALLOW=1 [[ -e "$LOCK" ]] && [[ $(date +%s) -gt $(( $(date -r "$LOCK" +%s) + 86400 )) ]] && ALLOW=1 [[ "$ALLOW" -eq 1 ]] && { echo "$MESSAGE" | mail -s "[ALERT] Disk Space on ${HOSTNAME}" "$EMAIL" touch "$LOCK" } fi

Why your alerts never arrive

Sending from cron without a full path

Cron’s PATH is minimal. Use absolute paths for the script on the cron line and optionally for mail inside the script ($(command -v mail)).

Mail goes to root@hostname locally

If SMTP is not set up, mail may only deliver locally. Confirm with a test message and watch /var/log/mail.log (path varies).

Understanding the Commands

PieceRole
hostnamePrints the system hostname for subjects and body
date '+%Y-%m-%d %H:%M:%S'Timestamp for logs and the email body
df / | awk … | tr -d '%'Numeric root filesystem use % for comparisons
du -sh /* | sort -rh | head -5Top-level directory sizes; helps you act on the alert
mail -s "subject" userReads body from stdin; sends via configured MTA

Frequently Asked Questions

How do I send an email from a bash script?

Use the mail command: echo 'Message body' | mail -s 'Subject' you@example.com. Install mailutils first on Ubuntu: sudo apt install mailutils

How do I install mailutils on Ubuntu?

Run: sudo apt update && sudo apt install mailutils. When prompted, choose Internet Site and use your server hostname.

Can I send email alerts automatically from a cron job?

Yes. Schedule your script with crontab -e. When the condition is met, the script sends the email. Add 2>&1 | mail -s 'Cron Error' you@example.com to capture cron output. For HTTP-based monitoring, pair this with the check if website is up script to receive alerts when your site returns a non-200 status.

What if mail command is not found?

Run: which mail — if it returns nothing, install with: sudo apt install mailutils (Ubuntu/Debian) or sudo yum install mailx (CentOS/RHEL).

How do I send email from bash using SMTP (Gmail)?

Use ssmtp or msmtp with your Gmail SMTP credentials. Configure /etc/ssmtp/ssmtp.conf with your gmail address and an App Password (not your real password).

Part of the Server Monitoring collection

BashSnippets logo

Written by Anguishe

Creator of BashSnippets.xyz

bashsnippets.xyz/about

Run this script on a real Linux server

Get $200 free credit — DigitalOcean

Get $200 Free →

Affiliate link · we earn a commission

Need a domain for your next project?

Register with Namecheap — free WHOIS privacy included

Check Domain Prices →

Affiliate link · we earn a commission

PAID RESOURCE — $9

The Production Bash Toolkit

6 scripts + shared library + 52-page field guide. The production layer the free snippets don't cover.

Get the Toolkit →

Related Snippets

Frequently Asked Questions

faq — snippet

How do I run this script?

Install mailutils first, save as mail-alert.sh, set your email address, run chmod +x mail-alert.sh, then execute ./mail-alert.sh.

faq — snippet

Does this work on macOS?

Not out of the box. macOS does not ship a mail transfer agent. Install msmtp or use curl with an SMTP API instead.

faq — snippet

How do I send an email from a bash script?

Run echo "body" | mail -s "Subject" user@example.com after installing mailutils on Ubuntu or Debian.

faq — snippet

Can I send email alerts automatically from a cron job?

Yes. Schedule any monitoring script with cron and pipe alert output to mail. Set MAILTO in crontab as an alternative.