Dash0 Raises $110M Series B at $1B Valuation

  • 10 min read

Where Are Docker Images Stored on Disk?

When /var/lib/docker fills up your boot disk or Docker Desktop starts complaining about space, you need to know where images actually live — and why you can't just rm your way out of it. The short answer is that Docker images are stored in different places depending on your OS: a real directory on Linux, and a virtual disk file on macOS and Windows. The long answer involves understanding how Docker's layered filesystem works, because that determines what you can safely delete and what you should leave alone.

Storage locations by operating system

Linux

On Linux, Docker runs natively and stores everything (images, containers, volumes, build cache) under /var/lib/docker. The exact subdirectory depends on the storage driver in use, but on any modern Linux system you'll be using overlay2, which means image layer data lives at:

1
/var/lib/docker/overlay2/

To confirm where your Docker daemon is writing, run:

bash
1
docker info | grep "Docker Root Dir"
1
Docker Root Dir: /var/lib/docker

If someone moved it, or you're running rootless Docker, this command will show you the real path rather than forcing you to guess.

One thing that trips people up: the directories inside /var/lib/docker/overlay2/ are named after content-addressed hashes, not image names. You won't find an nginx/ folder in there. The mapping between those hashes and actual image names lives in the metadata that docker image ls reads. Don't browse this directory by hand. Use docker system df to see what's taking space.

macOS

Docker doesn't run natively on macOS. Docker Desktop spins up a lightweight Linux VM and stores all image data inside a single sparse disk image on your Mac filesystem:

1
~/Library/Containers/com.docker.docker/Data/vms/0/data/Docker.raw

This file is sparse, meaning it grows as you pull images but doesn't automatically shrink when you delete them. Running ls -lsh Docker.raw will show you the actual bytes consumed versus the maximum allocated size, and they're often very different numbers.

You can see and change the disk image location and its maximum size in Docker Desktop under Settings → Resources → Advanced. Don't move the file manually in Finder; use the settings UI, or Docker Desktop will lose track of it.

Windows

On Windows with Docker Desktop using the WSL 2 backend (the default since Docker Desktop 4.x), image data lives inside a virtual hard disk. The path depends on your Docker Desktop version:

12345
# Docker Desktop < 4.15
C:\Users\<your-user>\AppData\Local\Docker\wsl\data\ext4.vhdx
# Docker Desktop 4.15+
C:\Users\<your-user>\AppData\Local\Docker\wsl\disk\docker_data.vhdx

Inside that .vhdx, the Docker root directory is still /var/lib/docker: it's a Linux filesystem, just wrapped in a virtual disk. Like the macOS .raw file, this .vhdx grows as you pull images and doesn't compact automatically. Windows requires a manual step to reclaim space after pruning images — see the pitfalls section below.

How overlay2 stores image layers

Understanding the directory structure helps you diagnose space issues rather than just deleting things until things break.

When you pull an image, Docker downloads each layer separately and stores it as a directory under /var/lib/docker/overlay2/. After pulling a five-layer image, you'll typically see at least five hash-named directories plus an l/ directory — sometimes more, depending on how layers are cached. The l/ directory contains short symlinks to each layer's diff/ subdirectory. The symlinks exist to avoid hitting argument length limits when mounting the overlay filesystem.

Each layer directory contains a diff/ subdirectory with the files that layer adds or modifies, and a link file containing its short identifier. The hash-named directories are deliberately opaque, and that's by design: they're not meant to be navigated by hand. Here's what they look like anyway:

bash
1
sudo ls /var/lib/docker/overlay2/
123456
223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7
3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b
4e9fa83caff3e8f4cc83693fa407a4a9fac9573deaf481506c102d484dd1e6a1
e8876a226237217ec61c4baf238a32992291d059fdac95ed6303bdff3f59cff5
eca1e4e1694283e001f200a667bb3cb40853cf2d1b12c29feda7422fed78afed
l

The efficiency gain here is real: if ten images share the same base ubuntu:22.04 layer, that layer is stored on disk exactly once. The docker pull output (where it says Already exists for some layers) is this deduplication in action.

When a container runs, overlay2 adds a writable upperdir on top of the read-only image layers. Any changes the container makes go into that upper layer. The image itself is never modified, which is why you can run a hundred containers from the same image simultaneously without them interfering with each other's writes.

How to check what's taking space

Docker has a built-in command for this:

bash
1
docker system df
12345
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 23 5 8.342GB 6.891GB (82%)
Containers 3 3 1.2MB 0B (0%)
Local Volumes 12 3 4.521GB 2.1GB (46%)
Build Cache 47 0 2.341GB 2.341GB

The RECLAIMABLE column is the useful part: it shows how much you can recover without losing anything you're currently using. docker system prune will clear stopped containers, dangling images, unused networks, and build cache. Add -a to also remove images that aren't referenced by any running container.

How to change the storage location

If your root disk is full and you want to move Docker's data directory to a larger volume, edit /etc/docker/daemon.json (create it if it doesn't exist):

json
123
{
"data-root": "/mnt/docker-data"
}

Then stop Docker, copy the existing data across, and restart:

bash
1234
sudo systemctl stop docker
sudo rsync -aP /var/lib/docker/ /mnt/docker-data/
sudo systemctl start docker
docker info | grep "Docker Root Dir"
1
Docker Root Dir: /mnt/docker-data

Verify the output before deleting the old directory. Keep /var/lib/docker around for a day or two until you're confident everything is working from the new location.

On Docker Desktop (macOS/Windows), use the Settings UI to relocate the disk image. The path is under Settings → Resources → Advanced → Disk image location. The UI handles the file move correctly; doing it manually doesn't update Docker Desktop's internal references.

Common issues

The virtual disk file doesn't shrink. On macOS and Windows, deleting images with docker image rm or docker system prune frees space inside the virtual disk, but the .raw or .vhdx file itself stays the same size on your host filesystem. To actually reclaim host disk space on macOS, run:

bash
1
docker run --privileged --pid=host docker/desktop-reclaim-space

On Windows, you need to stop the Docker service and then compact the virtual disk. If you have the Hyper-V PowerShell module installed (Windows Pro/Enterprise), run Optimize-VHD in PowerShell against the .vhdx file. On Windows Home or WSL 0.67+, use wsl --shutdown followed by wsl --compact instead. Restart Docker when done.

Don't manually delete from /var/lib/docker/. It's tempting to rm -rf a hash-named directory when you're out of space and in a hurry. Docker's metadata won't match the filesystem if you do, and you'll end up with broken images that appear to exist but fail to run. Always use Docker's own commands to remove images and containers.

Build cache is often the real culprit. docker image ls might show modest usage, but docker system df often reveals that build cache is consuming more space than images. Clear it with:

bash
1
docker builder prune

This removes build cache without touching your images.

Final thoughts

Docker images on Linux are in /var/lib/docker/overlay2/; on macOS and Windows they're inside a virtual disk file that Docker Desktop manages for you. The overlay2 storage driver's layer deduplication means the actual storage footprint is usually smaller than you'd expect from pulling many images, but build cache and stopped container layers add up quietly over time. Running docker system df regularly and docker system prune when the numbers look bad is a habit worth developing.

If you're running containerized workloads in production, understanding where Docker stores images is just the start. Disk pressure on your container host surfaces in your application's performance long before the disk actually fills up. Dash0 is an OpenTelemetry-native observability platform that correlates host-level resource signals with application behavior across logs, metrics, and traces in a single view, so you're not jumping between tools when things get tight. If you're also interested in runtime container metrics, Monitoring Container Resource Usage with Docker Stats is a good next read.

Start a free trial (no credit card required).