Shore Up
A storage tank with a red fill-line painted on its side; as the water rises past the line, a drain valve at the bottom opens and old crates float out.
LinuxSecurity

Auto-Clean Old Files When a Disk Fills Up

Ketan Aagja9 min read
No ratings yet

Before you run this

This script checks how full a filesystem is with df. If it is at or above a percentage you set, it deletes files older than a chosen age from one directory you designate — typically a cache, spool, or temp directory — using find. Its purpose is to keep a disk from filling up unattended, not to be a general cleanup tool.

Two things you must internalize before running it:

  • It deletes files permanently. find -delete unlinks files immediately; there is no recycle bin, no trash, no undo. The only recovery is restoring from a backup. Point it at a directory whose contents you are genuinely willing to lose (a regenerable cache, old logs you already ship elsewhere), never at /, /home, or a data directory.
  • The script ships with DRY_RUN=1. In that mode it only lists what it would delete. Leave it that way until you have read the list and agree with every line, then flip it to 0.

Privileges: it needs enough rights to read the target filesystem and delete the files in your cleanup directory. If those files are owned by root or a service account, run it as root (via sudo or a root cron entry). If the cleanup directory is your own, it runs fine unprivileged. Writing to /var/log/ requires root.

Test first: read the script, then run it by hand with DRY_RUN=1 on a non-production box or a copy of the directory before you ever schedule it or set DRY_RUN=0. Do not paste this onto a live server and walk away.

Assumed environment: Debian 12 / Ubuntu 22.04, GNU coreutils and findutils (their df and find), bash, and cron for scheduling. On RHEL/Alma the commands are identical; only the cron package name differs (cronie instead of cron), and you may prefer a systemd timer — I mention that at the end.

The script

Save this as /usr/local/bin/disk-cleanup.sh:

#!/usr/bin/env bash
#
# disk-cleanup.sh — When a filesystem crosses a usage threshold,
# delete old files from a single dedicated cleanup directory.

set -euo pipefail

# ---- Configuration: edit these ----
MOUNT="/"                       # filesystem to monitor
CLEAN_DIR="/var/tmp/appcache"   # directory whose old files get removed
THRESHOLD=85                    # percent-full that triggers cleanup
AGE_DAYS=30                     # remove files modified more than this many days ago
LOGFILE="/var/log/disk-cleanup.log"
DRY_RUN=1                       # 1 = list only (safe); 0 = actually delete
# -----------------------------------

log() {
    printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" | tee -a "$LOGFILE"
}

# Current usage as an integer. df --output=pcent is GNU coreutils; the
# last line holds the value like " 85%"; strip everything but digits.
usage=$(df --output=pcent "$MOUNT" | tail -1 | tr -dc '0-9')

log "Usage of $MOUNT is ${usage}% (threshold ${THRESHOLD}%)"

if (( usage < THRESHOLD )); then
    log "Below threshold, nothing to do."
    exit 0
fi

if [[ ! -d "$CLEAN_DIR" ]]; then
    log "ERROR: $CLEAN_DIR does not exist. Aborting."
    exit 1
fi

log "Threshold crossed. Targeting files older than ${AGE_DAYS} days in ${CLEAN_DIR}"

if (( DRY_RUN == 1 )); then
    log "DRY RUN — the following files WOULD be deleted:"
    # -type f: regular files only; -mtime +N: modified more than N*24h ago
    find "$CLEAN_DIR" -type f -mtime +"$AGE_DAYS" -print | tee -a "$LOGFILE"
else
    find "$CLEAN_DIR" -type f -mtime +"$AGE_DAYS" -print -delete | tee -a "$LOGFILE"
    log "Cleanup complete."
fi

Replace the four configuration values with yours. MOUNT is the filesystem you care about (/, /var, /data — whatever df lists). CLEAN_DIR is the obvious placeholder /var/tmp/appcache; change it to your real cache/spool path. Make it executable:

sudo chmod 750 /usr/local/bin/disk-cleanup.sh

A few things worth knowing about the tools I chose:

  • df --output=pcent is a GNU coreutils feature. It prints a header line (Pcent) and the value; tail -1 takes the value and tr -dc '0-9' reduces " 85%" to 85. This is more robust than parsing whole df columns by position.
  • -mtime +N matches by modification time — when the file's contents last changed — measured in 24-hour blocks. +30 means "more than 30 whole days ago." If you need last-access time instead, that is -atime, but access times are often disabled by the relatime/noatime mount options, so -mtime is the reliable default.
  • -type f restricts to regular files, so the script never tries to remove directories. Empty directories left behind are harmless; removing them safely is a separate, fiddlier job I am deliberately not bolting on here.

Test it by hand first

With DRY_RUN=1 still set, run it:

sudo /usr/local/bin/disk-cleanup.sh

If usage is below the threshold you will see one line saying so. To force the cleanup branch during testing without waiting for a real disk-fill, lower THRESHOLD temporarily (say to 1) and re-run. Read every path it prints. Confirm they all live under your CLEAN_DIR and are things you can lose. Only when you are satisfied, set DRY_RUN=0 and reset THRESHOLD to its real value.

Schedule it with cron

Run it as root on a schedule with cron. Edit root's crontab:

sudo crontab -e

Add a line to run every 30 minutes:

# Check disk usage and auto-clean the cache if the threshold is crossed
*/30 * * * * /usr/local/bin/disk-cleanup.sh >/dev/null 2>&1

The script already appends to its own logfile, so I discard cron's stdout/stderr here. If you would rather capture any unexpected errors from cron itself, redirect to a file instead of /dev/null.

Pick an interval that matches how fast your disk fills. Every 30 minutes is fine for a slowly-growing cache; a spool that can fill in minutes wants a tighter interval.

Verify it worked

After a run that crossed the threshold, check three things.

Look at the log:

sudo tail -n 20 /var/log/disk-cleanup.log

You should see the recorded usage percentage and, on a real (non-dry) run, the list of deleted files followed by Cleanup complete.

Confirm the space actually came back:

df -h /            # replace / with your MOUNT

Confirm the old files are gone but recent ones remain:

# Anything still older than the age limit? (should print nothing)
find /var/tmp/appcache -type f -mtime +30

And confirm cron is picking the job up by checking the system log after a scheduled interval (journalctl -u cron on Debian/Ubuntu, or grep CRON /var/log/syslog).

Undoing it

There is no undo for the deletions themselves — that is the nature of find -delete, and it is why the script is dry-run by default and scoped to one directory. If you removed something you needed, restore it from your backup.

What you can reverse safely is the automation. To stop it, remove the line from root's crontab (sudo crontab -e) and, if you like, delete the script and its log:

sudo rm /usr/local/bin/disk-cleanup.sh /var/log/disk-cleanup.log

If you prefer systemd

The same script can be driven by a systemd service unit plus a timer unit instead of cron — this gives you journalctl history and OnCalendar= scheduling. The script does not change; only the scheduling wrapper does. If you go that route, check the exact [Timer] syntax against the systemd.timer documentation rather than guessing at it.

For the flags used here, the GNU coreutils manual (df) and the findutils manual (find) are the canonical references — both are also on your system via man df and man find.

Written by
Ketan Aagja

Runs enterprise networks and security for a living, and writes Shore Up to turn two decades of hands-on Linux, Windows and mail-server work into guides you can actually use.

More about the author →

Was this article helpful?

Tap a star — no sign-in needed.

Be the first to rate this article.