Bash: Monitor Disk Usage and Send Email Alert
A lightweight Bash script to monitor disk usage on Linux servers and send an email alert when any partition exceeds a configurable threshold.
Overview
Running out of disk space on a production server causes application crashes, database corruption, and service outages — often at 3am. This script monitors all mounted partitions and sends an email alert when any of them exceeds a configurable usage threshold.
It is designed to run via cron and requires no external dependencies beyond mail or sendmail.
Prerequisites
- Linux server (Ubuntu, Debian, RHEL, CentOS, or any distro)
mailutilsormailxinstalled for email sending- A working mail relay (local postfix, or SMTP relay like SendGrid)
- Bash 4.0+
Install mail utilities
# Ubuntu / Debian
sudo apt install mailutils -y
# RHEL / CentOS / Fedora
sudo dnf install mailx -y
The Script
#!/usr/bin/env bash
#
# disk_monitor.sh — Monitor disk usage and send email alerts.
#
# Usage: ./disk_monitor.sh
# Configure the variables below, then add to cron.
set -euo pipefail
# ── Configuration ──────────────────────────────────────────────
THRESHOLD=80 # Alert when usage exceeds this percentage
ALERT_EMAIL="[email protected]" # Recipient email address
HOSTNAME=$(hostname -f) # Server hostname for the alert
EXCLUDE_FS="tmpfs|devtmpfs|squashfs|overlay" # Filesystem types to ignore
LOG_FILE="/var/log/disk_monitor.log" # Optional log file (set to "" to disable)
# ───────────────────────────────────────────────────────────────
ALERT_TRIGGERED=false
ALERT_BODY=""
# Get disk usage for all real partitions
while IFS= read -r line; do
usage=$(echo "$line" | awk '{print $5}' | tr -d '%')
partition=$(echo "$line" | awk '{print $6}')
filesystem=$(echo "$line" | awk '{print $1}')
size=$(echo "$line" | awk '{print $2}')
used=$(echo "$line" | awk '{print $3}')
available=$(echo "$line" | awk '{print $4}')
if [[ "$usage" -ge "$THRESHOLD" ]]; then
ALERT_TRIGGERED=true
ALERT_BODY+=" ALERT: ${partition} is at ${usage}% (${used} used of ${size}, ${available} free)
"
ALERT_BODY+=" Filesystem: ${filesystem}
"
ALERT_BODY+="
"
fi
done < <(df -h --output=source,size,used,avail,pcent,target -x tmpfs -x devtmpfs -x squashfs -x overlay | tail -n +2)
# Send alert if any partition exceeds threshold
if [[ "$ALERT_TRIGGERED" == true ]]; then
SUBJECT="[DISK ALERT] ${HOSTNAME} — partition(s) above ${THRESHOLD}%"
MESSAGE="Disk usage alert on ${HOSTNAME} at $(date '+%Y-%m-%d %H:%M:%S %Z')
The following partitions exceed the ${THRESHOLD}% usage threshold:
${ALERT_BODY}
Full disk report:
$(df -h --output=source,size,used,avail,pcent,target -x tmpfs -x devtmpfs -x squashfs -x overlay)
---
Sent by disk_monitor.sh on ${HOSTNAME}
"
echo "$MESSAGE" | mail -s "$SUBJECT" "$ALERT_EMAIL"
# Log the alert
if [[ -n "$LOG_FILE" ]]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ALERT sent — partitions above ${THRESHOLD}%" >> "$LOG_FILE"
fi
echo "Alert sent to ${ALERT_EMAIL}"
else
# Log the check
if [[ -n "$LOG_FILE" ]]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] OK — all partitions below ${THRESHOLD}%" >> "$LOG_FILE"
fi
echo "All partitions below ${THRESHOLD}%. No alert needed."
fi
Setup
1. Save the script
sudo mkdir -p /opt/scripts
sudo nano /opt/scripts/disk_monitor.sh
# Paste the script above
sudo chmod +x /opt/scripts/disk_monitor.sh
2. Edit the configuration
Open the script and change:
| Variable | Change To | Purpose |
|---|---|---|
THRESHOLD | 80 (or your preferred percentage) | Alert trigger point |
ALERT_EMAIL | Your IT team email | Where alerts are sent |
EXCLUDE_FS | Add any FS types to ignore | Skip irrelevant mounts |
3. Test it
# Run manually
sudo /opt/scripts/disk_monitor.sh
# Test with a low threshold to force an alert
sudo THRESHOLD=1 /opt/scripts/disk_monitor.sh
4. Add to cron
# Run every 15 minutes
sudo crontab -e
Add this line:
*/15 * * * * /opt/scripts/disk_monitor.sh 2>&1 | logger -t disk_monitor
This runs the script every 15 minutes and logs output to syslog.
For production servers, check every 5-15 minutes. For development or staging, every hour is usually sufficient. Adjust the cron schedule and threshold to match your environment's needs.
Advanced: SMTP Relay (SendGrid / AWS SES)
If you do not have a local mail relay, configure postfix to route through an external SMTP service:
SendGrid
# Install postfix and SASL
sudo apt install postfix libsasl2-modules -y
# Configure /etc/postfix/main.cf
sudo tee -a /etc/postfix/main.cf > /dev/null <<EOF
relayhost = [smtp.sendgrid.net]: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
header_size_limit = 4096000
EOF
# Add credentials
echo "[smtp.sendgrid.net]:587 apikey:YOUR_SENDGRID_API_KEY" | sudo tee /etc/postfix/sasl_passwd
sudo postmap /etc/postfix/sasl_passwd
sudo chmod 600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db
# Restart postfix
sudo systemctl restart postfix
Advanced: Slack / Teams Webhook Alternative
If your team uses Slack or Teams, send alerts via webhook instead of email:
# Replace the mail command with a webhook call
WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
if [[ "$ALERT_TRIGGERED" == true ]]; then
PAYLOAD=$(cat <<EOF
{
"text": ":warning: *Disk Alert on ${HOSTNAME}*\n\`\`\`\n${ALERT_BODY}\`\`\`"
}
EOF
)
curl -s -X POST -H 'Content-type: application/json' --data "$PAYLOAD" "$WEBHOOK_URL"
fi
Troubleshooting
| Issue | Cause | Fix |
|---|---|---|
| No email received | Mail utility not installed or relay not configured | Install mailutils and test with echo "test" | mail -s "test" [email protected] |
Alert for /snap or /run partitions | Pseudo-filesystems not excluded | Add the FS type to EXCLUDE_FS |
| Script fails with "permission denied" | Not executable | Run chmod +x /opt/scripts/disk_monitor.sh |
| Cron job not running | Path issues in cron environment | Use full paths for all commands in the script |
| Duplicate alerts every 15 minutes | Disk persistently above threshold | Add rate limiting (e.g., check if alert was sent in the last hour) |
Related Articles
Was this article helpful?
