Automatically monitor disk space usage across all filesystems and alert when usage exceeds threshold.

Overview

This script checks disk space usage on all mounted filesystems, identifies the largest directories consuming space, and sends alerts when usage exceeds a configurable threshold (default 90%). Alerts can be sent via email or logged to syslog.

Use Case: Proactive disk space monitoring to prevent out-of-space errors, automated alerting for capacity planning, and identifying space-consuming directories.

Platform: Linux (all distributions), macOS, Unix-like systems Requirements: Root/sudo privileges for full filesystem access, mail command (optional for email alerts) Execution Time: 30 seconds to 2 minutes depending on filesystem size

The Script

Lang: bash
  1#!/bin/bash
  2
  3#############################################################################
  4# Disk Space Monitor Script
  5#
  6# Description: Monitors disk space usage and alerts when threshold exceeded
  7# Author: glyph.sh
  8# Reference: https://glyph.sh/scripts/disk-space-monitor/
  9#
 10# Features:
 11# - Check all mounted filesystems
 12# - Configurable alert threshold (default 90%)
 13# - Email alerts or syslog logging
 14# - Identify largest directories consuming space
 15# - Exclude virtual/temporary filesystems
 16#############################################################################
 17
 18# Configuration
 19THRESHOLD=${THRESHOLD:-90}                    # Alert threshold (percentage)
 20EMAIL_TO=${EMAIL_TO:-"admin@example.com"}     # Email recipient
 21EMAIL_SUBJECT="[ALERT] Disk Space Warning"
 22USE_SYSLOG=${USE_SYSLOG:-true}               # Log to syslog (true/false)
 23USE_EMAIL=${USE_EMAIL:-false}                # Send email alerts (true/false)
 24TOP_DIRS=${TOP_DIRS:-10}                     # Number of largest dirs to show
 25MIN_DIR_SIZE=${MIN_DIR_SIZE:-100}            # Minimum dir size in MB to report
 26
 27# Filesystems to exclude from monitoring
 28EXCLUDE_FS="tmpfs|devtmpfs|squashfs|overlay|none|udev|cgroup"
 29
 30# Colors for output
 31RED='\033[0;31m'
 32YELLOW='\033[1;33m'
 33GREEN='\033[0;32m'
 34BLUE='\033[0;34m'
 35NC='\033[0m' # No Color
 36
 37#############################################################################
 38# Functions
 39#############################################################################
 40
 41print_header() {
 42    echo -e "${BLUE}========================================${NC}"
 43    echo -e "${BLUE} Disk Space Monitor${NC}"
 44    echo -e "${BLUE}========================================${NC}"
 45    echo ""
 46    echo "Threshold: ${THRESHOLD}%"
 47    echo "Date: $(date '+%Y-%m-%d %H:%M:%S')"
 48    echo ""
 49}
 50
 51log_message() {
 52    local level="$1"
 53    local message="$2"
 54
 55    # Log to syslog if enabled
 56    if [ "$USE_SYSLOG" = true ]; then
 57        logger -t "disk-space-monitor" -p "user.${level}" "$message"
 58    fi
 59
 60    # Print to console
 61    case "$level" in
 62        "warning")
 63            echo -e "${YELLOW}[WARNING]${NC} $message"
 64            ;;
 65        "error")
 66            echo -e "${RED}[ERROR]${NC} $message"
 67            ;;
 68        "info")
 69            echo -e "${GREEN}[INFO]${NC} $message"
 70            ;;
 71        *)
 72            echo "$message"
 73            ;;
 74    esac
 75}
 76
 77send_email_alert() {
 78    local subject="$1"
 79    local body="$2"
 80
 81    if [ "$USE_EMAIL" = true ] && command -v mail >/dev/null 2>&1; then
 82        echo "$body" | mail -s "$subject" "$EMAIL_TO"
 83        log_message "info" "Email alert sent to $EMAIL_TO"
 84    elif [ "$USE_EMAIL" = true ]; then
 85        log_message "error" "mail command not found. Cannot send email alerts."
 86    fi
 87}
 88
 89check_disk_space() {
 90    local alert_triggered=false
 91    local alert_message=""
 92
 93    echo -e "${BLUE}Checking filesystem usage...${NC}"
 94    echo ""
 95
 96    # Header
 97    printf "%-20s %8s %8s %8s %5s %s\n" "Filesystem" "Size" "Used" "Avail" "Use%" "Mounted"
 98    echo "--------------------------------------------------------------------------------"
 99
100    # Get filesystem usage, exclude virtual/temporary filesystems
101    while IFS= read -r line; do
102        # Parse df output
103        filesystem=$(echo "$line" | awk '{print $1}')
104        size=$(echo "$line" | awk '{print $2}')
105        used=$(echo "$line" | awk '{print $3}')
106        avail=$(echo "$line" | awk '{print $4}')
107        percent=$(echo "$line" | awk '{print $5}' | tr -d '%')
108        mountpoint=$(echo "$line" | awk '{print $6}')
109
110        # Skip if percent is not a number
111        if ! [[ "$percent" =~ ^[0-9]+$ ]]; then
112            continue
113        fi
114
115        # Determine status color
116        if [ "$percent" -ge "$THRESHOLD" ]; then
117            color=$RED
118            status="ALERT"
119            alert_triggered=true
120            alert_message="${alert_message}\n${filesystem} mounted on ${mountpoint}: ${percent}% used (${used} / ${size})"
121        elif [ "$percent" -ge $((THRESHOLD - 10)) ]; then
122            color=$YELLOW
123            status="WARN"
124        else
125            color=$GREEN
126            status="OK"
127        fi
128
129        # Print filesystem info
130        printf "${color}%-20s %8s %8s %8s %4s%% %s${NC}\n" \
131            "$filesystem" "$size" "$used" "$avail" "$percent" "$mountpoint"
132
133        # Log if over threshold
134        if [ "$percent" -ge "$THRESHOLD" ]; then
135            log_message "warning" "Filesystem $filesystem on $mountpoint is ${percent}% full"
136        fi
137    done < <(df -h 2>/dev/null | tail -n +2 | grep -vE "$EXCLUDE_FS")
138
139    echo ""
140
141    # Send alert if threshold exceeded
142    if [ "$alert_triggered" = true ]; then
143        local email_body="WARNING: Disk space usage has exceeded ${THRESHOLD}%\n\nAffected filesystems:${alert_message}\n\nHostname: $(hostname)\nDate: $(date)\n\nPlease investigate and free up space."
144        send_email_alert "$EMAIL_SUBJECT" "$(echo -e "$email_body")"
145    fi
146}
147
148find_largest_directories() {
149    echo -e "${BLUE}Finding largest directories...${NC}"
150    echo ""
151
152    # Find directories larger than MIN_DIR_SIZE
153    # This can take time on large filesystems
154    local search_paths=("/var" "/home" "/usr" "/opt" "/tmp")
155
156    for path in "${search_paths[@]}"; do
157        if [ -d "$path" ]; then
158            echo -e "${YELLOW}Analyzing: ${path}${NC}"
159
160            # Use du to find largest directories
161            # -x: stay on one filesystem (don't cross mount points)
162            # -h: human readable
163            # --max-depth=2: limit depth to avoid long scans
164            du -x -h --max-depth=2 "$path" 2>/dev/null | \
165                sort -rh | \
166                head -n "$TOP_DIRS" | \
167                while read -r size dir; do
168                    # Convert size to MB for comparison
169                    size_mb=$(echo "$size" | sed 's/[^0-9.]//g')
170                    unit=$(echo "$size" | sed 's/[0-9.]//g')
171
172                    # Convert to MB
173                    case "$unit" in
174                        G|g) size_mb=$(echo "$size_mb * 1024" | bc 2>/dev/null || echo "$size_mb") ;;
175                        K|k) size_mb=$(echo "$size_mb / 1024" | bc 2>/dev/null || echo "$size_mb") ;;
176                    esac
177
178                    printf "  %8s  %s\n" "$size" "$dir"
179                done
180            echo ""
181        fi
182    done
183}
184
185show_inode_usage() {
186    echo -e "${BLUE}Checking inode usage...${NC}"
187    echo ""
188
189    printf "%-20s %12s %12s %12s %5s %s\n" "Filesystem" "Inodes" "IUsed" "IFree" "IUse%" "Mounted"
190    echo "--------------------------------------------------------------------------------"
191
192    # Get inode usage
193    while IFS= read -r line; do
194        filesystem=$(echo "$line" | awk '{print $1}')
195        inodes=$(echo "$line" | awk '{print $2}')
196        iused=$(echo "$line" | awk '{print $3}')
197        ifree=$(echo "$line" | awk '{print $4}')
198        ipercent=$(echo "$line" | awk '{print $5}' | tr -d '%')
199        mountpoint=$(echo "$line" | awk '{print $6}')
200
201        # Skip if percent is not a number
202        if ! [[ "$ipercent" =~ ^[0-9]+$ ]]; then
203            continue
204        fi
205
206        # Determine status color
207        if [ "$ipercent" -ge "$THRESHOLD" ]; then
208            color=$RED
209            log_message "warning" "Inode usage on $filesystem ($mountpoint) is ${ipercent}% full"
210        elif [ "$ipercent" -ge $((THRESHOLD - 10)) ]; then
211            color=$YELLOW
212        else
213            color=$GREEN
214        fi
215
216        printf "${color}%-20s %12s %12s %12s %4s%% %s${NC}\n" \
217            "$filesystem" "$inodes" "$iused" "$ifree" "$ipercent" "$mountpoint"
218    done < <(df -i 2>/dev/null | tail -n +2 | grep -vE "$EXCLUDE_FS")
219
220    echo ""
221}
222
223print_summary() {
224    echo -e "${BLUE}========================================${NC}"
225    echo -e "${BLUE} Summary${NC}"
226    echo -e "${BLUE}========================================${NC}"
227    echo ""
228    echo "Monitoring completed at $(date '+%Y-%m-%d %H:%M:%S')"
229    echo "Threshold: ${THRESHOLD}%"
230    echo ""
231
232    # Count filesystems over threshold
233    local count=$(df -h 2>/dev/null | tail -n +2 | grep -vE "$EXCLUDE_FS" | awk '{print $5}' | tr -d '%' | awk -v t="$THRESHOLD" '$1 >= t {count++} END {print count+0}')
234
235    if [ "$count" -gt 0 ]; then
236        echo -e "${RED}WARNING: $count filesystem(s) over ${THRESHOLD}% capacity${NC}"
237    else
238        echo -e "${GREEN}All filesystems are below ${THRESHOLD}% capacity${NC}"
239    fi
240    echo ""
241}
242
243show_help() {
244    cat << EOF
245Usage: $0 [OPTIONS]
246
247Disk Space Monitoring Script
248
249OPTIONS:
250    -t, --threshold NUM      Alert threshold percentage (default: 90)
251    -e, --email ADDRESS      Email address for alerts
252    -s, --syslog             Enable syslog logging (default: true)
253    -m, --mail               Enable email alerts (default: false)
254    -d, --top-dirs NUM       Number of largest directories to show (default: 10)
255    -i, --inodes             Show inode usage
256    -h, --help               Display this help message
257
258EXAMPLES:
259    # Basic usage with defaults
260    $0
261
262    # Set custom threshold
263    $0 --threshold 85
264
265    # Enable email alerts
266    $0 --email admin@example.com --mail
267
268    # Show top 20 largest directories
269    $0 --top-dirs 20
270
271ENVIRONMENT VARIABLES:
272    THRESHOLD        Alert threshold percentage
273    EMAIL_TO         Email recipient address
274    USE_SYSLOG       Enable syslog (true/false)
275    USE_EMAIL        Enable email alerts (true/false)
276    TOP_DIRS         Number of top directories to show
277    MIN_DIR_SIZE     Minimum directory size in MB to report
278
279EOF
280    exit 0
281}
282
283#############################################################################
284# Main Script
285#############################################################################
286
287# Parse command line arguments
288SHOW_INODES=false
289
290while [[ $# -gt 0 ]]; do
291    case $1 in
292        -t|--threshold)
293            THRESHOLD="$2"
294            shift 2
295            ;;
296        -e|--email)
297            EMAIL_TO="$2"
298            shift 2
299            ;;
300        -s|--syslog)
301            USE_SYSLOG=true
302            shift
303            ;;
304        -m|--mail)
305            USE_EMAIL=true
306            shift
307            ;;
308        -d|--top-dirs)
309            TOP_DIRS="$2"
310            shift 2
311            ;;
312        -i|--inodes)
313            SHOW_INODES=true
314            shift
315            ;;
316        -h|--help)
317            show_help
318            ;;
319        *)
320            echo "Unknown option: $1"
321            echo "Use -h or --help for usage information"
322            exit 1
323            ;;
324    esac
325done
326
327# Validate threshold
328if ! [[ "$THRESHOLD" =~ ^[0-9]+$ ]] || [ "$THRESHOLD" -lt 1 ] || [ "$THRESHOLD" -gt 100 ]; then
329    echo "Error: Threshold must be between 1 and 100"
330    exit 1
331fi
332
333# Main execution
334print_header
335check_disk_space
336echo ""
337find_largest_directories
338echo ""
339
340if [ "$SHOW_INODES" = true ]; then
341    show_inode_usage
342fi
343
344print_summary
345
346log_message "info" "Disk space monitoring completed"
347
348exit 0

Usage

Basic Usage

Lang: bash
1chmod +x disk-space-monitor.sh
2./disk-space-monitor.sh

Custom Threshold

Lang: bash
1# Alert when usage exceeds 85%
2./disk-space-monitor.sh --threshold 85

Enable Email Alerts

Lang: bash
1# Send alerts to specific email
2./disk-space-monitor.sh --email admin@example.com --mail

Show More Directories

Lang: bash
1# Show top 20 largest directories
2./disk-space-monitor.sh --top-dirs 20

Check Inode Usage

Lang: bash
1# Include inode usage in report
2./disk-space-monitor.sh --inodes

Environment Variables

Lang: bash
1# Configure via environment variables
2export THRESHOLD=85
3export EMAIL_TO="admin@example.com"
4export USE_EMAIL=true
5export TOP_DIRS=15
6./disk-space-monitor.sh

What It Does

  1. Checks All Filesystems: Scans all mounted filesystems (excludes virtual/temporary)
  2. Compares Against Threshold: Identifies filesystems exceeding usage threshold
  3. Finds Large Directories: Locates largest directories consuming space
  4. Checks Inode Usage: Optionally monitors inode consumption (can fill up independently)
  5. Sends Alerts: Logs to syslog and/or sends email notifications
  6. Color-Coded Output: Visual indicators for usage levels (green/yellow/red)

Automated Monitoring

Cron Job Setup

Add to crontab for automated monitoring:

Lang: bash
 1# Edit crontab
 2crontab -e
 3
 4# Check disk space every hour
 50 * * * * /usr/local/bin/disk-space-monitor.sh --threshold 90 --syslog
 6
 7# Check every 6 hours with email alerts
 80 */6 * * * /usr/local/bin/disk-space-monitor.sh --threshold 90 --email admin@example.com --mail
 9
10# Daily detailed report with largest directories
110 8 * * * /usr/local/bin/disk-space-monitor.sh --top-dirs 20 --inodes --email admin@example.com --mail

Systemd Timer (Alternative to Cron)

Create systemd service and timer:

Lang: bash
 1# /etc/systemd/system/disk-space-monitor.service
 2[Unit]
 3Description=Disk Space Monitor
 4After=network.target
 5
 6[Service]
 7Type=oneshot
 8ExecStart=/usr/local/bin/disk-space-monitor.sh --threshold 90 --syslog
 9User=root
10
11[Install]
12WantedBy=multi-user.target
Lang: bash
 1# /etc/systemd/system/disk-space-monitor.timer
 2[Unit]
 3Description=Run Disk Space Monitor hourly
 4
 5[Timer]
 6OnCalendar=hourly
 7Persistent=true
 8
 9[Install]
10WantedBy=timers.target

Enable and start:

Lang: bash
1systemctl daemon-reload
2systemctl enable disk-space-monitor.timer
3systemctl start disk-space-monitor.timer

Common Issues This Prevents

  • Out of disk space errors
  • Application failures due to full filesystems
  • Log file growth consuming all space
  • Database failures from insufficient space
  • System crashes from full root partition
  • Backup failures from insufficient capacity

Email Configuration

Install Mail Utilities

Lang: bash
1# Debian/Ubuntu
2sudo apt-get install mailutils
3
4# RHEL/CentOS
5sudo yum install mailx
6
7# Configure with local MTA or relay host

Test Email Alerts

Lang: bash
1echo "Test message" | mail -s "Test Alert" admin@example.com

Understanding Output

Filesystem Usage

  • Green (<80%): Healthy, plenty of space available
  • Yellow (80-89%): Warning, monitor closely
  • Red (≥90%): Alert, action required

Largest Directories

Shows directories consuming most space for targeted cleanup:

  • Useful for identifying log accumulation
  • Finding old backups or cache files
  • Locating unexpected large files

Inode Usage

Inodes can be exhausted even with available disk space:

  • Happens with many small files
  • Check if filesystem shows space but can’t create files
  • Use --inodes flag to monitor

Cleanup Actions

When alerts trigger, investigate these common culprits:

Lang: bash
 1# Find large files
 2find /var -type f -size +100M -exec ls -lh {} \;
 3
 4# Find old log files
 5find /var/log -type f -mtime +30
 6
 7# Check for core dumps
 8find / -name "core.*" -type f 2>/dev/null
 9
10# Analyze package cache (Debian/Ubuntu)
11du -sh /var/cache/apt/archives/
12
13# Clean package cache (RHEL/CentOS)
14du -sh /var/cache/yum/

Integration with Monitoring Systems

Export to Prometheus

Modify script to export metrics:

Lang: bash
1# Add at end of script
2echo "# HELP disk_usage_percent Disk usage percentage" > /var/lib/node_exporter/disk_usage.prom
3df -h | grep -vE "$EXCLUDE_FS" | tail -n +2 | while read line; do
4    mount=$(echo $line | awk '{print $6}')
5    usage=$(echo $line | awk '{print $5}' | tr -d '%')
6    echo "disk_usage_percent{mountpoint=\"$mount\"} $usage" >> /var/lib/node_exporter/disk_usage.prom
7done

Webhook Alerts

Send to Slack/Discord/Teams:

Lang: bash
1# Add function for webhook alerts
2send_webhook_alert() {
3    local message="$1"
4    local webhook_url="YOUR_WEBHOOK_URL"
5
6    curl -X POST "$webhook_url" \
7        -H 'Content-Type: application/json' \
8        -d "{\"text\":\"$message\"}"
9}

See Also

Download

Lang: bash
1curl -O https://glyph.sh/scripts/disk-space-monitor.sh
2chmod +x disk-space-monitor.sh
3./disk-space-monitor.sh --help