Skip to main content

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.

5 min readUpdated March 29, 2026

Overviewsection

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.

Prerequisitessection

  • Linux server (Ubuntu, Debian, RHEL, CentOS, or any distro)
  • mailutils or mailx installed for email sending
  • A working mail relay (local postfix, or SMTP relay like SendGrid)
  • Bash 4.0+

Install mail utilitiessection

bash
# Ubuntu / Debian
sudo apt install mailutils -y

# RHEL / CentOS / Fedora
sudo dnf install mailx -y

The Scriptsection

bash
#!/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="it-alerts@company.com"   # 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

Setupsection

1. Save the scriptsection

bash
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 configurationsection

Open the script and change:

VariableChange ToPurpose
THRESHOLD80 (or your preferred percentage)Alert trigger point
ALERT_EMAILYour IT team emailWhere alerts are sent
EXCLUDE_FSAdd any FS types to ignoreSkip irrelevant mounts

3. Test itsection

bash
# 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 cronsection

bash
# 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.

Tip

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)section

If you do not have a local mail relay, configure postfix to route through an external SMTP service:

SendGridsection

bash
# 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 Alternativesection

If your team uses Slack or Teams, send alerts via webhook instead of email:

bash
# 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

Troubleshootingsection

IssueCauseFix
No email receivedMail utility not installed or relay not configuredInstall mailutils and test with echo "test" | mail -s "test" you@email.com
Alert for /snap or /run partitionsPseudo-filesystems not excludedAdd the FS type to EXCLUDE_FS
Script fails with "permission denied"Not executableRun chmod +x /opt/scripts/disk_monitor.sh
Cron job not runningPath issues in cron environmentUse full paths for all commands in the script
Duplicate alerts every 15 minutesDisk persistently above thresholdAdd rate limiting (e.g., check if alert was sent in the last hour)
BashLinuxMonitoringAutomationDevOps

Was this article helpful?