Docker from First PrinciplesPost 7 of 18 · 18 of 18 written

7. Storage: where data lives

Docker · 7 of 183 min readOct 2026

The container’s own filesystem is disposable and slow for heavy writes. Anything large or worth keeping (datasets, model weights, checkpoints, caches, databases) goes in a volume or a bind mount, which bypass the copy-on-write layer and outlive the container.

The five places data can live

Type Where the bytes are Lifetime Use it for
Writable layer The container’s OverlayFS upper directory Deleted with the container Small temporary files only
Named volume A directory Docker manages (/var/lib/docker/volumes/… on Linux) Until you delete the volume Hugging Face caches, vector databases, Postgres, Redis
Bind mount Any host path you choose As long as the host path exists Source code in development, datasets, checkpoints and outputs
tmpfs mount RAM Until the container stops Secrets, fast scratch space
/dev/shm Shared memory, 64 MB by default Until the container stops PyTorch DataLoader workers, NCCL (resize with --shm-size)

Syntax

# Named volume (created automatically if missing)
docker run -v hf-cache:/cache/hf myimg

# Bind mount, read-only
docker run -v /data/imagenet:/data:ro myimg

# The explicit --mount form, clearer in scripts
docker run --mount type=bind,src="$PWD"/runs,dst=/workspace/runs myimg
docker run --mount type=tmpfs,dst=/tmp,tmpfs-size=2g myimg

Three behaviours to know. With -v, a host path that doesn’t exist is silently created as an empty root-owned directory; --mount fails instead, which catches typos. Mounting over a directory hides whatever the image had there. And an empty named volume is pre-filled with the image’s contents at that path on first use, while a bind mount never is.

Model weights: three strategies

Strategy Strengths Weaknesses Use when
Bake into the image One immutable artifact, works offline, no startup download Images of tens of GB, slow pushes and pulls, rebuild on every weight change A fixed production model, air-gapped sites, small models
Mount a cache volume or host directory Fast iteration, one copy shared by many containers Depends on host state; first run downloads Development, a single server with several services
Download at startup Small, generic image Cold starts of minutes, needs network and a token Autoscaled services, ideally combined with a cache volume

For Hugging Face models, point the cache at a volume: set HF_HOME=/cache/hf and mount a named volume at /cache/hf. Every container sharing that volume reuses the download. Pass tokens as runtime environment variables, never as ENV or ARG in the Dockerfile.

Datasets and checkpoints

Mount datasets read-only (:ro), so a buggy script can’t damage them. On clusters, the shared filesystem (NFS, Lustre, GPFS) is mounted on the host, and you bind-mount the relevant path into the container. Write checkpoints and logs to a bind-mounted output directory, so they survive when the container is removed and can be read from the host.

Performance on your Mac

Bind mounts from macOS cross into Docker Desktop’s VM, so many small file operations are slow. Keep the Python environment inside the image or in a named volume, never on a bind mount. Bind-mount only your source code, and for heavy data loading prefer named volumes, which live inside the VM’s own disk.

Backups and housekeeping

Back up a volume by mounting it into a throwaway container alongside a host directory:

docker run --rm -v qdrant-data:/data -v "$PWD":/backup alpine \
  tar czf /backup/qdrant-data.tgz -C /data .

AI images are large: CUDA PyTorch images commonly weigh 5 to 15 GB, so disks fill fast. docker system df shows usage by images, containers, volumes and build cache. docker image prune removes dangling images, docker builder prune clears build cache, and docker system prune -a removes everything unused (add --volumes only if you mean it). Docker Desktop’s VM has its own disk limit, set in its settings.