A safe, thorough scheduled system cleaner for every *nix (Linux, macOS, BSD).
Project description
Cleanix CLI
A safe, thorough system cleaner for *every nix flavour — Linux (all major distros), macOS, and the BSDs. It analyzes your OS for junk — caches, temp files, package-manager leftovers, orphaned packages, broken symlinks, package-update config residue, rotated logs, trash, thumbnails, browser/dev caches, container cruft — and only removes anything after you confirm.
It can run on a schedule (a systemd timer on Linux/BSD, a launchd agent on macOS) to periodically analyze the system and notify you. Scheduled runs never delete anything; they only produce a report so you can review and clean on your own terms.
Platform support
Cleaners declare which platforms they apply to; cleanix auto-detects the OS
(and Linux distro via /etc/os-release) and only runs the relevant ones.
| Family | Detected as | Package/system cleaners |
|---|---|---|
| Linux | linux + distro id |
apt, dnf/yum, pacman, zypper, apk, xbps, portage, flatpak, snap, nix, journald, coredumps, /var/crash, .rpmnew/.pacnew/.dpkg-old residue |
| macOS | macos |
Homebrew, MacPorts, Xcode (DerivedData/DeviceSupport/simulators), ~/Library caches & logs, Trash |
| FreeBSD | freebsd |
pkg cache + autoremove, ports distfiles & work dirs |
| NetBSD | netbsd |
pkg, pkgin cache |
| OpenBSD | openbsd |
pkg |
| Cross-OS | all of the above | trash, thumbnails, ~/.cache, temp files, fonts, browser caches, broken symlinks, .DS_Store/AppleDouble, dev/language caches (cargo/go/gradle/ccache/composer/…), pip, npm/yarn, Docker, Podman |
Run cleanix list to see what applies to your machine, or cleanix list --all
to see every platform's cleaners.
AI / LLM leftovers (triaged, not nuked)
Local AI tooling scatters a lot of disposable junk and a lot of data you deliberately downloaded. Cleanix triages — it removes the trash and leaves your models and chat history alone:
| Tool | Removed (trash) | Kept (your data) |
|---|---|---|
| Ollama | dangling blobs from interrupted pulls, server logs | installed models (referenced blobs/manifests) |
| Hugging Face | *.incomplete downloads, .locks, .no_exist |
downloaded model/dataset weights |
| PyTorch/Triton/vLLM/CUDA | Triton, torch.compile/inductor, torch_extensions, vLLM, ~/.nv/ComputeCache, FlashInfer JIT caches |
— (all pure compile caches) |
| LM Studio / Jan | server logs | models, presets, conversations |
| Continue.dev | rebuildable embeddings index, logs, telemetry | sessions/config |
| Claude Code | throwaway shell snapshots, telemetry cache | projects, history |
| Aider | stray .aider.tags.cache.*, .aider.chat.history.md, .aider.input.history in project trees |
— |
AI coding-agent CLIs (retention-aware)
These accumulate rolling backups, per-session file-history/checkpoints, debug logs, and transcripts that no built-in command reliably prunes. Cleanix applies proper retention instead of all-or-nothing deletion:
| Tool | Removed | Kept |
|---|---|---|
Claude Code (~/.claude) |
rolling .claude.json backups beyond the newest keep_backups (default 2); debug/telemetry/paste/download/changelog caches; stale (>ai_history_max_age_days) file-history & transcripts |
~/.claude.json, settings.json, plugins/, credentials, and any currently-running session |
OpenAI Codex (~/.codex) |
session rollouts & TUI logs older than the retention age | recent sessions, config |
Gemini CLI (~/.gemini/tmp) |
stale per-project session/log data | config, recent sessions |
OpenCode (~/.local/share/opencode) |
logs, snapshots, tool-output | the SQLite DB, auth, storage |
Two knobs control retention (see config/default.yaml):
keep_backups: 2— how many newest rolling backups to keep.ai_history_max_age_days: 30— transcripts/history/logs older than this are offered (chat history is your data, so it's age-gated and conservative).
Per-session scratch (todos, shell snapshots, session env) touched within the last day is always left alone, so cleaning never disturbs a live agent session.
General app bloat
- Electron/Chromium apps (VS Code, Slack, Discord, Signal, Obsidian, …):
removes only rebuildable
Cache/Code Cache/GPUCache/Dawn*Cache/CachedData/Crashpad— neverLocal Storage,IndexedDB,Service Worker, or cookies (so you stay logged in). Covers both~/.config/*and Flatpak (~/.var/app/*) apps. - Flatpak app caches — every app's
~/.var/app/*/cache. - GPU shader caches — Mesa (
mesa_shader_cache[_db]), NVIDIAGLCache, RADV/AMD Vulkan caches (rebuilt after driver updates). - Steam — per-game shader caches, interrupted downloads, HTTP cache.
- JetBrains IDEs — caches, logs, and indexes (Linux
~/.cache/JetBrains, macOS~/Library/Caches/JetBrains).
Cleaners never offer the same path twice: dedicated cleaners own their
directories and the generic ~/.cache catch-all defers to them.
Container leftovers (Docker / Podman)
A leftover is anything the engine keeps that no existing container references and can be regenerated. Each category is surfaced as its own item whose estimated size matches exactly what pruning it reclaims — so the number you see is the number you get (no more counting unused-but-tagged images or volumes you never asked to touch):
- Dangling (untagged) images —
docker image prune -f - Stopped containers (writable layers) —
docker container prune -f - Build cache —
docker builder prune -f - Unused networks —
docker network prune -f
Two categories stay opt-in because they can destroy real work:
docker_prune_all_images— also remove tagged images not used by any container (image prune -a); frees more but forces a re-pull next time.docker_prune_volumes— also remove unused anonymous volumes, which may hold databases or other real data.
On macOS, Docker Desktop stores everything in a VM disk image. Pruning frees space inside the VM; the host-side
Docker.rawfile may not shrink until Docker Desktop compacts it.
Shell completion
cleanix completion bash | sudo tee /etc/bash_completion.d/cleanix
cleanix completion zsh > ~/.zfunc/_cleanix # a dir on your $fpath
cleanix completion fish > ~/.config/fish/completions/cleanix.fish
Completes subcommands, options, cleaner ids (for --only/--exclude) and config
keys — baked in from the live build.
Safe memory reclamation (RAM + swap)
Opt-in cleaners that free memory without ever killing or trimming apps:
sudo cleanix clean --only memory --execute # drop clean page/dentry/inode cache
sudo cleanix clean --only swap --execute # move swap back to RAM (OOM-guarded)
sudo cleanix clean --only memory_macos --execute # macOS `purge`
memoryrunssyncthendrop_caches— the kernel only releases clean cached pages, re-reading from disk later; no process loses data.swaprunsswapoff -a && swapon -aonly when free RAM comfortably exceeds swap in use (20% headroom), so it can never trigger an OOM kill.- These are excluded from the default
scan/clean(their effect is transient); run them explicitly with--only.
IDE & application coverage
- IDEs (
ide_caches): VS Code and forks (VSCodium, Cursor, Windsurf, …) logs /CachedExtensionVSIXs/ stale workspace storage, Sublime, Atom, Emacs native-comp cache, Zed logs, Qt Creator, Godot, Unity — plus JetBrains and Xcode via their dedicated cleaners. - Apps (
app_leftovers,snap_app_cache): logs/crash-reports/temp for Minecraft, Steam, Heroic, Lutris, Bottles, OBS, Zoom, Skype, Kodi, Nextcloud, Dropbox, Ferdium/Rambox, browser crash reports, and every snap's per-app cache.
Reversible cleaning, history & dashboard
- Quarantine (undo):
cleanix clean --quarantinemoves junk into a per-run quarantine instead of deleting it. Undo withcleanix restore, reclaim the space for good withcleanix quarantine empty, review withcleanix quarantine list. A safety net most cleaners don't offer. - History & stats: every real clean is logged;
cleanix statsshows lifetime space reclaimed and recent runs. cleanix info: one-glance dashboard — OS/distro, detected package managers, applicable cleaner counts, config path, lifetime cleaned, quarantine size.- Parallel scanning across a thread pool (≈1.4×+ faster), with a live progress bar.
scan --sort size --top Nto focus on the biggest wins.- User ignore-globs:
protected_globsin config adds your own never-delete paths on top of the built-in guard.
Highlights
- Scan first, clean on confirmation. Nothing is deleted without an explicit
--yesor an interactive confirmation. - Dry-run by default.
cleanix cleanshows what would be removed unless you pass--execute. - Safe deletes. A hard-coded protected-path guard refuses to touch critical
system directories on every platform (
/,/etc,/usr,/System,/Library,$HOME,~/Library,/usr/ports, ...). - Orphan & leftover aware. Finds orphaned packages, residual configs
(dpkg
rcstate), package-update residue (.rpmnew/.pacnew/.dpkg-old), dangling symlinks, and orphaned launcher entries — the cruft "improperly designed" packages leave behind. - Accurate sizing. Uses actual allocated blocks, not apparent size, so sparse files don't wildly overstate reclaimable space. Actively-written files are left alone (in-use guard).
- Modular & platform-gated. Each junk source is an isolated, auditable module that declares the platforms it applies to.
- Root-aware. Cleaners that need root are clearly flagged and skipped (with a note) when you run unprivileged.
- Scheduling. Install a periodic read-only scan that reports (and notifies)
on a daily/weekly/monthly cadence — a systemd user timer on Linux/BSD, a
launchd LaunchAgent on macOS. Same
cleanix schedulecommand on both.
Install
pip install cleanix-cli # from PyPI
# or from source:
cd cleanix-cli && pip install -e .
This installs the cleanix command. Standalone Linux/macOS binaries are also
attached to each GitHub Release.
Usage
# List available cleaners
cleanix list
# Analyze the system (read-only) and print a report
cleanix scan
# Analyze only specific cleaners
cleanix scan --only trash,thumbnails,apt
# Preview what a clean would remove (dry-run, the default)
cleanix clean
# Actually delete, asking for confirmation
cleanix clean --execute
# Delete without prompting (for scripts)
cleanix clean --execute --yes --only trash,pip_cache
# Emit the scan report as JSON (for tooling / notifications)
cleanix scan --json
# Install a weekly read-only scan that notifies you
# (systemd timer on Linux/BSD, launchd agent on macOS)
cleanix schedule install --frequency weekly
# Check / remove the schedule
cleanix schedule status
cleanix schedule uninstall
# View and change settings (no YAML editing needed)
cleanix config list
cleanix config set remove_old_kernels false
cleanix config set keep_kernels 3
cleanix config unset keep_kernels # revert to default
# See a factory-reset plan for this OS (advisory — runs nothing)
cleanix factory-reset
cleanix factory-reset --scope user
Configuration via CLI
cleanix config reads/writes ~/.config/cleanix/config.yaml with type-checked
values, so you never have to hand-edit YAML:
cleanix config list # every setting, value, source, description
cleanix config get <key>
cleanix config set <key> <value> # bool/int/float/list are validated
cleanix config unset <key> # revert to the built-in default
cleanix config path
Factory-reset advisor
cleanix factory-reset detects how your OS can be reset and prints a tiered,
copy-pasteable plan — it never executes anything destructive:
- NixOS / Guix → declarative rollback to a prior generation (✅ reversible)
- rpm-ostree (Silverblue/Kinoite) →
rpm-ostree resetto the base image (✅) - openSUSE MicroOS →
snapper rollback(✅) - macOS → the built-in "Erase All Content and Settings" (recommended)
- Traditional distros (Fedora/Ubuntu/Arch) → honestly reports there is no true factory reset; suggests snapshot-restore/reinstall and gives best-effort package/config/user reset steps.
--scope user|packages|system|full controls how much the plan covers.
Executing a reversible reset
On reversible systems only, cleanix can run the rollback for you:
sudo cleanix factory-reset --execute
This is heavily gated:
- Only reversible strategies run — NixOS (
nixos-rebuild switch --rollback), rpm-ostree (rpm-ostree reset), MicroOS (snapper rollback), Guix (guix system roll-back). Traditional distros and macOS refuse to execute. - Only the reversible rollback command runs — never the irreversible
dotfile/
rm -rfsteps (those stay advisory). - Requires root, an interactive terminal, and a typed confirmation
phrase (
reset <hostname>). Each action prints its undo command. - Stops on the first failure.
Without --execute, factory-reset only prints the plan.
System-wide mode (root)
Run unprivileged, cleanix scans your home. Run as root/sudo, it automatically sweeps every real user's home plus system-wide locations:
sudo cleanix scan # all users + system (auto when root)
sudo cleanix scan --current-user # restrict to the invoking user
cleanix scan --all-users # force multi-user even unprivileged
sudo cleanix scan --min-uid 500 # change the "real user" uid threshold
How it works:
- Cleaners declare a scope: user (trash, caches, browsers, AI tools, …)
or system (package managers,
/var/log, journal, coredumps, …). - User-scoped cleaners run once per target user, resolving
~,~/.cache,~/.config, uid, etc. against that user — never leaking root's environment. - Target users come from
/etc/passwd: root plus accounts with uid ≥min_uid(1000 on Linux/BSD, 500 on macOS) that have a real home directory. - Shared/system paths yielded for multiple users are de-duplicated, so nothing is offered — or deleted — twice.
- The protected-path guard is extended to cover every scanned user's home
(and their
~/.config,~/.local,~/Library, …), so a bug can never wipe another user's home.
Report-only tier (backups & snapshots)
Some things are leftovers but also recovery data — filesystem snapshots,
mobile-device backups, suspended-VM memory images. Cleanix surfaces these with
their size and the correct removal command, but they are never eligible
for deletion (clean skips them; even a forced attempt is refused):
Report-only — review and remove manually (never auto-deleted)
7.5 GiB Timeshift snapshot: 2026-01-01 timeshift --delete --snapshot '2026-01-01'
Covered report-only: Timeshift/Snapper snapshots, libvirt saved VM states, macOS Time Machine local snapshots, iOS device backups, macOS sleep image.
Extended coverage
Beyond the core cleaners, cleanix also handles:
- Old kernels — package-manager-driven; always keeps the running kernel and
the newest
keep_kernels(default 2). Setremove_old_kernels: falseto skip. - System caches & spools —
/var/cache(PackageKit/man/fontconfig/cups), Debian/var/backups, ABRT/apport crash spool, staged offline-update payloads. - Language package caches — Go modcache, rustup/deno/bun/nvm/pnpm, Android
SDK. Offline repos (Maven/Ivy/sbt/Coursier/NuGet/RubyGems) gated behind
include_offline_repos. - Desktop search indexes — Baloo, Tracker, Zeitgeist, GVFS metadata.
- VM/cloud tooling — VirtualBox logs, Vagrant boxes, kube/minikube/helm, AWS/Azure/gcloud/Terraform/Pulumi caches.
- Editor & build litter — Vim/Neovim swap/undo,
__pycache__,.pytest_cache/.mypy_cache/.ruff_cache/.tox, loose core dumps. - macOS extras — Saved Application State, CocoaPods/Carthage, QuickLook.
- Opt-in (off by default): editor backup files (
*~/*.bak/*.orig) viaremove_backup_files, and localepurge-style unused locales/man-pages viapurge_unused_locales.
Safety model
- Two-phase:
scanis always read-only.cleandefaults to dry-run. - Protected paths: deletion of (or inside) critical paths is refused at the
lowest level — see
cleanix/core/safety.py. - Age thresholds: temp/log cleaners only consider files older than a configurable age, so in-use files are left alone.
- Scheduled = analyze only: the timer runs
scanand writes a report; it does not clean.
Configuration
Defaults live in cleanix/config.py. Override them with a YAML file at
~/.config/cleanix/config.yaml (see config/default.yaml for the shape).
Project layout
cleanix/
cli.py # argparse entry point (scan / clean / list / schedule)
config.py # defaults + YAML overrides
core/
models.py # CleanableItem, CleanerReport, dataclasses
platform.py # OS + distro detection, platform tokens
context.py # multi-user scanning (target users, current-user binding)
safety.py # protected-path guard + safe delete
engine.py # runs cleaners, aggregates reports, executes cleans
registry.py # discovers, platform-filters & instantiates cleaners
utils.py # sizes (allocated blocks), walking, age/in-use guards
report.py # rich tables + JSON rendering
cleaners/
base.py # Cleaner ABC (declares .platforms)
# cross-platform
trash, thumbnails, user_cache, temp_files, font_cache, browsers,
broken_symlinks, apple_litter, dev_caches, pip_cache, npm_cache, containers
# logs & crashes
logs, coredumps, linux_extras (config residue + /var/crash)
# linux package managers
apt, dnf/yum, pacman, distro_pkg (zypper/apk/xbps/portage),
more_pkg (conda/mamba, guix, eopkg, swupd, opam, sdkman, gem, cpanm),
packaging_extras (flatpak/snap/nix/appimage)
# AI / LLM engines & clients
ai_tools (ollama, huggingface, compile caches, ai clients, aider)
ai_cli (claude code, codex, gemini cli, opencode — retention-aware)
# desktop-app bloat
app_cruft (electron caches, flatpak caches, gpu shaders, steam, jetbrains)
# extended system leftovers
sys_extras (/var/cache, /var/backups, crash spool, offline updates)
kernels (old-kernel purge), localizations (localepurge, opt-in)
# extended user leftovers
lang_caches (go/rust/js/.net/jvm), desktop_extras (search indexes)
virtualization (vbox/vagrant/k8s/cloud), editor_litter (swap/pycache/cores)
# backups & snapshots (report-only tier)
snapshots (timeshift/snapper/timemachine/device backups)
# macOS
macos (caches, trash, diagnostics, homebrew, macports, xcode)
macos_extra (saved state, cocoapods, sleep image)
# BSD
bsd (freebsd pkg, distfiles/work dirs, pkgin)
scheduler/
__init__.py # backend() — picks systemd or launchd for the OS
systemd.py # install/uninstall/status of a user systemd timer (Linux/BSD)
launchd.py # install/uninstall/status of a launchd LaunchAgent (macOS)
To add a cleaner: subclass Cleaner, set id/name/platforms/requires_root,
implement find_items() (read-only, yields CleanableItems), and register it in
cleaners/__init__.py. The engine handles dry-run, confirmation, root checks,
and the safety guard for you.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cleanix_cli-1.1.0.tar.gz.
File metadata
- Download URL: cleanix_cli-1.1.0.tar.gz
- Upload date:
- Size: 92.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8cc1a1149834cda2859710acc1052a3d2e43adbecc64807e80977303ffb521f3
|
|
| MD5 |
0ed2d6144080cc9ad4876f12675afc12
|
|
| BLAKE2b-256 |
eb1ed655c9602bcde338fa0046b8aa58699cd1b02e79b1c6cfff1aef1ede6033
|
Provenance
The following attestation bundles were made for cleanix_cli-1.1.0.tar.gz:
Publisher:
release.yml on CloudOpsWorld/cleanix-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cleanix_cli-1.1.0.tar.gz -
Subject digest:
8cc1a1149834cda2859710acc1052a3d2e43adbecc64807e80977303ffb521f3 - Sigstore transparency entry: 2176216397
- Sigstore integration time:
-
Permalink:
CloudOpsWorld/cleanix-cli@4125d4d8f9bb2e6d7bd55e3a71ef32f6beb006f0 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/CloudOpsWorld
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4125d4d8f9bb2e6d7bd55e3a71ef32f6beb006f0 -
Trigger Event:
push
-
Statement type:
File details
Details for the file cleanix_cli-1.1.0-py3-none-any.whl.
File metadata
- Download URL: cleanix_cli-1.1.0-py3-none-any.whl
- Upload date:
- Size: 106.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aff392a1e92408ab7296f01af635b421e009185485771aa3e4f98f0aec06bd63
|
|
| MD5 |
18739cbb650ad8020d4ab73e815cf7b8
|
|
| BLAKE2b-256 |
b83e7bc6dcdd3c2b9c83d62c855f1b51ac4539dbea9e01c510921a6f4d46f472
|
Provenance
The following attestation bundles were made for cleanix_cli-1.1.0-py3-none-any.whl:
Publisher:
release.yml on CloudOpsWorld/cleanix-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cleanix_cli-1.1.0-py3-none-any.whl -
Subject digest:
aff392a1e92408ab7296f01af635b421e009185485771aa3e4f98f0aec06bd63 - Sigstore transparency entry: 2176216511
- Sigstore integration time:
-
Permalink:
CloudOpsWorld/cleanix-cli@4125d4d8f9bb2e6d7bd55e3a71ef32f6beb006f0 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/CloudOpsWorld
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4125d4d8f9bb2e6d7bd55e3a71ef32f6beb006f0 -
Trigger Event:
push
-
Statement type: