cleanx is a safe, modular, dry-run-by-default disk & inode cleaner for Linux (Debian/Ubuntu focused).
Think “CCleaner for servers,” but Bash-only, scriptable, and fast.
- One file, zero exotic deps (just
bash,coreutils, and common tools). - Strict safety rails and explicit flags.
- Rich reports (blocks + inodes), JSON summary, quota-based runs.
- Self-update support + completions.
- Dry-run by default — prints actions; apply with
--yes. - Safety preflight — refuses to run if
/is ≥98% used (override with--force). - Low-impact mode —
ionice+nicewith--low-impact. - Tasks for: APT, logs, journald,
/tmp, user caches, language caches (Composer/npm/pnpm/yarn/pip), Snap/Flatpak, Docker/Podman/containerd, old kernels, coredumps, build caches, browser caches, Timeshift, package size view, FS hints. - Inode tooling — inode df, hotspots, parameterized deep scans.
- Configurable — global & user config files +
--config=FILE; include/exclude globs. - Secure erase —
--secure-erasetoshredfiles before removal. - Quota — stop when freed
SIZE, e.g.--quota=5G. - JSON report —
--jsonprints a machine-friendly summary. - Self-update —
--check-update,--update, with--channelsupport. - Shell completions —
--completions=bash|zsh. - Doctor —
--doctorchecks environment & prints quick FS state. - Locking — prevents concurrent runs via
/tmp/.cleanfy.lock.
Single-file install (matches Toolset layout):
sudo curl -fsSL "https://raw.githubusercontent.com/infocyph/Toolset/main/Clean/cleanx" \
-o /usr/local/bin/cleanx && sudo chmod +x /usr/local/bin/cleanx# Read-only system report (blocks, inodes, hotspots, deleted-but-open files)
cleanx --report
# Safe baseline cleanup (low I/O pressure)
sudo cleanx --yes --low-impact apt apt-residuals logs tmp tmpfiles usercache langcaches journal
# Free space until 5GB is reclaimed (applies changes)
sudo cleanx --yes --quota=5G logs tmp usercache
# Containers-heavy host
sudo cleanx --yes --low-impact --aggressive docker podman containerd
# Secure erase sensitive caches & truncate huge logs
sudo cleanx --yes --secure-erase usercache logs
reportis read-only and runs before any mutating tasks, even when combined with others.
cleanx [options] <tasks...>If you pass no tasks, cleanx shows --help and exits.
All tasks can be combined, e.g.:
sudo cleanx --yes logs tmp usercache| Task | What it does |
|---|---|
report |
Prints FS usage (blocks & inodes), top dirs by size & inode count, hotspots, deleted-but-open files |
apt |
apt autoremove --purge -y + apt clean |
apt-residuals |
Purges “rc” packages (residual configs) |
journal |
Rotates and vacuums journald (--vacuum-time or --vacuum-size via --journal-keep) |
logs |
Deletes rotated logs (*.gz) older than 14 days; truncates *.log over --log-max |
tmp |
Deletes /tmp entries older than --tmp-days (via find ... -mtime +N) |
tmpfiles |
Runs systemd-tmpfiles --clean if available |
usercache |
Clears $HOME/.cache, thumbnails, and Trash for --user (default: SUDO_USER/$USER) |
langcaches |
Clears Composer/npm/pnpm/yarn/pip caches for target user via su - |
snap |
Retains --snap-retain revisions; removes disabled snaps & saved snapshots |
flatpak |
flatpak uninstall --unused and flatpak remove --delete-data -y --unused |
docker |
docker system prune -af; with --aggressive also prunes --volumes |
podman |
podman system prune -af; with --aggressive also runs podman volume prune -f |
containerd |
Removes untagged images via ctr -n default images rm |
kernels |
Removes old kernels via apt autoremove --purge -y |
coredumps |
Removes core* > 50M (whole FS) and /var/crash/*; with --secure-erase uses shred |
buildcache |
Clears ccache stats & cache (if present) |
browsers |
Clears Chromium/Chrome/Firefox caches for target user |
timeshift |
Keeps last 3 of each Timeshift snapshot type (hourly/daily/weekly/monthly) |
pkgbig |
Lists top 30 packages by installed size (read-only) |
inode-scan |
Parameterized inode hotspot scan (--inode-path, --inode-depth, --inode-top) |
fshints |
Prints read-only Btrfs/ZFS maintenance suggestions (no commands run) |
all |
Runs a curated sweep: apt, apt-residuals, journal, logs, tmp, tmpfiles, usercache, langcaches, snap, flatpak, docker, podman, containerd, kernels, coredumps, buildcache, browsers, timeshift, fshints |
Many tasks are no-op if the underlying tool isn’t installed (e.g.,
snap,docker,flatpak).
These affect how cleanx runs:
-
--dry-runDefault. Show planned commands; do not modify the system. -
--yesApply changes (disables--dry-run). Still asks for confirmation once. -
--aggressiveStronger cleanup where supported (e.g.,docker system prune -af --volumes, podman volumes). -
--low-impactWraps actions inionice -c3 nice -n 10to reduce IO/CPU pressure. -
--forceRun even if root filesystem (/) is ≥98% used. Without this,cleanxbails out as a safety precaution. -
--secure-eraseUseshred -zuffor files before deleting directories in tasks that delete files. Slower but more privacy-friendly. -
--quota=SIZEStop once at leastSIZEhas been reclaimed on/. Examples:--quota=2G,--quota=800M. Internally usesdf -B1 /before & after tasks and compares. -
--jsonPrint a JSON summary at the end with before/after usage, reclaimed bytes, tasks, etc.
These control what is targeted and thresholds:
-
--journal-keep=VALHow much journald to keep: time (e.g.7d,12h) or size (e.g.200M). Default:7d. -
--log-max=SIZETruncate*.logfiles in/var/loglarger than this. Default:100M. -
--tmp-days=NDelete/tmpentries older thanNdays. Default:7. -
--snap-retain=NHow many Snap revisions to keep. Default:2. -
--user=NAMETarget user for cache & browser cleaning (affectsusercache,langcaches,browsers). Default:SUDO_USER, falling back to$USER. -
--inode-path=PATHRoot path forinode-scan. Default:. -
--inode-depth=NDepth forinode-scan(find ... -maxdepth N). Default:3. -
--inode-top=NHow many top directories by inode count to show ininode-scan. Default:50. -
--exclude-glob=GLOB(repeatable) Exclude paths globally in destructivefindcalls (e.g., logs/tmp). Internally turned intofind ... \( -path GLOB1 -o -path GLOB2 ... \) -prune -o. -
--include-glob=GLOB(repeatable) Restrict some operations to specific names (e.g., log truncation). Used as additional-name GLOBfilters where it makes sense.
-
--config=PATHExtra config file to source (evaluated after global/user config, before CLI overrides). -
--channel=NAMESelf-update/check channel (branch/tag). Default:main. Affects--check-updateand--update. -
--versionPrint version and exit. -
--check-updateFetch remote script, parse itsVERSION, compare, and print:local: 1.7.0 remote: 1.8.0 (channel: main) update_available: yesExit code
10when an update is available,0otherwise. -
--updateSelf-update from the repo (RAW_BASE/<channel>/Clean/cleanx), optional SHA256 verification if.sha256exists. Creates a backup:cleanx.bak.<timestamp>. -
--print-configPrint effective configuration (after config files and CLI overrides):cleanx v1.7.0 effective configuration --------------------------------------------- CHANNEL = main JOURNAL_KEEP = 7d LOG_MAX_SIZE = 100M TMP_DAYS = 7 SNAP_RETAIN = 2 TARGET_USER = hasan TARGET_HOME = /home/hasan ... EXCLUDE_GLOBS = /var/log/private/* INCLUDE_GLOBS = *.log *.gz -
--doctorEnvironment/requirements check. Verifies required tools (find,du,sort,awk,xargs,df), reports missing optional ones, and printsdf -hTprefixed withFS:. -
--completions=bash|zshPrint shell completions to stdout. You can redirect to the appropriate file. -
--help/-hShow usage and exit.
-
Dry-run first Every run is read-only unless you explicitly add
--yes. -
RootFS guard If
/is ≥98% used,cleanxexits with a warning (unless--forceis set). This prevents making a full-disk situation worse. -
Locking
cleanxuses/tmp/.cleanfy.lockto guard against concurrent runs. If the lock exists, it refuses to start (you can manually remove the file if it’s stale). -
Exclusions
--exclude-globglobs are honored across destructivefindcalls so you can protect sensitive paths. -
Secure erase is opt-in Without
--secure-erase, files are removed with normalrm/find -delete. With it, files are overwritten withshredbefore directory removal.
cleanx sources config files after internal defaults but before CLI args, in this order:
/etc/cleanfy.conf~/.config/cleanfy.conf--config=/path/to/custom.conf(highest precedence)
These files are just shell snippets; they can set any of the global variables the script uses.
Example:
# /etc/cleanfy.conf
JOURNAL_KEEP="200M"
LOG_MAX_SIZE="50M"
TMP_DAYS=5
SNAP_RETAIN=3
EXCLUDE_GLOBS=(
"/var/log/private/*"
"/var/tmp/keep/*"
)
INCLUDE_GLOBS=(
"*.log"
"*.gz"
)
# Set default channel for self-update
CHANNEL="stable"See your effective runtime config:
cleanx --print-configAdd --json to emit a JSON summary at the end:
sudo cleanx --yes --json logs tmp usercacheExample structure:
{
"tool": "cleanx",
"version": "1.7.0",
"channel": "main",
"timestamp": "2025-12-03T06:00:00Z",
"mode": "apply",
"aggressive": 0,
"low_impact": 1,
"secure_erase": 0,
"user": "hasan",
"quota_bytes": 0,
"quota_reached": 0,
"before_used_bytes": 1234567890,
"after_used_bytes": 987654321,
"reclaimed_bytes": 247914569,
"tasks": ["logs", "tmp", "usercache"],
"excludes": ["/var/log/private/*"],
"includes": ["*.log", "*.gz"]
}Fields:
mode— dry-run vs applyquota_bytes/quota_reached— whether the quota target was hitbefore_used_bytes/after_used_bytes/reclaimed_bytes— based ondf -B1 /tasks— executed tasks in orderexcludes/includes— globs in effect
Check remote version vs local:
cleanx --check-update # exit 10 if newer version existsUpdate to the latest version from a channel (default: main):
sudo cleanx --updateUse an alternate branch/tag as the channel:
sudo cleanx --channel=develop --updateGenerate completions and install:
# Bash
cleanx --completions=bash | sudo tee /etc/bash_completion.d/cleanx > /dev/null
# Zsh
cleanx --completions=zsh | sudo tee /usr/local/share/zsh/site-functions/_cleanx > /dev/nullReload your shell or source the files to activate.
Run weekly at low impact:
/etc/systemd/system/cleanx.service
[Unit]
Description=cleanx cleanup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/cleanx --yes --low-impact apt apt-residuals logs tmp tmpfiles usercache langcaches journal
Nice=10
IOSchedulingClass=idle/etc/systemd/system/cleanx.timer
[Unit]
Description=Run cleanx weekly
[Timer]
OnCalendar=Sun *-*-* 03:15
Persistent=true
[Install]
WantedBy=timers.targetEnable:
sudo systemctl daemon-reload
sudo systemctl enable --now cleanx.timercleanx --doctor- Ensures required tools:
find,du,sort,awk,xargs,df. - Lists missing optional tools:
journalctl,apt,dpkg,snap,flatpak,docker,podman,ctr,lsof,numfmt,ccache,timeshift,btrfs,zfs,curl,sha256sum,shasum. - Prints
df -hT(excluding squashfs/tmpfs/devtmpfs) as quick FS health.
1) Clean logs except a private tree; only touch .log & .gz:
sudo cleanx --yes \
--exclude-glob="/var/log/private/*" \
--include-glob="*.log" \
--include-glob="*.gz" \
logs2) Inode meltdown debugging under /var:
cleanx --report inode-scan \
--inode-path=/var \
--inode-depth=4 \
--inode-top=1003) Minimal container host reclaim (fast & safe):
sudo cleanx --yes docker containerd pkgbig4) Secure erase user caches + cap runtime with quota:
sudo cleanx --yes --secure-erase --quota=2G usercache logs5) Full sweep for a workstation:
sudo cleanx --yes --low-impact \
apt apt-residuals logs tmp tmpfiles usercache langcaches \
snap flatpak docker podman containerd browsers timeshift--secure-eraseusesshred -zufon files before removing directories.- It’s significantly slower than simple
rm/find -delete. - On CoW filesystems like Btrfs/ZFS, overwrite semantics are nuanced; see
fshintsfor FS-native commands (printed read-only).
- Targeted at modern Debian/Ubuntu derivatives.
- Required tools are minimal; optional tasks quietly degrade if their tools aren’t present.
Examples of optional binaries:
journalctl,apt,dpkg,snap,flatpak,docker,podman,ctr,lsof,ccache,timeshift,btrfs,zfs.
0— success1— generic error (missing tool, lock active, cancelled confirmation, checksum failure, etc.)10—--check-updatefound a newer version
- Use
--low-impacton busy nodes to be friendlier to latency-sensitive workloads. - Prefer targeted runs (
logs tmp usercache) for speed rather than always usingall. - Use
--quota=SIZEwhen you “just need N GB back” and don’t care which task gets you there. - Avoid
--secure-eraseunless you explicitly need overwrite semantics.