Back to articles
Technology Insight

Automating Docker Disk Space Optimization: Using Systemd Timers to Clear Build Cache and Dangling Images on Production VPS

June 4, 2026

Introduction: The Hidden Cost of Containerization

Docker has revolutionized the way software is developed, packaged, and deployed. By encapsulating applications into lightweight containers, engineering teams achieve unparalleled environmental consistency across staging and production. However, in production environments—particularly on virtual private servers (VPS) with constrained storage capacity—Docker possesses a notorious appetite for disk space. Over time, continuous integration and continuous deployment (CI/CD) pipelines generate substantial overhead in the form of dangling images and accumulated build cache.

When a VPS reaches 100% disk utilization, the consequences are immediate and severe: database transactions fail, logging daemons crash, and application containers halt unexpectedly. Manual intervention (running docker system prune) provides temporary relief, but it is a reactive approach prone to human error and neglect. To guarantee high availability and operational efficiency, engineers must implement a proactive, automated maintenance strategy. This guide details how to construct a robust, production-ready automation system using Systemd Timers to dynamically manage and optimize Docker disk capacity.

---

Understanding the Culprits: Dangling Images and Build Cache

Before automating the cleanup process, it is vital to understand exactly what data is consuming storage and why it can become safe to delete.

What are Dangling Images?

In Docker terminology, a dangling image is an image that is no longer associated with any tagged repository. These typically manifest as : when executing the docker images command. Dangling images frequently occur during sequential deployments. When a new version of an image is pulled or built with an existing tag (e.g., web-app:latest), Docker untags the older image rather than deleting it. The old layers remain on the host filesystem, consuming valuable gigabytes while serving no active purpose.

The Role of Docker BuildKit and Build Cache

Modern Docker environments utilize BuildKit, an enhanced backend engine that drastically accelerates image compilation through aggressive caching. While BuildKit greatly optimizes build times by reusing unmodified layers, its cache grows monotonically. On a CI/CD runner or a VPS where images are frequently recompiled locally, the build cache can easily expand to tens of gigabytes within a matter of days. Without regular purging, this cache transitions from a performance asset to an operational liability.

---

Why Systemd Timers Excel Over Traditional Cron Jobs

Historically, Linux administrators relied exclusively on the cron daemon for scheduling recurring maintenance tasks. While cron remains functional, modern Linux distributions offer a superior alternative: Systemd Timers. Utilizing Systemd Timers for Docker maintenance introduces several enterprise-grade advantages:

  • Granular Logging and Observability: Every execution of a Systemd service is natively captured by journalctl. This allows administrators to audit precisely when a cleanup occurred, what command was executed, and how much disk space was recovered.
  • Dependency Management: Systemd Timers trigger specific Systemd Services. This architecture ensures that the cleanup script will only execute if prerequisite conditions are met (for example, ensuring the Docker daemon itself is actively running).
  • Resource Control: Through Linux cgroups, Systemd allows you to restrict the CPU and memory consumption of the cleanup process, preventing maintenance cycles from starving production applications of resources.
  • Randomized Delay: Systemd allows the inclusion of a RandomizedDelaySec directive, preventing multiple servers from executing heavy I/O operations at the exact same second, which reduces concurrent load on shared hypervisors.
---

Step-by-Step Implementation Guide

Follow this structural blueprint to implement automated Docker pruning on your Ubuntu, Debian, or CentOS VPS.

Step 1: Creating the Bash Cleanup Script

First, we establish a standardized script that contains the precise Docker commands required for safe, non-destructive purging. We will place this script within the secure /usr/local/bin/ directory.

sudo nano /usr/local/bin/docker-cleanup.sh

Paste the following script content:

#!/bin/bash
# Official Docker Optimization Script for Production VPS
echo "=== Starting Docker Optimization Flow: $(date) ==="

# Prune dangling images without affecting active containers
echo "Optimizing unused and dangling images..."
docker image prune -f

# Prune BuildKit build cache older than 48 hours to preserve recent efficiency
echo "Optimizing BuildKit build cache..."
docker builder prune -f --filter "until=48h"

echo "=== Optimization Completed Successfully ==="

After saving the file, you must alter its permissions to make it executable by the system system daemon:

sudo chmod +x /usr/local/bin/docker-cleanup.sh

Step 2: Defining the Systemd Service Units

Next, we construct the Systemd service configuration. This file instructs Systemd on how to execute the script we created in the previous step.

sudo nano /etc/systemd/system/docker-cleanup.service

Input the following configuration settings:

[Unit]
Description=Automated Docker Storage Optimization Service
After=docker.service
Requires=docker.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/docker-cleanup.sh
StandardOutput=journal
StandardError=journal

Step 3: Constructing the Systemd Timer Configuration

Now, create the companion timer file that controls the schedule for the service execution. In this production example, we configure the routine to run every Sunday at 03:00 AM, minimizing potential disruptions during peak traffic hours.

sudo nano /etc/systemd/system/docker-cleanup.timer

Populate the file with the following directives:

[Unit]
Description=Weekly Trigger for Docker Storage Optimization Service

[Timer]
OnCalendar=Sun *-*-* 03:00:00
Persistent=true
RandomizedDelaySec=1800

[Install]
WantedBy=timers.target

Note: Setting Persistent=true ensures that if the VPS is turned off during the scheduled window, Systemd will immediately run the script upon the next boot sequence.

Step 4: Activation and Verification

To finalize the setup, reload the Systemd manager configuration, enable the timer to start on system boot, and initiate it immediately.

sudo systemctl daemon-reload
sudo systemctl enable docker-cleanup.timer
sudo systemctl start docker-cleanup.timer

To verify that your new scheduler is active and to view its next projected execution time, run:

systemctl list-timers --all | grep docker-cleanup
---

Auditing and Log Monitoring

A crucial part of maintaining server infrastructure is verifying execution logs. To audit the output of your automated cleanup script and monitor how much space is saved over time, utilize the system journal:

sudo journalctl -u docker-cleanup.service

This unified logging system will accurately display the stdout and stderr streams of your bash script, making troubleshooting direct and simple.

---

Conclusion: Safe Production Safeguards

By automating the removal of dangling images and old BuildKit caches via Systemd Timers, you actively safeguard your production VPS from critical out-of-space downtime. However, remember to exercise caution with aggressive pruning. Avoid using the global docker system prune -a --volumes flag in automated environments unless you are fully certain that stopped containers, unused networks, and persistent data volumes do not contain vital application states. The targeted approach outlined above balances efficiency with safety, ensuring a lean and highly performant infrastructure.

Automating Docker Disk Space Optimization: Using Systemd Timers to Clear Build Cache and Dangling Images on Production VPS | DPTCloud