Docker Storage
How Docker handles filesystems, layers, persistence, and data sharing. Understanding Docker storage is a prerequisite for Kubernetes volumes, PersistentVolumes, and StorageClasses — the concepts map directly. Source: CKA Day 28
Layered Architecture
A Docker image is a stack of read-only layers. Each instruction in a Dockerfile creates one layer:
FROM node:18-alpine → Layer 1: base image (~60 MB)
WORKDIR /app → Layer 2: directory metadata
COPY package*.json ./ → Layer 3: dependency manifest files
RUN npm install → Layer 4: installed node_modules
COPY . . → Layer 5: application source code
EXPOSE 3000 → Metadata (no layer)
CMD ["node", "server.js"] → Metadata (no layer)Build Cache Behavior
When you rebuild an image after a code change, Docker reuses layers from cache up to the first changed instruction:
Step 1/6 : FROM node:18-alpine → Using cache
Step 2/6 : WORKDIR /app → Using cache
Step 3/6 : COPY package*.json ./ → Using cache
Step 4/6 : RUN npm install → Using cache
Step 5/6 : COPY . . → **Rebuilding** (source code changed)
Step 6/6 : CMD ["node", "server.js"] → Using cache
Layer ordering best practice: Put frequently changing instructions (like
COPY . .) near the end of the Dockerfile. Put slow, stable instructions (likeRUN npm install) early so they stay cached.
Immutable Infrastructure
Because image layers are read-only, a built image is immutable. The same image promoted from dev → staging → production cannot be accidentally modified mid-pipeline. This is a core principle of reliable deployments.
The Container Layer (Writable)
When you run docker run, Docker creates a writable container layer on top of the read-only image layers:
┌─────────────────────────────────────────┐
│ Container Layer (writable) │ ← New files, modifications, logs
├─────────────────────────────────────────┤
│ Image Layer N (read-only) │ ← COPY . .
│ Image Layer N-1 (read-only) │ ← RUN npm install
│ ... │
│ Image Layer 1 (read-only) │ ← FROM node:18-alpine
└─────────────────────────────────────────┘
- The container layer is a copy-on-write overlay
- You can create, modify, and delete files inside the running container
- When the container is removed, the container layer is destroyed — all changes are lost
This is fine for stateless applications but problematic for databases, file uploads, or any stateful workload.
Storage Drivers
Storage drivers manage how image layers and the container layer are stored and accessed on disk.
| Driver | Status | Best For |
|---|---|---|
| overlay2 | ✅ Default | Most Linux distributions (Ubuntu, CentOS, Fedora, Debian) |
| zfs | ✅ Supported | Systems already using ZFS for the host filesystem |
| vfs | ✅ Supported | Testing, no copy-on-write (slower, consumes more disk) |
| aufs | ⚠️ Deprecated | Older Ubuntu versions; removed in modern Docker |
| devicemapper | ⚠️ Deprecated | RHEL/CentOS legacy; replaced by overlay2 |
Docker selects the optimal storage driver automatically based on the host OS. You can check your current driver:
docker info | grep "Storage Driver"
# Storage Driver: overlay2The storage driver is responsible for:
- Creating and managing read-only image layers
- Creating the writable container layer
- Merging layers into a single unified filesystem view
Volume Drivers
Volume drivers manage persistent storage — data that lives outside the container lifecycle.
| Driver | Type | Use Case |
|---|---|---|
| local | Built-in | Default. Stores data on the host filesystem at /var/lib/docker/volumes/ |
| ebs | Cloud (AWS) | Mounts Amazon EBS volumes into containers |
| gce | Cloud (GCP) | Mounts Google Compute Engine persistent disks |
| azure_file | Cloud (Azure) | Mounts Azure File Storage shares |
| vsphere | Enterprise | VMware vSphere storage |
| portworx | Enterprise | Software-defined storage for containers |
The volume driver is what makes data persistent — it writes to host disk, network storage, or cloud block storage instead of the container’s ephemeral overlay.
Docker Volumes
A named volume is the recommended way to persist data in Docker. Docker manages the volume lifecycle independently of containers.
Lifecycle
# Create a volume explicitly
docker volume create pg_data
# Or let Docker auto-create it on first use
docker run -d -v pg_data:/var/lib/postgresql/data postgres
# List volumes
docker volume ls
# Inspect a volume
docker volume inspect pg_data
# Shows: Mountpoint, Driver, Labels, Scope
# Remove a volume (data is deleted)
docker volume rm pg_dataStorage Location
On Linux hosts, Docker stores named volume data at:
/var/lib/docker/volumes/<volume_name>/_data/
This is the default for the local driver. Cloud drivers store data in their respective backends.
Volume Mount Syntax
# Short syntax (-v)
docker run -d -v pg_data:/var/lib/postgresql/data postgres
# Long syntax (--mount)
docker run -d \
--mount type=volume,source=pg_data,target=/var/lib/postgresql/data \
postgresBoth are equivalent. The --mount syntax is more explicit and self-documenting.
Sharing Volumes Between Containers
Multiple containers can mount the same volume for shared data access:
# Container 1: writes data
docker run -d -v shared_data:/data --name writer myapp
# Container 2: reads the same data
docker run -d -v shared_data:/data --name reader myappThis is the Docker equivalent of Kubernetes’ shared volume pattern in multi-container Pods.
Bind Mounts
A bind mount maps an existing host directory directly into a container. Unlike named volumes, Docker does not manage the storage location — you specify the exact host path.
Syntax
# Short syntax (-v)
docker run -d -v /home/user/myapp:/app myimage
# Long syntax (--mount)
docker run -d \
--mount type=bind,source=/home/user/myapp,target=/app \
myimageVolume vs Bind Mount Comparison
| Aspect | Named Volume | Bind Mount |
|---|---|---|
| Host path | Docker-managed (/var/lib/docker/volumes/) | Any user-specified directory |
| Creation | docker volume create or auto-created | Must exist on host before mounting |
| Management | docker volume ls/rm/prune | Host filesystem only |
| Portability | Portable across hosts (driver abstracts location) | Host-path dependent |
| Best for | Databases, application state, shared data | Development (live code reload), config injection |
| Performance | Native filesystem speed | Native filesystem speed |
Common Use Cases
Named volumes:
- Database persistence (PostgreSQL, MySQL, MongoDB data directories)
- Application uploads and user-generated content
- Shared state between container restarts
Bind mounts:
- Live code reloading during development (
-v $(pwd):/app) - Injecting host config files (nginx.conf, app settings)
- Accessing host logs or certificates from inside a container
tmpfs Mounts (In-Memory)
For ephemeral, high-performance storage that never touches disk:
docker run -d --mount type=tmpfs,target=/cache,tmpfs-size=100m myimageData in tmpfs mounts is lost when the container stops. Useful for caches, session stores, or sensitive data that should not persist.
Docker to Kubernetes Storage Bridge
Docker storage concepts map directly to Kubernetes primitives:
| Docker | Kubernetes | Description |
|---|---|---|
| Container layer (ephemeral) | emptyDir volume | Pod-scoped scratch space, deleted when Pod dies |
| Named volume | PersistentVolume (PV) | Cluster-wide storage resource |
| Volume driver (local, ebs, gce) | StorageClass | Defines provisioner (aws-ebs, gce-pd, local) and parameters |
docker volume create | PersistentVolumeClaim (PVC) | Requests storage from the cluster; triggers dynamic provisioning |
Bind mount (-v /host:/container) | hostPath volume | Mounts a host directory into a Pod (node-local only) |
| tmpfs mount | emptyDir with medium: Memory | RAM-backed ephemeral storage |
| Shared volume across containers | Pod volumes + container volumeMounts | All containers in a Pod can mount the same volume |
This mapping is why the CKA course teaches Docker storage before Kubernetes storage — the mental model is the same, but Kubernetes adds cluster-wide abstraction layers.
Common Pitfalls
| Pitfall | Cause | Fix |
|---|---|---|
Data lost after docker rm | Used container layer only, no volume mount | Add -v or --mount to persist data outside the container |
| Permission denied inside container | Container user ID mismatches host file owner | Use named volumes (Docker manages permissions) or set user in Dockerfile |
| Bind mount overwrites container files | Bind mount target matches existing directory in image | Mount to a different path, or ensure host directory has required files |
| Volume fills up disk | Unbounded log/data growth | Implement log rotation; use docker system prune for cleanup |
| ”Permission denied” on Docker socket | User not in docker group | sudo usermod -aG docker $USER (log out and back in) |
Imperative Commands Cheatsheet
# Volumes
docker volume create my_vol
docker volume ls
docker volume inspect my_vol
docker volume rm my_vol
docker volume prune # Remove all unused volumes
# Run with volume
docker run -d -v my_vol:/data myimage
docker run -d --mount type=volume,source=my_vol,target=/data myimage
# Run with bind mount
docker run -d -v /host/path:/container/path myimage
docker run -d --mount type=bind,source=/host/path,target=/container/path myimage
# Inspect container mounts
docker inspect <container_id> --format='{{json .Mounts}}'
# Disk usage
docker system df -vSee Also
- Docker Fundamentals — Container architecture, images, and workflow
- Dockerize a Project — Writing Dockerfiles, multi-stage builds, and layer caching
- Run vs Attach vs Exec — Container process interaction
- Kubernetes Concepts Index — Storage section: Volumes, PersistentVolumes, PVCs, StorageClasses
- Kubernetes Storage — deep-dive on PVs, PVCs, StorageClasses, and the full K8s storage model
- Multi-Container Pods — Shared volumes between containers in a Pod
- Init Containers — Pre-start setup using shared volumes
- Sidecar Pattern — Logging and monitoring sidecars that share volumes with the main app
Tags: docker storage volumes bind-mount layered-architecture overlay2 persistent-storage devops cka-prerequisite