=RECLAIM(thy.disk)
1
lds.kosir.info · agentless · MIT

Linux Disk Space

Thy disks are full, and the hour is late.

It reads a list of hosts over SSH, escalates with sudo, and tells thee exactly what is eating the disk. Then, only if thou sayest yes, it makes room — one host at a time.

“And the disk was at ninety-seven percent, and no one had looked.” Findings 3:16 — as written by its elder sibling, linux-audit

This is what thou runnest when that verse comes true.

local.user@localhost $ linux_diskspace discover --hosts fleet.txt
/dev/mapper/rhel-root · xfs3.1G free of 100G
97%
↑ click the gauge to reclaim — it drains what is safe to drain
2

The Confession

Two verbs. One changes nothing.

It looks before it touches, and it never touches without asking. There is no unattended mode, and there was never going to be one.

discover — read-only, full stop. It collects df and df -i per filesystem, walks the ones over threshold with du -x, scans the usual culprits, and writes a plan, a spreadsheet, and a plain-text summary. It mutates nothing.

apply — takes that plan and does not trust it. Every candidate is re-measured against the live host before thou ever seest it, because a plan is a snapshot and a disk is not. Then it asks, per host, and does only what thou approvest.

3

What Fills the Disk

And what may empty it, colour-coded by consequence

The fills are not decoration. Green is what it will never touch, amber is regenerable, red is gone for good. Same three colours the workbook writes, so the page and the report agree on what is dangerous.

ClassWordWhy
Current logs
*.log · messages · secure
truncateA daemon holds these open. Delete frees nothing until it reopens, and can break logging. Truncating in place frees it now.
Rotated / compressed
.gz .xz .bz2 .old .N
deleteNothing holds them open. Usually the biggest easy win.
journaldvacuumNever rm the journal. Runs journalctl --vacuum-size.
dnf cache
/var/cache/dnf
cleanFully regenerable.
Old kernelsremoveVia dnf only, re-derived live. Keeps the running kernel plus two.
Core dumpsdeleteRegenerable, often large.
/tmp · /var/tmpreport onlyLive processes use these. Opt in with --tmp-max-age-days to propose deleting the truly old.
App logs outside /var/logreport onlyToo application-specific for a blanket rule.
Deleted-but-open filesreport onlyNamed with process and PID. Truncating a live fd can corrupt its output — thou decidest.
4

Two Truths, Kept

The wisdom the tool refuses to forget

Truncate, do not delete. The running daemon holds its log open by descriptor, not by name. Unlink the file and the bytes stay chained to the process until it reopens; the disk is no emptier and the logging is now broken. truncate -s 0 frees the space this instant and the daemon writes on, none the wiser.

When df is full and du is empty, someone is holding a ghost. A process has a deleted file open, so its blocks are never returned. This tool finds it with lsof +L1, names the process and its PID, and stops there. It will not truncate a stranger's open descriptor to look clever. That is thy call, and thy restart.

5

The Rite of Reclamation

Receive it, look, then empty — in that order

# receive it git clone https://github.com/vikozs/linux-diskspace.git cd linux-diskspace python3 -m pip install -r requirements.txt
# look, and be told — read-only, mutates nothing python3 linux_diskspace.py discover --hosts hosts.txt \ -u admin --ask-sudo-pass
# empty — re-validates live, asks per host python3 linux_diskspace.py apply --plan plan.json \ -u admin --ask-sudo-pass

Auth flags match linux-audit and linux-harden: keys or agent by default, --ask-ssh-pass for password login, --sudo-pass-same-as-ssh for sudo. Passwords travel by stdin or environment, never argv.

6

The Approval

Per host, after the disk has been asked again

web01.example.com: [current_log] truncate /var/log/app/access.log 3.0G [rotated_log] delete /var/log/messages-20260710.gz 40.0M [journal] vacuum current usage: 512.0M [dnf_cache] clean /var/cache/dnf 1.0G [old_kernel] remove running=5.14.0-427.el9 remove=(none) Apply 4 action(s) on web01.example.com? [y/N/q]

y applies the host. N, the default, spares it. q ends the run. Anything that failed re-validation — a path now missing, now protected, or a /tmp file no longer old enough — is dropped quietly rather than shown to thee as a promise it can no longer keep.

7

What This Actually Is

Breaking the fourth wall, briefly

A real tool, written by a real sysadmin who got tired of SSHing into eighty servers to find out which one filled up at 3am. Agentless, parallel, MIT licensed. It fails gracefully: hosts it cannot reach go to their own sheet with the reason, and the run carries on.

It re-validates every candidate against the live host before it touches anything, because a plan is a snapshot and disk state is not. Every path is resolved with realpath before an operation, then checked against a hardcoded deny list that no flag can override — so a symlink cannot walk the tool into a place it must never go:

/etc · /boot · /proc · /sys · /dev · /bin · /sbin · /lib · /lib64 · /usr · /root · /home · /var/lib/{mysql, pgsql, docker, containers, libvirt}

It writes an xlsx, and it never writes a formula into one. Everything a host reports is written as text, so a compromised server cannot hide a payload in its hostname and have it fire when thou openest the report. Same as its elder sibling. That part is still not a joke.

Built by vK, companion to linux-audit — the one that found the ninety-seven percent — and linux-harden. The code lives on GitHub under MIT.

Kneel also at the parish and read at the scriptorium. The Cluster abides. The Loop reconciles. The disk, left alone, fills.