Skip to content
Articles

Articles · 03

Diagnosing and resolving disk saturation caused by dangling Docker images on a production VPS.

Reading time · 2 min

Freeing disk space by cleaning up Docker images

Context

On a production VPS hosting several Docker services, the main disk reached 99% usage, causing errors and slowdowns across the services.

This article retraces the full diagnosis and resolution.

What is a dangling Docker image?

Before the diagnosis, it is important to distinguish the terms:

  • A dangling image is no longer tagged, so its tag appears as <none>. It typically appears when an image is rebuilt with the same tag: the old version loses its tag and becomes dangling.
  • An unused image is not referenced by any container, whether running or stopped. A dangling image is always unused, but an unused image is not necessarily dangling.

Step-by-step diagnosis

1. Check overall disk usage

df -h

Result: the main 155 GB disk was 99% full.

2. Identify the largest directory

sudo du -h --max-depth=1 / | sort -hr | head -n 20

Result: /var used 137 GB, far more than the other directories.

3. Investigate /var

sudo du -h --max-depth=1 /var | sort -hr

Result: /var/lib used 136 GB.

4. Inspect /var/lib

sudo du -h --max-depth=1 /var/lib | sort -hr

Result: /var/lib/docker used 135 GB. The cause was identified.

5. Check active containers

docker ps -a

Several production containers were active, so deleting everything was not an option.

6. List Docker images

# List all images
docker images

# List images by name/tag
docker images <image_name>

# List dangling images only
docker images --filter "dangling=true"

# List dangling images for a specific service
docker images nginx --filter "dangling=true"

This revealed many old, obsolete, dangling images with the <none> tag.

Resolution

Clean dangling images service by service

# Example for a cache service
docker rmi $(docker images redis -f "dangling=true" -q)

# Example for a web service (nginx)
docker rmi $(docker images nginx -f "dangling=true" -q)

# Example for an application service (PHP)
docker rmi $(docker images php -f "dangling=true" -q)

This removes only dangling images for the selected service. Images used by existing containers, including stopped ones, are not removed. Repeat it for each service, distinguishing variants where necessary, for example php and php_prod.

Alternative: global cleanup

Docker also provides a global dangling-image cleanup command:

docker image prune

To remove images unused by any container as well:

docker image prune -a

Caution: prune -a removes every unused image, not only dangling ones. Use it carefully on a production server.

Outcome

  • Dozens of GB freed on the root partition
  • Disk space returned to a stable, usable state
  • Better visibility into the Docker images present on the server

Practices to prevent recurrence

  • Configure periodic cleanup with a cron job: docker image prune -f
  • Use explicit tags instead of latest to identify versions more clearly
  • Include cleanup in CI/CD pipelines after deployment
  • Monitor disk space with alerts, for example through the VPS provider's monitoring