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:
1docker info | grep "Docker Root Dir"
1Docker 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.15C:\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:
1sudo ls /var/lib/docker/overlay2/
123456223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f73a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b4e9fa83caff3e8f4cc83693fa407a4a9fac9573deaf481506c102d484dd1e6a1e8876a226237217ec61c4baf238a32992291d059fdac95ed6303bdff3f59cff5eca1e4e1694283e001f200a667bb3cb40853cf2d1b12c29feda7422fed78afedl
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:
1docker system df
12345TYPE TOTAL ACTIVE SIZE RECLAIMABLEImages 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):
123{"data-root": "/mnt/docker-data"}
Then stop Docker, copy the existing data across, and restart:
1234sudo systemctl stop dockersudo rsync -aP /var/lib/docker/ /mnt/docker-data/sudo systemctl start dockerdocker info | grep "Docker Root Dir"
1Docker 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:
1docker 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:
1docker 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).