🧹 One command to find and remove unused Docker containers, images, volumes, and networks on your Proxmox host.
Dry-run first, backup automatically, protect what matters. Install once, schedule with cron, and forget about it.
Quick Start • Features • Configuration • CLI Reference • Troubleshooting
Running Docker on a Proxmox host tends to leave behind unused containers, images, volumes, and networks — quietly eating disk space. Proxmox Cleanup finds and removes them, with a few safety nets: a metadata manifest written before anything is deleted, a dry-run you can preview first, protection patterns for the things you want to keep, and dependency checks so nothing in use gets touched.
One-line install, schedule it with cron, then forget about it.
Prerequisites: Node.js 18+, npm, Docker daemon running, Proxmox VE (optional for Proxmox integration).
curl -fsSL https://raw.githubusercontent.com/hiall-fyi/proxmox-cleanup/main/scripts/install.sh | bashThe installer handles Node.js dependencies, build, global CLI setup, systemd service, config files, and log rotation.
Successful installation with all components configured
Install from Source
git clone https://github.com/hiall-fyi/proxmox-cleanup.git
cd proxmox-cleanup
npm install
npm run build
npm install -g . # optional: puts `proxmox-cleanup` on your PATHWithout the last line, run it as node dist/cli/index.js from the clone.
nano /etc/proxmox-cleanup/config.jsonproxmox-cleanup dry-run -c /etc/proxmox-cleanup/config.jsonproxmox-cleanup cleanup -c /etc/proxmox-cleanup/config.json
Real-world results: 38 resources scanned, 1.02 GB freed in 2.3 seconds
- Automated Docker cleanup — remove unused containers, images, volumes, and networks in one command
- Safety first — backup before cleanup, dry-run mode, protected resource patterns, dependency checking
- Well tested — property-based tests with
fast-check, structured logging, explicit error reporting - Proxmox VE friendly — tested on Proxmox VE 8.x running Docker on the host
- Scheduled runs via systemd or cron — the installer registers a systemd unit you can drive from a timer or system cron
- Readable reports — disk space freed, execution time, what was kept or skipped and why
- CLI with the common commands you'd expect —
cleanup,dry-run,list,validate-config, plus the usual flags
| Type | What Gets Cleaned |
|---|---|
| containers | Containers that have exited, were created but never started, or are dead |
| images | Images not used by any container |
| volumes | Volumes not mounted by any container |
| networks | Networks with no attached containers (excluding defaults) |
Containers that are running, paused, restarting, or mid-removal are never touched. A paused container is a live workload, so it's left alone and reported as skipped with the reason.
- Dependency Checking — Containers using images, volumes mounted by containers, and networks with attached containers are all protected
- Protected Resources — System networks (bridge, host, none), resources matching protection patterns, tagged resources
- Backup Manifest — Before deleting anything, resource metadata (names, IDs, sizes, dependencies) is written to a JSON file. It's a record for manual recovery, not a restore point: metadata can't bring back a volume's contents or an image's layers. A
restorecommand that re-pulls images by name is planned. - Dry-Run Mode — Preview all operations without making changes. The report and the log both say "would remove", so a preview can't be mistaken for a completed cleanup
Create a config.json file (see config.example.json):
{
"proxmox": {
"host": "proxmox.example.com",
"token": "root@pam:your-api-token"
},
"cleanup": {
"dryRun": false,
"resourceTypes": [],
"protectedPatterns": ["important-*", "system-*"],
"backupEnabled": true,
"backupPath": "./backups",
"minAge": "7d",
"keepBackups": 30
},
"reporting": {
"verbose": true,
"logPath": "./logs",
"keepReports": 30
}
}All configuration options can be overridden via CLI flags. A flag only takes effect when you actually pass it, so settings in your config file are used otherwise.
File retention: each run writes a report and a summary to the log directory,
and each cleanup writes a backup manifest. keepReports and keepBackups
(default 30 each) bound how many of these are kept — the oldest are deleted once
the limit is passed. The log files themselves rotate separately.
Age filtering: The optional minAge setting (or --older-than CLI flag) accepts a duration like 7d (7 days), 12h (12 hours), 30m (30 minutes). Only resources older than this are removed — age is how long ago the resource was created, not last-used. Accepted units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
Volumes and creation time: Docker volumes often report no creation time. When a resource's creation time is unavailable, --older-than skips it entirely and cleanup, dry-run, and list all show a separate count of these skipped resources. This keeps the tool safe by default — if the Engine can't tell you when a volume was created, the age filter won't guess.
Volume and network sizes: the Docker Engine doesn't report either, so they show as "size unknown" rather than 0 B. A volume listed for cleanup may still hold a lot of data.
The installer registers proxmox-cleanup.service as a systemd unit. Drive it from a systemd timer or regular cron:
# /etc/systemd/system/proxmox-cleanup.timer
[Unit]
Description=Run Proxmox Cleanup daily
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.targetsystemctl enable --now proxmox-cleanup.timer| Pattern | Schedule |
|---|---|
0 2 * * * |
Daily at 2 AM |
0 */6 * * * |
Every 6 hours |
0 0 * * 0 |
Weekly on Sunday |
0 0 1 * * |
Monthly on 1st |
Protect resources from cleanup using patterns:
- Wildcards:
important-*,*-production,*-system-*(matched against the name) - Exact names:
my-important-container - Labels:
tag:backupmatches any resource carrying that label;tag:env=productionmatches the key and value - IDs:
id:ec3f0931a6e6(the short IDdocker imagesanddocker psprint), or the fullsha256:…digest. Prefixes shorter than 12 characters are rejected as ambiguous.
Untagged ("dangling") images: these have no meaningful name, so a name pattern
like important-* will never match one. To keep a specific dangling image, protect
it by ID, by a label, or by a wildcard on its short ID (*ec3f0931a6e6*) — the
short ID appears in how the tool displays it.
Prefixes are lower-case and exact (tag:, id:). If a pattern can't possibly
match anything — a misspelled prefix like tags:, an ID prefix too short to be
unambiguous, or a prefix with nothing after it — the command stops and tells you
which pattern is wrong instead of running with a protection list that does
nothing.
Execute cleanup of unused Docker resources.
proxmox-cleanup cleanup [options]
Options:
-d, --dry-run Preview without removing
-t, --types <types> Resource types (containers,images,volumes,networks)
-p, --protect <patterns> Protection patterns (wildcards supported)
-b, --backup Create backup (default: true)
--no-backup Disable backup
--backup-path <path> Custom backup directory (default: ./backups)
-c, --config <path> Configuration file path
-v, --verbose Enable verbose logging
--proxmox-host <host> Proxmox host address
--proxmox-token <token> Proxmox API token
--older-than <duration> Only remove resources older than this (e.g. 7d, 12h)
--json Output JSON only (suppresses human-readable output)Preview what would be removed without making changes.
proxmox-cleanup dry-run [options]
Options:
-t, --types <types> Resource types to scan (containers,images,volumes,networks)
-p, --protect <patterns> Protection patterns (wildcards supported)
-c, --config <path> Path to configuration file
-v, --verbose Enable verbose logging
--log-path <path> Custom log directory path (default: ./logs)
--proxmox-host <host> Proxmox host address
--proxmox-token <token> Proxmox API token
--older-than <duration> Only remove resources older than this (e.g. 7d, 12h)
--json Output JSON only (suppresses human-readable output)List unused Docker resources without removing them. Results are grouped by type and sorted largest-first.
proxmox-cleanup list [options]
Options:
-t, --types <types> Resource types to list (containers,images,volumes,networks)
-p, --protect <patterns> Protection patterns (wildcards supported)
-c, --config <path> Path to configuration file
--older-than <duration> Only list resources older than this (e.g. 7d, 12h)
--json Output JSON only (suppresses human-readable output)Validate configuration file and test connections.
proxmox-cleanup validate-config -c /etc/proxmox-cleanup/config.json
Options:
-c, --config <path> Path to configuration file
--json Output JSON only (suppresses human-readable output)| Code | Meaning |
|---|---|
| 0 | Everything the tool set out to do succeeded. Resources that were skipped (protected, still in use, or undatable) are a success, not a failure. |
| 1 | The run couldn't proceed (Docker unreachable, bad config, invalid --older-than), or at least one resource failed to remove. Failures are itemised in the report. |
If you pass -c with a path that doesn't exist, the command fails rather than
falling back to defaults — otherwise a typo would run a cleanup with none of
your protection patterns.
# Preview what would be removed
proxmox-cleanup dry-run
# Preview specific resource types
proxmox-cleanup dry-run --types containers,images
# Clean all unused resources with backup
proxmox-cleanup cleanup
# Clean specific types without backup
proxmox-cleanup cleanup --types volumes --no-backup
# List all unused resources (sorted largest-first)
proxmox-cleanup list
# Only clean resources created more than 7 days ago
proxmox-cleanup cleanup --older-than 7d
# Machine-readable JSON output for scripting
proxmox-cleanup list --json > unused-resources.json
# Verbose mode for troubleshooting
proxmox-cleanup cleanup --verbose -c /etc/proxmox-cleanup/config.jsonproxmox-cleanup/
├── src/
│ ├── types/ # TypeScript type definitions
│ ├── interfaces/ # Interface contracts
│ ├── clients/ # Docker & Proxmox API clients
│ ├── scanners/ # Resource scanning logic
│ ├── utils/ # Utility functions
│ ├── managers/ # Backup management
│ ├── reporters/ # Report generation
│ ├── orchestrators/ # Main workflow coordination
│ └── cli/ # Command-line interface
├── config.example.json # Example configuration
└── README.md
192 tests. Property-based testing with fast-check (100+ random inputs per property) covering resource identification, safe removal guarantees, backup completeness, size calculation accuracy, and report consistency, alongside unit tests for the clients, scanner, orchestrator, reporter, and backup manager. All seven Docker container states are covered, and a state the tool doesn't recognise is treated as not removable.
npm test # Run the full suite
npm run test:coverage # Run with coverage
npm run build # Build
npm run lint # LintingDocker daemon not running
systemctl status docker
systemctl start dockerPermission denied errors
sudo usermod -aG docker $USER
newgrp dockerConfiguration validation failed
proxmox-cleanup validate-config -c /etc/proxmox-cleanup/config.json
tail -f /var/log/proxmox-cleanup/cleanup.logFor other issues, use the --verbose flag for detailed logging and check the logs in your configured log directory. Still stuck? Open an issue — the form asks for the few things that pin most problems down. For usage questions, Discussions is the better place.
If something was removed that you wanted to keep, the report in your log directory lists exactly what went, and the backup manifest in your backup directory records its metadata. Both are worth attaching to the issue.
MIT License — Free to use, modify, and distribute. See LICENSE for full details.
Made with ❤️ by Joe Yiu (@hiall-fyi)
Contributions welcome!
- Fork the repository
- Create feature branch (
git checkout -b feature/AmazingFeature) - Commit changes (
git commit -m 'Add AmazingFeature') - Push to branch (
git push origin feature/AmazingFeature) - Open a Pull Request
Disclaimer
This project is not affiliated with, endorsed by, or connected to Proxmox Server Solutions GmbH or Docker, Inc. Proxmox and the Proxmox logo are registered trademarks of Proxmox Server Solutions GmbH. Docker and the Docker logo are registered trademarks of Docker, Inc. All product names, logos, and brands are property of their respective owners.
This tool is provided "as is" without warranty of any kind. Use at your own risk.
See CHANGELOG.md for version history.