Docker from First PrinciplesPost 7 of 18 · 18 of 18 written
- How to use this guide
- 01The problem Docker solves
- 02What a container really is
- 03Images: layers, hashes and registries
- 04Docker's architecture
- 05Container lifecycle
- 06Dockerfiles in depth
- 07Storage: where data lives
- 08Networking
- 09Docker Compose for multi-service AI apps
- 10GPUs in containers
- 11Building good images for AI workloads
- 12Serving and production patterns
- 13Security
- 14Debugging and introspection
- 15Beyond Docker
- 16Hands-on learning path
- 17Question bank
- 18Glossary and cheat sheet
7. Storage: where data lives
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 myimgThree 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.