Docker accumulates disk usage from four main sources: unused images (every pulled tag stays on disk until explicitly removed), stopped containers (they retain their writable layer), dangling volumes (volumes no longer attached to any container), and build cache (intermediate layers from image builds). On a busy production server these can accumulate to tens or hundreds of gigabytes. 'docker system df' shows exactly what each category is using and how much is reclaimable, and the prune commands remove unused resources safely without touching running containers.
What Consumes Docker Disk Space
Docker’s disk usage grows from several independent sources, each requiring a different cleanup approach:
1. Container images — every ‘docker pull’, ‘docker build’, and image tag creates or retains image layers on disk. Images from previous deployments, old base image versions, and pulled-but-unused images accumulate. ‘docker images’ shows all stored images; ‘docker image prune -a’ removes all images not referenced by any container (running or stopped).
2. Container writable layers — every stopped container retains its writable layer (filesystem changes made during the container’s lifetime). On a system that creates containers frequently (CI runners, one-shot tasks), stopped containers can accumulate rapidly. ‘docker container prune’ removes all stopped containers.
3. Build cache — every layer created during ‘docker build’ is cached to speed up subsequent builds. On a build server, the cache can reach many gigabytes. ‘docker builder prune’ removes all build cache; ‘–filter until=24h’ removes only cache older than 24 hours.
4. Volumes — named volumes not attached to any running or stopped container are ‘dangling’. Anonymous volumes created with ‘docker run -v /data’ become dangling when their container is removed without ‘–volumes’. ‘docker volume prune’ removes dangling volumes — use caution as this is irreversible.
5. Container logs — Docker’s json-file log driver writes container stdout/stderr to /var/lib/docker/containers/
Diagnosing and Reclaiming Docker Disk Space
# Step 1: See total disk usage by category
docker system df
# Output:
# TYPE TOTAL ACTIVE SIZE RECLAIMABLE
# Images 42 8 18.2GB 14.1GB (77%)
# Containers 5 3 1.2GB 890MB (74%)
# Local Volumes 12 4 8.4GB 6.1GB (72%)
# Build Cache 0 0 0B 0B
# Step 2: Verbose view (shows individual items)
docker system df -v
# Step 3: Safe prune — removes only clearly unused resources
# (does NOT remove volumes by default)
docker system prune
# Step 4: Nuclear option — remove everything not attached to running containers
# WARNING: removes all unused images, stopped containers, dangling volumes, build cache
docker system prune -a --volumes
# Step 5: Category-specific pruning (safer, more controlled)
docker container prune # remove stopped containers
docker image prune # remove dangling images only
docker image prune -a # remove all unused images
docker volume prune # remove dangling volumes
docker builder prune # remove build cache
docker builder prune --filter until=24h # remove cache older than 24h‘docker system prune’ without ‘-a’ removes only dangling images (untagged layers from builds), stopped containers, unused networks, and build cache. It does NOT remove images that have a tag, even if no container is using them. Adding ‘-a’ extends cleanup to all images not referenced by any container — this is the most effective space reclamation but requires re-pulling images on next use. Adding ‘–volumes’ extends to dangling volumes — use only when you are certain the volumes contain no needed data.
Prune Commands and Their Scope
| Flag / Parameter | Description | Security Note |
|---|---|---|
docker system prune | Removes: dangling images (untagged), stopped containers, unused networks, build cache. Does NOT remove: tagged images not used by any container, volumes. Safe to run in production at any time — only removes resources that cannot possibly be needed by a running container. | Run 'docker system prune' as part of a scheduled maintenance cron on CI and build servers. On application servers, run it after deployments when old image versions are no longer needed. |
docker image prune -a | Removes all images not referenced by any container (running or stopped). After pruning, starting a container using a now-removed image will trigger a pull. Use '–filter until=72h' to keep recently pulled images and only remove older unused ones. | After 'docker image prune -a', if a container fails and needs to restart, Docker will pull the image from the registry. Ensure the registry is accessible before running aggressive image pruning in production environments with restart policies. |
docker volume prune | Removes all volumes not attached to any container (running or stopped). This permanently deletes the volume data with no recovery option. Only run after explicitly confirming the volumes do not contain needed data. Use '–filter label!=keep' to protect specific volumes. | Never run 'docker volume prune' in production without first auditing dangling volumes with 'docker volume ls -f dangling=true' and verifying each volume's content. Database volumes, certificate stores, and configuration volumes may be detached from containers due to temporary maintenance without being safe to delete. |
docker builder prune | Removes Docker build cache. This does not affect running containers or images — only the intermediate layers cached from previous builds. Build operations after pruning will take longer on first run as layers are rebuilt. Use '–filter until=24h' to prune only cache older than a day. | Build cache can contain sensitive data from build arguments (–build-arg). While the build cache is not accessible to running containers, pruning old build cache on build servers is good hygiene to limit exposure of build-time secrets. |
Managing Docker Disk Space in Production
Configuring Container Log Rotation
Docker’s default json-file log driver has no size limit. A single verbose container can write gigabytes of logs. Configure log rotation globally in the Docker daemon config or per-container at run time.
# Global log rotation: edit /etc/docker/daemon.json
# (restart Docker daemon after changes)
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "3"
}
}
# Apply the new daemon config
sudo systemctl restart docker
# Per-container log rotation at run time:
docker run -d \
--log-opt max-size=50m \
--log-opt max-file=3 \
--name myapp \
myapp:latest
# In Docker Compose:
services:
app:
image: myapp:latest
logging:
driver: json-file
options:
max-size: "50m"
max-file: "3"
# Check current log file size for a container
du -sh /var/lib/docker/containers/<container-id>/*-json.log
# Find large log files across all containers
du -sh /var/lib/docker/containers/*/*-json.log | sort -h | tail -10Scheduled Cleanup with Cron
Run ‘docker system prune’ automatically on a schedule to prevent disk accumulation. The command is safe to run while containers are running — it only removes resources that are definitively unused.
# Add to crontab (runs at 3 AM daily)
crontab -e
# Add:
# 0 3 * * * /usr/bin/docker system prune -f >> /var/log/docker-prune.log 2>&1
# For more aggressive cleanup (weekly, removes all unused images):
# 0 2 * * 0 /usr/bin/docker image prune -a -f --filter until=168h >> /var/log/docker-prune.log 2>&1
# Prune with age filter (keep last 3 days of unused images):
docker image prune -a --filter until=72h -f
# Prune stopped containers older than 1 day:
docker container prune --filter until=24h -f
# Check disk usage before and after:
df -h /var/lib/docker
docker system dfProtecting Volumes from Prune
Use labels to mark volumes that should not be pruned. The ‘docker volume prune’ command respects label filters, allowing you to keep important data volumes while removing truly orphaned ones.
# Label important volumes to protect them from pruning
docker volume create \
--label keep=true \
--label description=postgres-data \
postgres-data
# Or in Docker Compose (volumes section):
volumes:
postgres-data:
labels:
keep: "true"
# Prune only volumes WITHOUT the keep label:
docker volume prune --filter label!=keep -f
# List all volumes with their labels:
docker volume ls --format 'table {{.Name}}\t{{.Labels}}'
# List dangling volumes before pruning:
docker volume ls -f dangling=trueDisk Full Impact on Production Systems
A full disk on a Docker host causes immediate production failures: containers cannot write logs, cannot create new files, and may crash if they write to their writable layer. The Docker daemon may fail to start new containers if it cannot allocate the overlay filesystem. Recovery from a completely full disk requires knowing which cleanup operation to run without making the problem worse. Additional considerations apply when running Docker in production: monitoring container health metrics, setting up centralised log aggregation, implementing image signing and verification workflows, and auditing container runtime configuration against CIS Docker benchmarks. Each of these layers adds defence in depth beyond the controls described above. Review the full security posture regularly as Docker releases update default security settings and new CVEs are discovered in container runtime components.
- When /var/lib/docker fills up, all containers on the host are affected — not just the containers with large logs. Containers that cannot write to their filesystem will crash or behave unpredictably. Set up disk usage monitoring alerts at 80% threshold.
- Never run 'docker volume prune' in an emergency to free space without first inspecting the volumes. Deleting a database volume to free disk space destroys data permanently. Instead, start with 'docker image prune -a' and 'docker builder prune' which have no data loss risk.
- Log truncation using '> /var/lib/docker/containers/<id>/*-json.log' truncates the current log file but does not free inode space while Docker holds the file descriptor. Restart the container or use 'kill -USR1' to rotate the log for immediate disk recovery.
- Docker build cache can accumulate particularly fast on CI/CD servers. If builds run frequently, 'docker builder prune' may need to run after every build pipeline or on a frequent schedule. The BuildKit cache can be configured with '–cache-to' flags to limit total cache size.
Practical Examples
Emergency Disk Recovery Procedure
# Emergency procedure when disk is full (in order of safety)
# 1. Check how much space each Docker category is using
docker system df
df -h /var/lib/docker
# 2. Remove build cache first (safe, no data loss)
docker builder prune -f
# Check recovered space:
df -h /var/lib/docker
# 3. Remove stopped containers (safe if you don't need debug info)
docker container prune -f
# 4. Remove unused images (safe for running containers)
# Keep images used in last 48 hours:
docker image prune -a --filter until=48h -f
# 5. Check for large log files
du -sh /var/lib/docker/containers/*/*-json.log 2>/dev/null | \
sort -h | tail -5
# 6. Truncate a specific large log file (safe while container runs)
# WARNING: this destroys historical logs
sudo sh -c '> /var/lib/docker/containers/<container-id>/<container-id>-json.log'
# 7. ONLY IF SAFE: remove dangling volumes
# List first:
docker volume ls -f dangling=true
# Then only remove clearly safe ones:
docker volume rm <volume-name>Run these steps in order from least to most risky. Build cache and dangling images have zero data loss risk. Stopped container writable layers are usually safe to remove. Log truncation loses historical log data. Volume pruning has permanent data loss risk — list and audit before running.
Quick Reference: Key Commands and Options
# Inspect current configuration
docker inspect <container> --format='{{json .HostConfig}}' | python3 -m json.tool
# View container resource usage
docker stats <container> --no-stream
# Check container logs
docker logs <container> --tail 50 --timestamps
# Verify running processes inside container
docker exec <container> ps aux
# Check container environment variables
docker exec <container> env | sortThese diagnostic commands work across all container scenarios: ‘docker inspect’ shows the full HostConfig including all runtime parameters, ‘docker stats’ shows real-time resource consumption, and ‘docker logs’ shows output from the main process. Use these as the starting point for any container investigation before diving into more specific tooling.
Disk Space Troubleshooting
Problem: docker system prune ran but disk usage barely changed
Solution: The space is likely in container logs or in named volumes (both excluded from basic prune). Check log file sizes: ‘du -sh /var/lib/docker/containers/*/*-json.log | sort -h | tail -10’. Check volume sizes: ‘docker system df -v’. If logs are large, configure log rotation in /etc/docker/daemon.json and restart the Docker daemon.
Problem: Docker reports reclaimable space but df shows disk still full after prune
Solution: Filesystem space is not immediately returned to the OS if processes hold open file descriptors to deleted files. This is common with truncated log files. Restart the container whose log was truncated to close and reopen the log file descriptor. Alternatively, restart the Docker daemon: ‘systemctl restart docker’.
Problem: Overlay2 directory in /var/lib/docker is huge but prune does not reclaim it
Solution: The overlay2 directory contains live container filesystems and image layers. Running containers and all tagged images contribute to this space. ‘docker image prune -a’ removes unused image layers. If space is still large, running containers with large writable layers are the cause — investigate which containers write large amounts of data with ‘docker diff
Prune Regularly, Rotate Logs, Monitor Disk Usage
Docker disk accumulation is predictable and preventable. The four main contributors — unused images, stopped containers, build cache, and unrotated logs — each have straightforward management strategies. Setting up log rotation in daemon.json and running ‘docker system prune’ on a cron schedule prevents disk full emergencies. When disk does fill up, address build cache and images first (no data loss risk) before considering volumes.
- Configure log rotation in /etc/docker/daemon.json with max-size and max-file before deploying any container. The default json-file driver with no limits will fill disk on any moderately verbose application.
- ‘docker system df’ shows disk usage broken down by category with reclaimable amounts — always run this first to understand where the space is before choosing a prune command.
- Never run ‘docker volume prune’ in an emergency without first listing dangling volumes and verifying they contain no needed data. Database volumes can appear dangling during maintenance and be destroyed irreversibly.
Related Docker Topics
understanding Docker volume types and lifecycle • log driver configuration and rotation strategies • disk management as part of production Docker setup • reducing image size to minimise disk footprint
Docker Disk Management on Production Servers?
INTRAM sets up Docker disk management, log rotation, and automated cleanup for Linux production servers.
Get Docker Disk Management Help