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 –
dfreports the percentage of used blocks, not the exact free space. For very large filesystems, a 90 % threshold might still leave a few GB free; tweakTARGETif 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 usingsystemd.path’sPathModified=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’sPathChanged=only on directories that change infrequently, or add a simple sleep in the script if you notice too many restarts. - Security – Running
apt-get cleanas 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
- What a half‑open TCP connection in “ss” really means (and how to close it)
- How I Stopped /var/log from Flooding the Root Filesystem with Systemd Journal Rotations
- Fixing the “root=UUID not found” error after cloning an SSD with Clonezilla
- Pulling all failed SSH login attempts from auth.log in under a minute with awk
- Rootless Podman Container Drops Its Persistent Volume After a Reboot: A Quick Fix for My HomeLab 🚀