What you will learn
Monitor disk usage with a two-tier threshold: alert at a warning level, and only run an automated, tightly scoped cleanup past a hard threshold — so a runaway cleanup script never becomes the thing that deletes data it shouldn't.
Before you begin
Automated deletion is inherently risky. This design deliberately narrows what can be auto-deleted (only files matching a specific, safe pattern in a specific directory) rather than a generic "delete oldest files anywhere" approach.
1. Check usage and classify the severity
#!/usr/bin/env bash
set -Eeuo pipefail
MOUNT="/var"
WARN_PCT=80
CRITICAL_PCT=90
CLEANUP_DIR="/var/cache/myapp/tmp"
usage_pct="$(df --output=pcent "$MOUNT" | tail -1 | tr -dc '0-9')"
echo "disk usage on ${MOUNT}: ${usage_pct}%"
if (( usage_pct < WARN_PCT )); then
echo "ok"
exit 0
fi
2. Warn without acting between the two thresholds
if (( usage_pct >= WARN_PCT && usage_pct < CRITICAL_PCT )); then
echo "WARN: ${usage_pct}% >= ${WARN_PCT}%, no automated action taken"
exit 1
fi
The gap between warning and critical is deliberate: it gives a human time to investigate why usage is climbing before an automated script starts deleting anything. Auto-cleanup only fires past the harder threshold.
3. Scoped, auditable cleanup past the critical threshold
if (( usage_pct >= CRITICAL_PCT )); then
echo "CRITICAL: ${usage_pct}% >= ${CRITICAL_PCT}%, running scoped cleanup"
if [[ ! -d "$CLEANUP_DIR" ]]; then
echo "cleanup dir ${CLEANUP_DIR} missing, aborting cleanup" >&2
exit 2
fi
before_kb="$(du -sk "$CLEANUP_DIR" | cut -f1)"
# Only ever remove files this app owns, matching a known-safe pattern, older than a day.
find "$CLEANUP_DIR" -maxdepth 2 -type f -name '*.tmp' -mtime +1 -print -delete
after_pct="$(df --output=pcent "$MOUNT" | tail -1 | tr -dc '0-9')"
freed_kb=$(( before_kb - $(du -sk "$CLEANUP_DIR" | cut -f1) ))
echo "cleanup freed ~${freed_kb}KB, usage now ${after_pct}%"
if (( after_pct >= CRITICAL_PCT )); then
echo "STILL CRITICAL after cleanup: ${after_pct}%" >&2
exit 2
fi
fi
Hardcoding the deletion pattern (*.tmp, -maxdepth 2, one specific directory) rather than accepting it as a parameter means a misconfigured or compromised caller can't turn this into an arbitrary-file-deletion tool.
Example output
disk usage on /var: 93%
CRITICAL: 93% >= 90%, running scoped cleanup
cleanup freed ~2048000KB, usage now 84%
Verify the result
Fill a scratch mount past both thresholds with dummy .tmp files and confirm: nothing is deleted below WARN_PCT, a warning fires with no deletion between the thresholds, and only matching files under CLEANUP_DIR are removed past CRITICAL_PCT.
Troubleshooting
If usage doesn't drop enough after cleanup, the script correctly re-reports STILL CRITICAL rather than silently succeeding — that's the signal to widen retention policy elsewhere (see auto-log-rotation-pipeline) rather than loosening this script's deletion scope.
Next steps
Alert on both WARN and the post-cleanup STILL CRITICAL case via webhook-alert-dispatcher; treat the latter as higher severity.