Introduction
Docker simplifies application deployment by packaging applications and their dependencies into portable containers. However, without proper storage management, Docker environments can gradually consume significant disk space, causing application builds to fail, deployments to stall and production services to become unstable.
One particularly common issue occurs when organisations continuously rebuild and redeploy Docker images without removing unused versions.
A Linux server with a 100 GB or larger root filesystem can unexpectedly reach 90% disk utilisation even when its running containers appear relatively small.
In this article, we explore a real-world Docker storage troubleshooting scenario, explain how to identify the actual cause of excessive disk consumption, and demonstrate safe cleanup and prevention strategies.
1. Understanding Docker Disk Space Consumption
Docker uses several storage components that consume disk space independently.
| Docker component | Description | Common storage issue |
|---|---|---|
| Images | Read-only layers containing applications and dependencies | Historical versions accumulate |
| Containers | Running or stopped instances with writable layers | Temporary files accumulate |
| Build cache | Reusable intermediate build data | Cache grows after repeated builds |
| Volumes | Persistent application and database data | Databases and backups expand |
| Container logs | Application output collected by Docker | Logs grow without rotation |
| OverlayFS | Docker's layered filesystem storage | Old image layers occupy the root disk |
On Linux installations, Docker typically stores its data in:
/var/lib/dockerUnless Docker is configured to use a separate storage location, its images, writable layers, volumes and build data commonly compete with the operating system for disk space.
This makes disk-space monitoring particularly important on production Linux servers.
2. Real-World Scenario: Docker Builds Failing Due to Disk Space
Consider a production Linux server running several containerised services, including:
Web applications
Backend APIs
Background processing workers
PostgreSQL databases
Redis
Reverse proxy services
The environment uses Docker Compose for application deployment, with frequent image rebuilds as new application releases are published.
Eventually, the server reports insufficient disk space during Docker builds.
An initial filesystem check reveals:
df -hExample output:
Filesystem Size Used Avail Use%
/dev/root 124G 114G 11G 92%The root filesystem is already 92% utilised.
Although 11 GB remains available, that may not be sufficient for a Docker build involving large base images, dependency installation and multiple intermediate build stages.
Why Docker Overlay Mounts Can Be Misleading
Running df -h may display several entries similar to:
overlay 124G 114G 11G 92% /var/lib/docker/overlay2/.../merged
overlay 124G 114G 11G 92% /var/lib/docker/overlay2/.../mergedThese entries do not mean each container consumes 114 GB.
Docker's OverlayFS storage driver presents layered container filesystems using the underlying host filesystem.
Consequently, several overlay mounts may report identical filesystem capacity and utilisation.
The actual disk consumption must be investigated using Docker-specific commands and filesystem analysis tools.
3. Step-by-Step Docker Disk Space Investigation
Step 1: Check Host Disk Utilisation
Begin by examining available filesystem capacity.
df -hTo inspect filesystem usage on the root partition:
df -h /Next, identify which directories consume the most storage.
sudo du -xhd1 / 2>/dev/null | sort -hFor the Docker data directory:
sudo du -xhd1 /var/lib/docker 2>/dev/null | sort -hThese commands help determine whether the problem is primarily associated with Docker, operating system logs, application files or another directory.
Step 2: Analyse Docker Storage
Docker provides a built-in command to report storage consumption.
sudo docker system dfFor detailed information:
sudo docker system df -vThe verbose output includes image sizes, shared layers, unique layer sizes, container associations and volume sizes.
Important values include:
Shared size: Layers shared by multiple images.
Unique size: Image layers not shared with other images.
Containers: Number of containers referencing an image.
Reclaimable: Space Docker estimates can be recovered from unused resources.
In the troubleshooting scenario, the investigation uncovered numerous historical images from repeated deployments.
Some application images were larger than 2 GB, and new versions had been generated frequently.
Many older images appeared as:
REPOSITORY TAG SIZE CONTAINERS
<none> <none> 2.7GB 0
<none> <none> 1.8GB 0
<none> <none> 2.7GB 0
<none> <none> 1.8GB 0These entries indicated that Docker had retained old, untagged images that were no longer referenced by containers.
Over time, the accumulation of historical image layers became a major storage concern.
Step 3: Check Docker Build Cache
Docker builds can retain intermediate data to accelerate future builds.
To inspect the build cache:
sudo docker buildx duThe output helps establish whether BuildKit cache is responsible for storage growth.
In this scenario, the reported build cache was effectively zero.
That was an important finding.
Although the disk-space problem occurred during builds, the build cache itself was not the cause. The investigation instead pointed towards accumulated Docker images.
Lesson: A problem that becomes visible during a Docker build is not necessarily caused by Docker build cache.
Step 4: Check Container Writable Layers
Inspect all containers and their writable-layer usage.
sudo docker ps -a --sizeExample:
NAME SIZE
web-service 150MB
api-service 2MB
worker-service 300kB
database 0BThe SIZE field shows the approximate writable-layer size, while virtual size also includes image data.
In the investigated environment, most container writable layers were relatively small.
This reduced the likelihood that temporary files stored inside running containers were the primary cause.
Step 5: Check Docker Container Logs
Docker containers commonly use the json-file logging driver.
Without rotation, log files can grow continuously.
Inspect the largest JSON log files:
sudo find /var/lib/docker/containers \
-type f -name "*-json.log" \
-printf '%s %p\n' \
| sort -nr | head -20 \
| numfmt --field=1 --to=iecIn the scenario, the largest individual logs were a few hundred megabytes.
Although log management was still necessary, the observed log files did not explain the majority of disk consumption.
Avoid manually deleting Docker-managed log files while containers are running.
Instead, configure proper log rotation.
Step 6: Check Docker Volumes
Docker volumes store persistent data such as database files, uploaded documents and application state.
List volumes:
sudo docker volume lsInspect their sizes using:
sudo docker system df -vFor standard local volumes:
sudo du -sh /var/lib/docker/volumes/* \
2>/dev/null | sort -hThe investigated environment contained PostgreSQL and Redis volumes, but their reported sizes were comparatively small.
Production warning: Never delete Docker volumes without confirming their contents, dependencies and backup status. Removing a database volume can permanently destroy application data.
4. Root Cause: Accumulation of Historical Docker Images
The investigation identified a repeated pattern.
Each application deployment produced new Docker images, while older image versions remained on the server.
For example:
Deployment 1: API 1.8GB + Worker 2.7GB
Deployment 2: API 1.8GB + Worker 2.7GB
Deployment 3: API 1.8GB + Worker 2.7GB
Deployment 4: API 1.8GB + Worker 2.7GBAlthough the images could share base layers, each application release could introduce significant new image data.
This happens frequently when organisations:
Rebuild images directly on production servers.
Repeatedly use the
latestimage tag.Deploy multiple services several times a day.
Retain old images for rollback without a retention policy.
Do not include controlled image cleanup in the deployment process.
Why the latest Tag Contributes to Confusion
Consider:
docker build -t company-api:latest .After rebuilding and tagging the replacement image, the old image may lose its tag and appear as:
<none>:<none>If it is not referenced by a container, it may become eligible for cleanup.
However, untagged images are not always unused.
A running container may still reference an older image ID after a newer image receives the latest tag.
Therefore, image cleanup must account for container references rather than assuming every untagged image can be deleted indiscriminately.
5. How to Safely Clean Up Docker Disk Space
Before cleanup, inspect your images and containers.
sudo docker image ls -a
sudo docker ps -a
sudo docker system dfOption 1: Remove Unused Dangling Images
The recommended first action for this scenario is:
sudo docker image pruneThis removes dangling images that are not associated with containers.
It does not remove Docker volumes and does not stop running containers.
After cleanup, verify filesystem usage:
df -h /Then confirm running container status:
sudo docker psOption 2: Remove Dangling Images Older Than 24 Hours
For controlled cleanup of older dangling images:
sudo docker image prune -f \
--filter "until=24h"This helps prevent recently generated image layers from being removed immediately.
Note that age-based cleanup is not a complete rollback-retention strategy.
Option 3: Remove Unused Build Cache
Where build cache is consuming substantial disk space:
sudo docker builder pruneFor Buildx-managed cache:
sudo docker buildx pruneRemoving the cache may increase the time required for subsequent builds.
In the investigated case, build cache was not the major contributor, so cache cleanup alone would not have solved the problem.
Commands to Avoid Without Proper Assessment
Be cautious with broad cleanup commands:
sudo docker system prune -a --volumesThe -a option expands image cleanup to all unused images, including tagged images. Volume-related cleanup options can also remove persistent storage depending on the command and Docker version.
Do not manually remove Docker's internal directories:
sudo rm -rf /var/lib/docker/overlay2This can damage Docker's storage metadata and disrupt running workloads.
6. Configure Docker Log Rotation
Even when log files are not the primary cause of a disk-space issue, production environments should limit their growth.
Docker supports log rotation using daemon-level logging configuration.
Edit:
sudo nano /etc/docker/daemon.jsonFor the json-file logging driver, configure:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "20m",
"max-file": "5"
}
}This configuration limits each newly created container's JSON log files to approximately five files of 20 MB each.
Existing daemon settings must be preserved when making changes.
New logging defaults generally apply to newly created containers, so existing containers need to be recreated to adopt the configuration.
Schedule Docker daemon changes during an appropriate maintenance window.
For new installations, Docker's local logging driver is another option that supports rotation by default.
7. Reduce Docker Image Sizes
Large application images significantly increase the storage required for deployment and rollback.
Consider a background worker image of approximately 2.7 GB.
If deployments frequently produce substantial new layers, historical versions can consume significant storage.
Use Multi-Stage Docker Builds
A multi-stage Dockerfile separates build dependencies from the final application runtime.
Example for a Node.js application:
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --from=builder /app/dist ./dist
USER node
CMD ["node", "dist/index.js"]This illustrative Dockerfile assumes the application's compiled output is stored in dist and that runtime dependencies are declared correctly.
Application-specific files and native dependencies may require further adjustments.
Use a .dockerignore File
A .dockerignore file prevents unnecessary local files from entering the build context.
Example:
.git
node_modules
coverage
*.log
.env
.env.*
backups/
tmp/
dist/Ensure required build artefacts are not excluded accidentally.
Reducing build context size can improve build efficiency and avoid copying unnecessary data into intermediate layers.
8. Establish a Docker Image Retention Policy
Production environments should have a defined image-retention process.
A practical approach includes:
| Area | Recommended practice |
|---|---|
| Production images | Use immutable version tags or image digests |
| Rollback | Preserve approved previous releases |
| Cleanup | Remove unreferenced images under a defined retention policy |
| Logs | Configure rotation and centralised collection |
| Build cache | Review periodically and limit excessive accumulation |
| Volumes | Protect persistent data with backups |
| Monitoring | Alert before the filesystem becomes critically full |
For example, an organisation might retain the current release and two previous verified releases in its container registry.
On production hosts, preserve any locally required rollback images while removing other unreferenced image data.
A controlled deployment pipeline should verify the new application's health before performing cleanup.
9. Monitor Docker Disk Usage Before It Becomes Critical
A disk-space issue should ideally be detected before it interrupts deployment.
Useful Linux monitoring commands include:
df -h /sudo docker system dfsudo du -xhd1 /var/lib/docker \
2>/dev/null | sort -hOrganisations can also integrate filesystem metrics into monitoring platforms such as Azure Monitor, Prometheus or Grafana.
An example operational threshold strategy is:
| Disk utilisation | Operational response |
|---|---|
| Below 70% | Normal monitoring |
| 70–80% | Review storage growth |
| 80–90% | Investigate and plan cleanup |
| Above 90% | Urgent capacity assessment |
| Above 95% | Critical alert and immediate remediation |
These are illustrative thresholds. Appropriate limits depend on disk size, application growth rate and workload behaviour.
10. Long-Term Recommendation: Separate Builds From Production
One of the most effective ways to reduce Docker storage pressure on production servers is to build images outside the production environment.
A modern deployment process can follow this workflow:
A developer commits code to a Git repository.
A CI/CD pipeline builds the Docker image.
The pipeline scans and tests the image.
The image is published to a private container registry.
The production server pulls the approved image.
Health checks confirm a successful deployment.
A defined retention policy removes unnecessary old images.
Azure Container Registry, GitHub Actions and other CI/CD platforms can support this approach.
Separating image builds from production reduces build-time CPU, memory and temporary disk demands on application hosts.
Production disk usage still requires management, particularly when multiple image versions are retained locally.
11. Key Lessons From This Troubleshooting Scenario
This investigation highlights several important operational lessons.
First, do not assume build cache is responsible simply because a failure occurs during a Docker build. Docker's storage reports may reveal that build cache is negligible.
Second, distinguish container storage from historical image storage. Running containers may have very small writable layers while unused historical images consume substantial disk capacity.
Third, avoid broad cleanup commands on production servers. Databases, persistent volumes and rollback requirements must be considered before removing Docker resources.
Fourth, implement image lifecycle management. Frequent deployments without retention policies can progressively exhaust host storage.
Finally, effective Docker operations require more than simply running containers. Storage monitoring, image optimisation, log management and deployment controls are essential for long-term stability.
Conclusion
Docker disk exhaustion is a common but preventable infrastructure issue.
In this troubleshooting scenario, a Linux server reached approximately 92% disk utilisation. A detailed investigation showed that repeated deployments had left numerous historical Docker images on the host.
The findings pointed to accumulated image data rather than excessive build cache or database-volume growth as the principal Docker-related storage concern.
By using Docker's diagnostic commands, identifying unused images, implementing controlled cleanup and improving deployment practices, organisations can reduce storage risks and improve application reliability.
More importantly, proactive monitoring and automated image lifecycle management can help prevent the same issue from recurring.
Need Help With Docker, DevOps or Cloud Infrastructure?
Onsys Technologies provides professional IT infrastructure, database administration, cloud engineering and managed IT services to organisations looking to improve the reliability, performance and security of their technology platforms.
Our technical services include:
Docker and container environment troubleshooting
Azure cloud infrastructure and application deployment
CI/CD pipeline implementation and optimisation
Linux server administration
SQL Server, PostgreSQL and Oracle database administration
Infrastructure monitoring, performance optimisation and managed IT support
Whether you are experiencing storage issues, deployment failures or infrastructure performance problems, our team can help identify the underlying cause and implement sustainable improvements.
Contact Onsys Technologies: www.onsys.com.au
Onsys Technologies — Cloud, Database and IT Infrastructure Expertise.

