Using a systemd path unit to clean my home server’s cache when disk space hits 90%

When the disk is almost full

I’ve run a handful of home‑servers for years, and the first thing that usually trips you up is a suddenly full disk.
Caches from apt, Docker, Flatpak, and the like can balloon without you noticing.
A quick, automated cleanup that only kicks in when the root filesystem is >90 % used keeps the system sane without you having to remember to run a script every week.

Why a systemd path unit?

Systemd’s path units are great for this.
They watch a file or directory and start a service when something changes.
They’re lighter than timers, and you don’t need a separate cron daemon.
When the cache directory gets a new file, the path unit triggers a service that checks the current usage and cleans only if the threshold is exceeded.

The cleanup service

Create /etc/systemd/system/cache-cleaner.service:

[Unit]
Description=Clean home‑server cache when disk usage is high
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/cache-cleaner.sh
User=root
# Protect the service from accidental writes
ProtectSystem=full
ProtectHome=yes
PrivateTmp=yes

The script is deliberately simple: it checks the root filesystem, cleans the apt cache, and can be extended to other caches.

cache-cleaner.sh (make it executable):

#!/usr/bin/env bash
# cache-cleaner.sh – run only when / is >90% full

TARGET=90
USAGE=$(df / --output=pcent | tail -n1 | tr -d '%')

if [ "$USAGE" -gt "$TARGET" ]; then
    echo "Disk usage $USAGE% > $TARGET% – cleaning cache"
    # Example: clean apt cache
    apt-get clean
    # Add other cache cleanups here
else
    echo "Disk usage $USAGE% ≤ $TARGET% – nothing to do"
fi

I keep the script in /usr/local/bin so it’s out of the way, but you can place it wherever you prefer.

The path unit

Create /etc/systemd/system/cache-cleaner.path:

[Unit]
Description=Trigger cache-cleaner.service on cache changes

[Path]
PathChanged=/var/cache/apt/archives
# If you want to monitor multiple caches, add PathChanged entries

[Install]
WantedBy=multi-user.target

When any file in /var/cache/apt/archives is added, removed, or modified, systemd starts cache-cleaner.service.
The service then decides whether to act.

Enabling and testing

sudo systemctl daemon-reload
sudo systemctl enable --now cache-cleaner.path

Check status:

systemctl status cache-cleaner.path

To test, touch a file in the cache directory:

sudo touch /var/cache/apt/archives/testfile

Then inspect the journal:

journalctl -u cache-cleaner.service -b

You should see the script’s output. If the disk is below 90 %, the service will exit without cleaning.

Caveats & troubleshooting

  • Permissions – The service runs as root. Make sure the script and its directory are owned by root and not writable by untrusted users; otherwise you’re opening a privilege‑escalation hole.
  • Threshold accuracy – df reports the percentage of used blocks, not the exact free space. For very large filesystems, a 90 % threshold might still leave a few GB free; tweak TARGET if you need tighter control.
  • Multiple caches – If you monitor several directories, add a PathChanged= line for each. The service will run once per change; if you want to batch multiple changes, consider debouncing with a short sleep or using systemd.path’s PathModified= with a debounce timer in the script.
  • Performance – The service is lightweight, but if the cache directory is heavily written to, the service may start frequently. Use systemd.path’s PathChanged= only on directories that change infrequently, or add a simple sleep in the script if you notice too many restarts.
  • Security – Running apt-get clean as root is safe because the command is part of the package manager. Avoid running arbitrary cleanup scripts that delete user data.

A quick word

This is a small addition to your setup that saves you from the dreaded “disk full” crash.
Give it a spin, tweak the threshold to match your usage pattern, and you’ll rarely have to manually clean caches again.


See also