You remove old Docker containers. You clean up unused images. You even run docker system prune.

But somehow, your disk is still running out of space.

Sounds familiar?

I recently ran into this on my Linux machine. I had been using Docker for several projects involving ClickHouse, PostgreSQL, Airbyte, Superset, and other services. Over time, those experiments accumulated.

The containers were no longer running. Some projects hadn't been touched in months. Yet Docker was still consuming a significant amount of disk space.

What made this interesting was that the problem wasn't primarily the containers or images. A substantial amount of storage was sitting in persistent Docker volumes.

In this post, I'll walk through how I investigated the issue, identified the largest consumers, cleaned up unused Docker resources, and recovered disk space without deleting the data belonging to my active containers.

Let's get into it.

1. Finding out where the disk space was going

The first step was to identify which directories were consuming the most space.

Instead of immediately running Docker cleanup commands, I started at /var.

sudo du -xhd1 /var | sort -h 

Here's what I found:

Directory Size
/var/lib 121 GB
/var/log 2.3 GB
/var/cache 450 MB
Other directories Relatively small
Total /var 124 GB

The result immediately pointed towards /var/lib.

I narrowed the investigation further:

sudo du -xhd1 /var/lib | sort -h 

And this was where things got interesting.

Directory Size
/var/lib/docker 79 GB
/var/lib/clickhouse 19 GB
/var/lib/rancher 12 GB
/var/lib/containerd 11 GB
/var/lib/flatpak 2 GB

Docker alone was consuming approximately 79 GB.

Since I had several Docker-based development environments on this machine, it made sense to investigate Docker first.

A quick explanation of the command:

  • du reports disk usage.
  • -x prevents crossing filesystem boundaries.
  • -h displays human-readable sizes.
  • -d1 limits the output to one directory level.
  • sort -h sorts the sizes correctly, including units such as MB and GB.

This is a useful starting point whenever you're trying to locate unexpected disk usage on a Linux machine.

2. Looking beyond containers and images

My next step was to understand what Docker was actually storing.

For this, Docker provides:

docker system df 

But I wanted more detail, so I used:

docker system df -v 

The -v flag provides a detailed breakdown of Docker's storage usage, including images, containers, local volumes, and build cache.

The output revealed something I wasn't expecting.

Images

I had accumulated images from several projects:

  • ClickHouse server
  • ClickHouse Keeper
  • PostgreSQL
  • Airflow-related projects
  • Qdrant-related and other development environments
  • Superset and supporting services
  • Various testing images

Some images were several gigabytes in size.

However, Docker images can share underlying layers, so adding up the displayed image sizes doesn't necessarily give you the actual disk space consumed.

Containers

I also checked the containers:

docker ps -a --size 

This lists both running and stopped containers, along with their writable-layer sizes.

There were several stopped containers left behind by previous projects.

However, their writable layers were relatively small.

This was an important distinction: the container's writable layer is not the same thing as the data stored in a mounted Docker volume.

If a database stores its data in a Docker volume, checking the container's writable-layer size won't tell you how much space that database is consuming.

Build cache

Docker also retains build cache to speed up future builds.

My initial investigation showed that build cache was consuming around 3.3 GB.

So there were multiple sources of storage usage:

  • Unused images
  • Stopped containers
  • Persistent volumes
  • Build cache

I now had a much clearer picture of what needed to be investigated.

3. The real culprit: Docker volumes

This was the most interesting part of the investigation.

When I looked at the local volume section in docker system df -v, I found several volumes consuming significant amounts of storage.

Some examples from the output:

Volume Size
clickhouse-blog_ch_data 22.77 GB
Anonymous volume 8.91 GB
Anonymous volume 8.94 GB
clickhouse_clickhouse_data 9.61 GB
Other project volumes Several GB combined

These were not container writable layers. They were persistent Docker volumes.

Why do Docker volumes remain after containers are removed?

Docker volumes are designed to persist independently of containers.

This is especially important for stateful applications such as:

  • ClickHouse
  • PostgreSQL
  • MySQL
  • Redis
  • MinIO

Imagine running PostgreSQL in a container and storing its database files in a named volume.

services: postgres: image: postgres:17 volumes: - postgres_data:/var/lib/postgresql/data volumes: postgres_data: 

If you remove the PostgreSQL container, the named volume doesn't automatically disappear.

That is intentional. Otherwise, removing a container could accidentally destroy your database.

The problem is that development environments tend to accumulate.

You spin up a ClickHouse cluster for testing, experiment with a PostgreSQL setup, build a small Airbyte pipeline, and later move on to something else.

The containers disappear, but their volumes can remain.

Over time, these volumes can consume a surprising amount of disk space.

4. Understanding named and anonymous volumes

Docker supports two types of volumes:

Named volumes

These have an explicitly assigned name.

For example:

docker volume create clickhouse_data 

Or through Docker Compose:

volumes: clickhouse_data: 

Docker Compose commonly prefixes volume names with the project name. That's why you might see names such as:

clickhouse_clickhouse_data qdocs-dev_postgres-data airbyte_clickhouse_data 

Anonymous volumes

These are created without an explicitly assigned name and typically receive a long identifier.

You might see something like:

cc18585a... 

Both types of volumes can persist after their containers are removed.

To list volumes:

docker volume ls 

To inspect volumes that aren't currently referenced by containers:

docker volume ls -f dangling=true 

This command was particularly useful during my investigation.

However, there's an important distinction:

A dangling volume is not necessarily an unnecessary volume.

It only tells you that no container currently references it.

A volume might belong to a database you intend to restart next week. It might contain test data you haven't backed up. It might be an old project that you still need.

Always inspect before deleting.

5. Cleaning up unused Docker resources

After identifying the old containers, images, networks, and anonymous volumes, I decided to clean up unused Docker resources.

I ran:

docker system prune -a --volumes 

This is a powerful cleanup command.

It removes:

  • Stopped containers
  • Networks not used by containers
  • Images not referenced by any container
  • Unused build cache
  • Unused anonymous volumes

Docker displayed a confirmation prompt, and I reviewed it before proceeding.

The cleanup completed and reported:

Total reclaimed space: 33.49GB 

That's a substantial amount of storage recovered.

But here's something important I learned during this process.

docker system prune --volumes does not mean delete every volume

The --volumes option on docker system prune targets unused anonymous volumes. Named volumes need separate attention.

For named volumes, Docker provides:

docker volume prune -a 

The -a option includes unused named volumes as well as anonymous ones.

Be careful with this command. A volume can be unused by every current container and still contain valuable database data.

If you want to delete only one specific volume, you can remove it explicitly:

docker volume rm <volume_name> 

That's what I eventually did with one of the old ClickHouse volumes.

6. Investigating a 9.61 GB ClickHouse volume

After the initial cleanup, Docker was still consuming around 34 GB.

When I inspected the remaining volumes, one immediately caught my attention:

clickhouse_clickhouse_data 

Docker reported its size as:

9.612GB 

I inspected it:

docker volume inspect clickhouse_clickhouse_data 

The output showed that it belonged to a Docker Compose project named clickhouse.

It had been created on:

2025-12-30 

This was an old development environment from the previous year.

The volume had no container references, so I wanted to understand what was actually occupying the space.

I inspected its contents:

sudo du -xhd2 \ /var/lib/docker/volumes/clickhouse_clickhouse_data/_data \ | sort -h 

The result was interesting:

160M .../store/b6b 214M .../store/648 347M .../store/54f 356M .../store/692 475M .../store/81a 763M .../store/19d 6.9G .../store/504 9.2G .../store 9.2G .../_data 

Most of the volume's data was under ClickHouse's store directory.

The store directory contains the underlying data for ClickHouse tables. The large store/504 directory alone occupied approximately 6.9 GB.

Since this was an old development environment and I no longer needed its data, I removed the volume explicitly:

docker volume rm clickhouse_clickhouse_data 

Docker confirmed that the volume was removed.

This was a much more targeted cleanup than deleting every unused volume on the machine.

7. Verifying the actual disk-space recovery

After cleanup, I wanted to verify the result at two levels:

  1. Docker's own storage accounting.
  2. The actual filesystem usage.

I ran:

df -h / 

Before removing the old ClickHouse volume, the filesystem showed:

Filesystem Size Used Avail Use% Mounted on /dev/nvme0n1p3 227G 151G 64G 71% / 

After removing it:

Filesystem Size Used Avail Use% Mounted on /dev/nvme0n1p3 227G 143G 72G 67% / 

The filesystem usage decreased by approximately 8 GB.

I also checked Docker's storage usage again:

docker system df -v 

And checked the Docker directory directly:

sudo du -xhd1 /var/lib/docker | sort -h 

The result:

4.0K /var/lib/docker/runtimes 4.0K /var/lib/docker/swarm 4.0K /var/lib/docker/tmp 8.0K /var/lib/docker/rootfs 12K /var/lib/docker/plugins 48K /var/lib/docker/containers 92K /var/lib/docker/image 128K /var/lib/docker/network 51M /var/lib/docker/buildkit 25G /var/lib/docker 25G /var/lib/docker/volumes 

Docker's directory usage had dropped from approximately 34 GB to 25 GB.

There was still a substantial amount of storage in volumes belonging to other projects, but I had already recovered a significant amount without disturbing the active ClickHouse container.

Why don't all these numbers match exactly?

You might notice that Docker reported the old volume as 9.612 GB, while du reported approximately 9.2 GB. The filesystem's used space also decreased by approximately 8 GB.

These measurements aren't interchangeable:

  • docker system df -v reports Docker's storage accounting.
  • du reports filesystem usage for the directories it scans.
  • df reports usage and available capacity at the filesystem level.

Filesystem allocation, metadata, shared storage, and differences in accounting can cause these values to differ.

The important thing is to use the appropriate measurement for the question you're trying to answer.

8. A few commands worth keeping around

Here are the commands I found most useful during this investigation.

Find the largest directories:

sudo du -xhd1 /var/lib | sort -h 

Get a detailed Docker storage breakdown:

docker system df -v 

List all containers, including stopped ones:

docker ps -a --size 

List Docker volumes:

docker volume ls 

List volumes not currently referenced by containers:

docker volume ls -f dangling=true 

Inspect a volume:

docker volume inspect <volume_name> 

Check the actual contents of a volume:

sudo du -xhd2 \ /var/lib/docker/volumes/<volume_name>/_data \ | sort -h 

Remove a specific unused volume:

docker volume rm <volume_name> 

Check filesystem usage:

df -h / 

These commands make it much easier to move from a vague "Docker is consuming too much disk space" problem to identifying exactly where the storage is going.

9. Lessons learned

A few things stood out from this investigation.

1. Removing containers doesn't necessarily remove their data.

Docker volumes are persistent by design. Old development environments can leave behind gigabytes of data even after their containers are gone.

2. Don't assume images are the biggest storage consumers.

My investigation initially looked like a Docker image cleanup problem. But persistent volumes turned out to be a major contributor.

3. docker system df -v should be one of your first commands.

It gives you a much better picture of Docker's storage usage than simply looking at docker ps.

4. Dangling doesn't mean disposable.

A volume without a container reference can still contain valuable database data. Inspect it before deleting it.

5. Be careful with prune commands.

docker system prune -a --volumes and docker volume prune -a have different scopes. Understand what each command removes before running it on a development machine or production host.

6. Always verify cleanup at the filesystem level.

Docker's reported reclaimed space is useful, but df -h and du help confirm what actually changed on the host.

Conclusion

What started as an investigation into unexpected disk usage turned into a useful reminder about how Docker handles persistent storage.

On my development machine, Docker had accumulated old containers, images, build cache, and volumes from several projects.

The initial cleanup reported 33.49 GB reclaimed. I then identified an old ClickHouse volume occupying approximately 9.2 GB on disk and removed it explicitly. Docker's directory usage eventually dropped from approximately 79 GB at the start of the investigation to 25 GB.

The biggest takeaway?

Don't just inspect your containers when Docker consumes too much disk space. Inspect your volumes too.

Your containers might be gone, but their data could still be sitting on your disk.