MrZaKaRiAGit

Prunemate

Docker image & resource cleanup helper, on a schedule!

Created: 12/22/2025Last pushed: 6/22/2026
Prunemate

Overview

Docker image & resource cleanup helper, on a schedule!

Tags & Topics
DockerHTML
View Source

PruneMate

prunemate-logo

PruneMate

Docker image & resource cleanup helper, on a schedule!

Buy Me a Coffee Website

A sleek, lightweight web interface to automatically clean up Docker resources on a schedule. Built with Python (Flask) · Docker SDK · APScheduler · Gunicorn

Keep your Docker host tidy with scheduled cleanup of unused images, containers, networks, and volumes.

⚠️ DISCLAIMER: PruneMate uses Docker's native prune commands to delete unused resources. This means it removes containers, images, networks, and volumes that Docker considers "unused" - be careful with volumes as they may contain important data. Ensure you understand what will be pruned before enabling automated schedules. The author is not responsible for any data loss or system issues. Use at your own risk.


✨ Features

  • 🕐 Flexible scheduling - Daily, Weekly, or Monthly cleanup runs with optional manual-only mode
  • 🔀 Schedule control toggle - Enable/disable automatic scheduling, so PruneMate only runs manually
  • 🔍 Prune preview - See exactly what will be deleted before executing manual prune operations
  • 🌍 Timezone aware - Configure your local timezone
  • 🕒 12/24-hour time format - Choose your preferred time display
  • 🐳 Multi-host support - Manage multiple Docker hosts from one interface (requires docker-socket-proxy on remote hosts)
  • 🧹 Selective cleanup - Choose what to prune: containers, images, networks, volumes, build cache
  • 🏗️ Build cache cleanup - Reclaim significant space by pruning Docker builder cache (often 10GB+)
  • 📊 All-Time Statistics - Track cumulative space reclaimed and resources deleted across all runs
  • 🏠 Homepage integration - Display statistics in your Homepage dashboard (works with authentication enabled)
  • 🎨 Modern UI - Dark theme with smooth animations and responsive design
  • 🔒 Secure authentication - Optional login protection with password hashing and Basic Auth support
  • 🏗️ Multi-architecture support - Native amd64 and arm64 Docker images (Intel/AMD, Raspberry Pi, Apple Silicon)
  • 🔒 Safe & controlled - Manual trigger with preview and detailed logging
  • 📈 Detailed reports - See exactly what was cleaned and how much space was reclaimed

📷 Screenshots

Main Dashboard

The overall look and feel of the PruneMate dashboard

Authentication page

The login page, (when enabled in the docker-compose.yaml environment variables)

External Docker hosts

Add external Docker hosts via docker-socket-proxy

prunemate-cleanup

Notification Settings

Set up notifications via Gotify, ntfy.sh, Discord, or Telegram to stay informed about cleanup results.

prunemate-notifications

Prune preview

A brief interface that shows which Docker resources will be pruned during the next cleanup run, either manually triggered or scheduled.

prunemate-preview


🚀 Quick Start with Docker Compose

Prerequisites

  • Docker and Docker Compose installed
  • Access to Docker socket (/var/run/docker.sock)

Installation

  1. Create a docker-compose.yaml file:
services:
  prunemate:
    image: anoniemerd/prunemate:latest  # Supports amd64 and arm64
    container_name: prunemate
    ports:
      - "7676:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./logs:/var/log
      - ./config:/config
    environment:
      - PRUNEMATE_TZ=Europe/Amsterdam # Change this to your desired timezone
      - PRUNEMATE_TIME_24H=true #false for 12-Hour format (AM/PM)
      # Optional: Enable authentication (generate hash with: docker run --rm anoniemerd/prunemate python prunemate.py --gen-hash "password")
      # - PRUNEMATE_AUTH_USER=admin
      # - PRUNEMATE_AUTH_PASSWORD_HASH=your_base64_encoded_hash_here
    restart: unless-stopped
  1. Start PruneMate:
docker-compose up -d
  1. Access the web UI of PruneMate:

Open your browser and navigate to:

http://<your-server-ip>:7676/

🚀 Quick Start with Docker Run

Using Docker CLI:

docker run -d \
  --name prunemate \
  -p 7676:8080 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v $(pwd)/logs:/var/log \
  -v $(pwd)/config:/config \
  -e PRUNEMATE_TZ=Europe/Amsterdam \
  -e PRUNEMATE_TIME_24H=true \
  --restart unless-stopped \
  anoniemerd/prunemate:latest

Access the web UI:

http://<your-server-ip>:7676/

Volume explanations:

  • /var/run/docker.sock - Required for Docker API access
  • ./logs - Stores application logs (rotating, 5MB max per file)
  • ./config - Stores configuration and state files

⚙️ Configuration

Environment Variables

Variable Default Description
PRUNEMATE_TZ UTC Timezone for scheduling (e.g., Europe/Amsterdam, America/New_York)
PRUNEMATE_TIME_24H true Time format: true for 24-hour, false for 12-hour (AM/PM)
PRUNEMATE_CONFIG /config/config.json Path to configuration file
PRUNEMATE_AUTH_USER admin Username for authentication (optional, only used when auth is enabled)
PRUNEMATE_AUTH_PASSWORD_HASH (none) Base64-encoded password hash (enables authentication when set)

🔐 Authentication (Optional)

PruneMate supports optional password protection for the web interface and API endpoints.

Key features:

  • 🔒 Form-based login - Styled login page matching the app's design
  • 🔑 Secure hashing - Passwords are hashed using scrypt (industry standard)
  • 🌐 API compatibility - Basic Auth fallback for external tools (Homepage, Dashy, etc.)
  • 🚪 Logout button - Convenient session management

To enable authentication:

  1. Generate a password hash using the built-in tool:
docker run --rm anoniemerd/prunemate:latest python prunemate.py --gen-hash "your_password"

This outputs a Base64-encoded hash (safe for YAML, no special characters):

c2NyeXB0OjMyNzY4Ojg6MSRvcDdnZFlGR1JmRFp4Y1RjJDBmMzNlYzc4NzExZTI4MzllYjk0MWFiOTZkOGUyZGNjNGRhMzU2NTlmMGI1ZDg0NjhjZTdkMThhODhmNmQ3ZGRhOGU4YzdmMDYxMWZiNzAyYjA0ZGNhNTBjZWMxZjFlYzc3ZjhlNzJhYmM0MmQ3OTQ5NDM2MDUzZWRlZjlhZGY0

Why Base64? Raw scrypt hashes contain $ characters that Docker Compose interprets as environment variables, corrupting the hash. Base64 encoding produces alphanumeric strings that YAML handles safely without escaping.

✅ Special characters that work well:

  • Hash characters: #
  • At sign: @
  • Percent: %
  • Asterisk: *
  • Ampersand: &
  • Caret: ^

⚠️ Avoid these characters:

  • Exclamation mark: ! (bash history expansion)
  • Dollar sign: $ (variable expansion - even Base64 encoded, can cause issues in some contexts)

Safe examples:

  • MyPassword#123
  • Test@secure%pass
  • prunemate&admin^2024
  • MyPass*Admin#99
  1. Add to your docker-compose.yaml:
environment:
  - PRUNEMATE_AUTH_USER=admin  # Optional (default: admin)
  - PRUNEMATE_AUTH_PASSWORD_HASH=c2NyeXB0OjMyNzY4Ojg6MSRvcDdnZFlGR1JmRFp4Y1RjJDBmMzNlYzc4...
  1. Restart the container:
docker-compose up -d

Important notes:

  • Authentication is opt-in - only enabled when PRUNEMATE_AUTH_PASSWORD_HASH is set
  • Without the hash variable, the app runs in open mode (backward compatible)
  • For API clients (Homepage, etc.), use Basic Auth with your actual password (not the hash)
  • The hash is Base64-encoded to prevent Docker Compose from interpreting $ characters as variables

Web Interface Settings

Access the web interface at http://localhost:7676/ (or your server IP) to configure:

Schedule Settings:

  • Frequency: Daily, Weekly, or Monthly
  • Time: When to run the cleanup (supports both 12h and 24h format)
  • Day: Day of week (for weekly) or day of month (for monthly)

Cleanup Options:

  • ☑️ All unused containers
  • ☑️ All unused images
  • ☑️ All unused networks
  • ☑️ All unused volumes

Notification Settings:

  • Provider: Gotify, ntfy.sh, Discord, or Telegram
  • Configuration: Provider-specific credentials (URL/Token for Gotify, URL/Topic for ntfy, Webhook URL for Discord, Bot Token/Chat ID for Telegram)
  • Priority: Low (silent), Medium, or High priority notifications (provider-dependent)
  • Only notify on changes: Only send notifications when something was actually cleaned

🧠 How it works

  1. Scheduler runs every minute checking if it's time to execute
  2. Loads latest config from persistent storage
  3. Executes Docker prune commands for selected resource types
  4. Collects statistics on what was removed and space reclaimed
  5. Updates all-time statistics with cumulative data (space, counts, timestamps)
  6. Sends notification (if configured and enabled)
  7. Logs everything with timezone-aware timestamps

📊 View detailed architecture & flowchart

File Structure

/config/
├── config.json          # Your configuration (persistent)
├── stats.json           # All-time statistics (cumulative data)
├── prunemate.lock       # Prevents concurrent runs
└── last_run_key         # Tracks last successful run

/var/log/
└── prunemate.log        # Application logs (rotating, 5MB max)

All-Time Statistics

PruneMate tracks cumulative statistics across all prune runs:

Metrics tracked:

  • 💾 Total Space Reclaimed - Cumulative disk space freed (displayed in MB/GB/TB)
  • 📦 Containers Deleted - Total count of unused containers removed
  • 🖼️ Images Deleted - Total count of unused images removed
  • 🔗 Networks Deleted - Total count of unused networks removed
  • 💿 Volumes Deleted - Total count of unused volumes removed
  • 🔄 Total Prune Runs - Number of times prune has executed
  • 📅 First Run - Timestamp of the very first prune execution
  • 🕐 Last Run - Timestamp of the most recent prune execution

Technical details:

  • Statistics persist in /config/stats.json using atomic writes with file locking
  • Updates occur after every prune run, regardless of whether resources were deleted
  • Timestamps are timezone-aware and respect PRUNEMATE_TZ setting
  • Date/time display in UI follows configured 12h/24h format
  • Statistics survive container restarts and updates
  • Auto-refresh after manual prune runs via web interface

🔔 Notification Setup

Gotify

Gotify is a self-hosted notification service.

Setup steps:

  1. Install and run Gotify server
  2. Create a new application in Gotify
  3. Copy the application token
  4. Configure in PruneMate:
    • Provider: Gotify
    • URL: https://your-gotify-server.com
    • Token: Your application token

ntfy.sh

ntfy.sh is a simple pub-sub notification service (self-hosted or public).

Setup steps:

  1. Choose a unique topic name (e.g., prunemate-alerts)
  2. Configure in PruneMate:
    • Provider: ntfy
    • URL: https://ntfy.sh (or your self-hosted instance, supports username:password@host format)
    • Topic: Your chosen topic name
    • Token: (Optional) Bearer token for authentication

Authentication options:

  • Bearer token: Recommended for API access tokens (higher priority)
  • URL credentials: Use https://username:password@ntfy.example.com format (RFC 3986 compliant)
  • No authentication: Works with public topics

Subscribe to notifications:

  • Web: Visit https://ntfy.sh/your-topic
  • Mobile: Install the ntfy app (Android / iOS) and subscribe to your topic
  • Desktop: Use ntfy desktop app or web browser

Discord

Discord webhooks allow notifications directly to your Discord server.

Setup steps:

  1. Open your Discord server settings
  2. Go to IntegrationsWebhooks
  3. Click New Webhook or edit existing webhook
  4. Copy the Webhook URL
  5. Configure in PruneMate:
    • Provider: Discord
    • Webhook URL: https://discord.com/api/webhooks/...

Priority colors:

  • Low: Green (informational)
  • Medium: Orange (warning)
  • High: Red (critical)

Telegram

Telegram Bot API enables notifications via Telegram bots.

Setup steps:

  1. Open Telegram and search for @BotFather
  2. Send /newbot and follow the instructions
  3. Give your bot a name (e.g., "PruneMate Notifications")
  4. Give your bot a username ending in "bot" (e.g., "prunemate_notif_bot")
  5. Copy the Bot Token (format: 123456789:ABCdefGHIjklMNOpqrsTUVwxyz)
  6. Get your Chat ID:
    • Easy method: Message @userinfobot or @getmyid_bot to get your Chat ID
    • Alternative: Message your bot, then visit https://api.telegram.org/bot<BOT_TOKEN>/getUpdates and find "chat":{"id":123456789}
  7. Configure in PruneMate:
    • Provider: Telegram
    • Bot Token: Your bot token from BotFather
    • Chat ID: Your numeric chat ID (or @channelname for channels)

Priority behavior:

  • Low: Silent notifications (no sound)
  • Medium/High: Normal notifications with sound

Advanced usage:

  • Groups: Add bot to group, get group Chat ID (starts with -)
  • Channels: Use channel username with @ (e.g., @mychannel) or numeric ID

🌐 Multi-Host Setup (Optional)

PruneMate can manage multiple Docker hosts from a single interface. Each prune operation runs across all enabled hosts with aggregated results.

Security First: Use Docker Socket Proxy

⚠️ Never expose Docker sockets directly! Always use docker-socket-proxy to limit API access.

Quick Setup

1. Deploy proxy on each remote host:

services:
  dockerproxy:
    image: ghcr.io/tecnativa/docker-socket-proxy:latest
    environment:
      - CONTAINERS=1
      - IMAGES=1
      - NETWORKS=1
      - VOLUMES=1
      - BUILD=1         # REQUIRED FOR BUILD CACHE PRUNE
      - POST=1          # Required for prune operations
    ports:
      - "2375:2375"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    restart: unless-stopped

⚠️ IMPORTANT: The BUILD=1 environment variable is REQUIRED to enable Docker build cache pruning. Without it, build cache prune operations will fail with a 403 error.

2. Add hosts in PruneMate UI:

  • Navigate to Docker Hosts section
  • Click Add New Host
  • Enter name (e.g., "NAS") and URL (e.g., tcp://192.168.1.50:2375)
  • Toggle hosts on/off as needed

3. Test connection: Click Run now and check logs for successful connection to all hosts.

Troubleshooting

  • Connection refused: Verify proxy is running (docker ps) and port 2375 is accessible
  • Permission denied: Ensure proxy has POST=1 environment variable
  • Host skipped: Check URL format starts with tcp://, http://, or https://

🏠 Homepage Dashboard Integration

PruneMate provides a custom API endpoint at /api/stats that returns all-time statistics in a format compatible with Homepage dashboard widgets.

prunemate-homepage

Setup

Add this configuration to your Homepage services.yaml:

- PruneMate:
    href: http://<your-server-ip>:7676
    description: Docker Cleanup Automation
    icon: https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/prunemate.webp
    widget:
      type: customapi
      url: http://<your-server-ip>:7676/api/stats
      mappings:
        - field: pruneRuns
          label: Prune Runs
          format: number
        - field: lastRunText
          label: Last Run
        - field: imagesDeleted
          label: Images Pruned
          format: number
        - field: spaceReclaimedHuman
          label: Space Saved

Available Fields

The /api/stats endpoint returns the following fields:

Field Type Description Homepage Format
pruneRuns number Total number of prune operations executed number
containersDeleted number Total containers deleted across all runs number
imagesDeleted number Total images deleted across all runs number
networksDeleted number Total networks deleted across all runs number
volumesDeleted number Total volumes deleted across all runs number
buildCacheDeleted number Total build cache entries deleted across all runs number
spaceReclaimed number Total space reclaimed in bytes number
spaceReclaimedHuman string Human-readable space reclaimed (e.g., "2.5 GB") text
lastRunText string Relative time as text (e.g., "2h ago") text
lastRunTimestamp number Unix timestamp in seconds of last run number
lastRun string ISO timestamp of most recent prune run date
firstRun string ISO timestamp of first prune run date

Example /api/stats output

{
  "pruneRuns": 42,
  "containersDeleted": 156,
  "imagesDeleted": 89,
  "networksDeleted": 12,
  "volumesDeleted": 7,
  "buildCacheDeleted": 715,
  "spaceReclaimed": 5368709120,
  "spaceReclaimedHuman": "5.00 GB",
  "lastRunText": "2h ago",
  "lastRunTimestamp": 1733454000,
  "lastRun": "2025-12-06T03:00:00+01:00",
  "firstRun": "2025-01-15T03:00:00+01:00"
}

🧠 Troubleshooting

Problem Solution
❌ Can't access web interface • Check if port 7676 is available and not blocked by firewall
• Verify container is running: docker ps
• Check logs: docker logs prunemate
🏗️ ARM architecture error • Since V1.3.1+: Image now has native multi-architecture support (amd64 + arm64)
• Pull anoniemerd/prunemate:latest - it will auto-detect your platform
• No local build required anymore!
• If running older versions, use build: . in docker-compose.yaml
⚙️ Container not starting • View startup errors: docker logs prunemate
• Verify Docker socket is accessible
• Check if port 7676 is already in use
🔒 Permission denied errors • Ensure /var/run/docker.sock exists and is accessible
• On Linux, Docker daemon must be running
• User running Docker must have proper permissions
🕐 Wrong timezone in logs/schedule • Set PRUNEMATE_TZ environment variable correctly
• Restart container after changing: docker-compose restart
• Verify timezone in logs matches expected
📧 Notifications not working • Test notification settings in web interface
• Verify notification server URL is accessible
• Check token/topic is correct
• Review logs for error messages
🗂️ Configuration not persisting • Ensure ./config volume is mounted correctly
• Check file permissions on host ./config directory
• Verify container has write access
🧹 Cleanup not running on schedule • Check schedule configuration in web interface
• Verify timezone is set correctly
• Review logs: "Next scheduled run" messages
• Ensure container is running continuously

Logging

What the logs contain:

  • ✅ Scheduler heartbeats (every minute)
  • 📝 Configuration changes
  • 🧹 Prune job executions with results
  • 📨 Notification delivery status
  • ❌ Error messages and warnings

📜 Release Notes

[V1.3.3] - Ferbruary 2026

Changed

  • 🏗️ New info button - A new information button has been added to the newly launched Prunemate.org website.

📖 View full changelog


📬 Support

Have questions or need help?


☕ Support the Project

If you find PruneMate useful and would like to support the development, consider buying me a coffee!

Buy Me a Coffee

Your support helps me dedicate more time to maintaining and improving PruneMate! ❤️


👤 Author & License

Author: Anoniemerd
🐙 GitHub: https://github.com/anoniemerd
📦 Repository: https://github.com/anoniemerd/PruneMate


👥 Contributors

I'm grateful for the contributions that make PruneMate better!

Contributors

  • @difagume - 🔐 Authentication system implementation (V1.3.0)
  • @shollyethan - 🎨 Logo redesign & added the logo to Self-Hosted Dashboard Icons

Project maintainer/owner


📜 License — AGPLv3

This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).

By using, modifying, or distributing this software, you must:

  • Keep this copyright notice
  • Disclose source code of any modified version
  • Disclose source code if used to provide a network service
  • License any derivative works under AGPL-3.0

See the full license text in: LICENSE

⚠️ Disclaimer

USE AT YOUR OWN RISK. PruneMate is provided "as is" without warranty of any kind. The author(s) and contributors are not responsible for:

  • Data loss from pruned Docker resources
  • Service interruptions or downtime
  • System instability or performance issues
  • Any damages resulting from the use or misuse of this software

Always:

  • ✅ Understand which resources will be deleted
  • ✅ Keep backups of important data and configurations
  • ✅ Review logs after prune operations
  • ✅ Start with conservative settings

© 2026 – PruneMate Project


Keep your Docker host clean with PruneMate! 🐳🧹

Repository Stats

Stars
0
Forks
0
Watchers
0
Size
2.9 MB
Primary Language
HTML
LicenseAGPL-3.0
Issues0 Open

Gallery

Project screenshot 1
Project screenshot 2
Project screenshot 3
Project screenshot 4
+ 3 more images in README