╔══════════════════════════════════════════════════════════════╗
║ ║
║ ██████╗ ██████╗ ██████╗██╗ ██╗███████╗██████╗ ║
║ ██╔══██╗██╔═══██╗██╔════╝██║ ██╔╝██╔════╝██╔══██╗ ║
║ ██║ ██║██║ ██║██║ █████╔╝ █████╗ ██████╔╝ ║
║ ██║ ██║██║ ██║██║ ██╔═██╗ ██╔══╝ ██╔══██╗ ║
║ ██████╔╝╚██████╔╝╚██████╗██║ ██╗███████╗██║ ██║ ║
║ ╚═════╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ║
║ ║
║ ██████╗██╗ ███████╗ █████╗ ███╗ ██╗ ║
║ ██╔════╝██║ ██╔════╝██╔══██╗████╗ ██║ ║
║ ██║ ██║ █████╗ ███████║██╔██╗ ██║ ║
║ ██║ ██║ ██╔══╝ ██╔══██║██║╚██╗██║ ║
║ ╚██████╗███████╗███████╗██║ ██║██║ ╚████║ ║
║ ╚═════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝ ║
║ ║
╚══════════════════════════════════════════════════════════════╝
Reclaim disk space instantly with secure, one-shot Docker cleanup
| 🎯 Feature | 📊 Details |
|---|---|
| Execution Mode | One-shot (cron-ready) |
| Security | Non-root, GID matching, no --privileged |
| Configurability | 15+ environment variables |
| Multi-Host | Docker context support |
| Safety | DRY_RUN mode, conservative defaults |
| Performance | ~50MB memory, 30s-4min execution |
| Platforms | Linux, macOS (Docker daemon required) |
Automated Docker cleanup container that reclaims disk space by removing unused containers, images, volumes, networks, and build cache. Designed for one-shot execution with secure, non-privileged access to Docker daemon.
🔑 Key Advantages:
--privileged mode neededDocker accumulates disk space quickly:
docker-cleaner provides:
| Feature | docker-cleaner | docker system prune | Manual cleanup |
|---|---|---|---|
| One command | ✅ | ✅ | ❌ |
| Scheduled execution | ✅ | ❌ | ❌ |
| Multi-host support | ✅ | ❌ | ❌ |
| Non-root security | ✅ | N/A | N/A |
| Comprehensive logging | ✅ | ⚠️ | ❌ |
| Configuration flexibility | ✅ | ⚠️ | ✅ |
| DRY_RUN preview | ✅ | ⚠️ | ❌ |
# Pull latest version
docker pull rinzlerfr/docker-cleaner:latest
# Run complete cleanup
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-e PRUNE_ALL=true \
-e PRUNE_VOLUMES=true \
-e CLEANUP_VOLUMES=true \
rinzlerfr/docker-cleaner:latest
# Clone repository
git clone https://github.com/rinzlerfr/docker-cleaner.git
cd docker-cleaner
# Build image
docker build -t docker-cleaner .
# Run cleanup
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-e PRUNE_ALL=true \
docker-cleaner
# Test with DRY_RUN mode (recommended first time)
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-e PRUNE_ALL=true \
-e PRUNE_VOLUMES=true \
-e CLEANUP_VOLUMES=true \
-e DRY_RUN=true \
rinzlerfr/docker-cleaner:latest
Expected output:
🧹 Docker Cleanup Container v1.0.0
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚙️ Configuration:
• Mode: DRY_RUN (no deletion)
• Cleanup: containers, images (all), volumes, networks, cache
• Filters: none
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🔍 [DRY_RUN] Would remove 15 stopped containers
🔍 [DRY_RUN] Would remove 8 unused images (2.3 GB)
🔍 [DRY_RUN] Would remove 3 unused volumes (450 MB)
🔍 [DRY_RUN] Would remove 2 unused networks
🔍 [DRY_RUN] Would remove build cache (1.1 GB)
📊 Total space reclaimable: 3.85 GB
✅ Dry-run completed successfully
# FULL CLEANUP - Cleans EVERYTHING (recommended)
docker-compose --profile full up docker-cleaner-full
# Default cleanup (conservative - volumes protected)
docker-compose up docker-cleaner
# Dry-run mode (preview without deletion)
docker-compose --profile dryrun up docker-cleaner-dryrun
# Complete cleanup in one pass
./examples/cleanup-all.sh
# Complete guaranteed cleanup (2 passes - removes orphaned images)
./examples/cleanup-complete.sh
# Test with dry-run
DRY_RUN=true ./examples/cleanup-all.sh
┌─────────────────────────────────────────────────────────┐
│ HOST MACHINE │
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ Docker Daemon (dockerd) │ │
│ │ │ │
│ │ /var/run/docker.sock (GID: 998) │ │
│ └─────────────┬────────────────────────────┘ │
│ │ Socket Access │
│ │ (via GID matching) │
│ ┌─────────────▼────────────────────────────┐ │
│ │ docker-cleaner Container │ │
│ │ ┌────────────────────────────────────┐ │ │
│ │ │ 1. Detect socket GID (998) │ │ │
│ │ │ 2. Create group with GID 998 │ │ │
│ │ │ 3. Add user to group │ │ │
│ │ │ 4. Execute cleanup as non-root │ │ │
│ │ └────────────────────────────────────┘ │ │
│ │ │ │
│ │ User: cleaner (UID 1000) ✅ Non-root │ │
│ │ Group: docker (GID 998) ✅ Socket access│ │
│ └───────────────────────────────────────────┘ │
│ │
│ Result: Secure cleanup without --privileged │
└─────────────────────────────────────────────────────────┘
⚠️ IMPORTANT: The container always runs on the host to be cleaned - it never connects to remote hosts.
When you use Docker contexts (e.g., docker context use nas), the Docker
client on your machine sends the docker run command to the remote daemon,
which then executes the container locally on that host. The volume mount
/var/run/docker.sock:/var/run/docker.sock is interpreted by the execution
host's daemon, not your client machine.
In other words: If you run this container while in NAS context, the container runs ON the NAS and mounts the NAS's socket, cleaning the NAS's Docker resources. Your client machine only displays the output.
Instead of requiring --privileged mode, the container uses dynamic GID
matching:
/var/run/docker.sock at startupThis provides secure, non-root access to the Docker daemon without privileged mode.
Cleans EVERYTHING except running containers and volumes in use:
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
-e PRUNE_ALL=true \
-e PRUNE_VOLUMES=true \
-e CLEANUP_VOLUMES=true \
rinzlerfr/docker-cleaner:latest
What gets removed:
What is protected:
⚠️ Important note: Docker checks image usage AT THE TIME of pruning. If you have stopped containers that are removed by cleanup, their base images remain because they were referenced at the time of verification. To remove these orphaned images, simply run docker-cleaner a second time.
Conservative cleanup that protects volumes:
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
rinzlerfr/docker-cleaner:latest
What gets removed:
What is protected:
All configuration is done via environment variables:
| Variable | Default | Description |
|---|---|---|
PRUNE_ALL | false | Remove all unused images (not just dangling) |
PRUNE_VOLUMES | false | Include volumes in cleanup ⚠️ DANGER |
PRUNE_FORCE | true | Skip confirmation prompts |
| Variable | Default | Description |
|---|---|---|
CLEANUP_CONTAINERS | true | Prune stopped containers |
CLEANUP_IMAGES | true | Prune unused images |
CLEANUP_VOLUMES | false | Prune unused volumes ⚠️ |
CLEANUP_NETWORKS | true | Prune unused networks |
CLEANUP_BUILD_CACHE | true | Prune build cache |
| Variable | Default | Description |
|---|---|---|
PRUNE_FILTER_UNTIL | (none) | Remove resources older than duration |
PRUNE_FILTER_LABEL | (none) | Filter by label (e.g., keep!=true) |
| Variable | Default | Description |
|---|---|---|
LOG_LEVEL | INFO | Log level: DEBUG, INFO, WARN, ERROR |
LOG_FORMAT | text | Output format: text or json |
QUIET | false | Minimal output |
| Variable | Default | Description |
|---|---|---|
DRY_RUN | false | Preview without deleting |
# One-time cleanup
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
rinzlerfr/docker-cleaner:latest
# Weekly cron job
0 2 * * 0 docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
rinzlerfr/docker-cleaner:latest
Option A: Using --context flag (Recommended)
# Run cleanup on NAS without switching context
docker --context nas run --rm -v /var/run/docker.sock:/var/run/docker.sock \
rinzlerfr/docker-cleaner:latest
# Your active context remains unchanged
Option B: Using context switching
# Switch to NAS context
docker context use nas
# Run cleanup (executes on NAS, cleans NAS Docker)
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
rinzlerfr/docker-cleaner:latest
# Switch back
docker context use default
cleanup:
stage: post-build
script:
- docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
rinzlerfr/docker-cleaner:latest
name: Docker Cleanup
on:
schedule:
- cron: '0 2 * * *'
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- name: Run Docker Cleanup
run: |
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-e PRUNE_FILTER_UNTIL=168h \
rinzlerfr/docker-cleaner:latest
# Define contexts for each host
docker context create dev-server --docker "host=ssh://dev-server"
docker context create staging-server --docker "host=ssh://staging-server"
# Cleanup script for all hosts (using --context flag, recommended)
for ctx in default dev-server staging-server; do
echo "Cleaning $ctx..."
docker --context $ctx run --rm -v /var/run/docker.sock:/var/run/docker.sock \
rinzlerfr/docker-cleaner:latest
done
# No need to restore context - it was never changed!
To use docker-cleaner on remote Docker hosts via contexts, you need to configure remote access to the Docker daemon.
IMPORTANT: Remote Docker hosts must expose the Docker API on port 2375 (TCP) or 2376 (TLS).
The easiest and safest way to expose Docker remotely without modifying daemon configuration:
# On the remote Docker host, run:
docker run -d \
--name docker-socat \
--restart unless-stopped \
--network host \
-v /var/run/docker.sock:/var/run/docker.sock \
alpine/socat \
tcp-listen:2375,fork,reuseaddr unix-connect:/var/run/docker.sock
Security Notes:
Verify it's working:
# On remote host
docker ps | grep docker-socat
# From local machine
curl http://remote-host:2375/version
For production environments, use SSH-based contexts instead:
# No special configuration needed on remote host
# Just ensure SSH access is available
# Create SSH-based context
docker context create remote-nas \
--docker "host=ssh://user@remote-host"
# Test it
docker --context remote-nas ps
Advantages:
Once the remote host is configured (socat or SSH), create a Docker context:
# For TCP access (with socat)
docker context create nas \
--docker "host=tcp://nas.local:2375"
# For SSH access (recommended)
docker context create nas \
--docker "host=ssh://[email protected]"
# Verify the context
docker context ls
docker --context nas ps
# Run cleanup on remote host via context
docker --context nas run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
rinzlerfr/docker-cleaner:latest
For more details on context setup and security considerations, see docs/testing-guide.md.
$ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
-e PRUNE_ALL=true -e PRUNE_VOLUMES=true -e CLEANUP_VOLUMES=true \
rinzlerfr/docker-cleaner:latest
🧹 Docker Cleanup Container v1.0.0
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚙️ Configuration:
• Mode: CLEANUP (deletion enabled)
• Cleanup: containers, images (all), volumes, networks, cache
• Filters: none
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🗑️ Removing stopped containers...
✓ Removed 15 containers
🗑️ Removing unused images (all)...
✓ Removed 8 images
✓ Reclaimed 2.3 GB
🗑️ Removing unused volumes...
✓ Removed 3 volumes
✓ Reclaimed 450 MB
🗑️ Removing unused networks...
✓ Removed 2 networks
🗑️ Removing build cache...
✓ Removed cache
✓ Reclaimed 1.1 GB
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 Summary:
• Total space reclaimed: 3.85 GB
• Operations: 5/5 successful
• Exit code: 0
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ Cleanup completed successfully
⚠️ WARNING: Mounting docker.sock grants equivalent root access to
the host.
Least Privilege Execution
--privileged flag requiredResource Protection
Audit Trail
Best Practices
DRY_RUN=true firstFor detailed security documentation, see docs/security-guide.md.
Expected execution times vary based on resource count:
| Resource Count | Estimated Time |
|---|---|
| Small (<50) | ~30 seconds |
| Medium (50-200) | ~1 minute |
| Large (200-1000) | ~2 minutes |
| Very Large (>1000) | ~4 minutes |
Memory usage: ~50MB peak
0: Success (all operations succeeded)1: Partial failure (some operations failed, some succeeded)2: Complete failure (cannot connect to Docker or all operations failed)The docker-cleaner image is automatically published to Docker Hub with each release:
# Pull latest version
docker pull rinzlerfr/docker-cleaner:latest
# Pull specific version
docker pull rinzlerfr/docker-cleaner:v1.0.0
# Run from Docker Hub
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
rinzlerfr/docker-cleaner:latest
latest - Latest stable releasev1.0.0, v1.0, v1 - Semantic version tagsamd64, arm64Releases are triggered automatically when you push a version tag:
# Create and push a release tag
git tag -a v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0
This triggers a GitHub Action that:
docker build -t docker-cleaner .
docker-cleaner includes a comprehensive testing framework that validates cleanup operations across different execution contexts:
# Run complete test suite (local + container + remote)
./tests/99-run-all-tests.sh
# Run only local script tests
./tests/99-run-all-tests.sh --only-local
# Run only container tests
./tests/99-run-all-tests.sh --only-container
# Run specific test types
./tests/11-test-local-cleanup.sh # Local script testing
./tests/12-test-container-cleanup.sh # Container testing
./tests/13-test-remote-contexts.sh # Remote context testing
# Test with different configurations
./tests/11-test-local-cleanup.sh --prune-all --prune-volumes
./tests/11-test-local-cleanup.sh --dry-run
# Test on specific Docker context
./tests/99-run-all-tests.sh --context remote-nas
# Create test resources for manual testing
./tests/01-setup-test-resources.sh
# Cleanup test resources
./tests/03-cleanup-test-resources.sh
# Validate cleanup operations
./tests/02-vali
Content type
Image
Digest
sha256:47adb508e…
Size
74.7 MB
Last updated
11 months ago
docker pull rinzlerfr/docker-cleaner