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 (like RUN 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.

DriverStatusBest For
overlay2✅ DefaultMost Linux distributions (Ubuntu, CentOS, Fedora, Debian)
zfs✅ SupportedSystems already using ZFS for the host filesystem
vfs✅ SupportedTesting, no copy-on-write (slower, consumes more disk)
aufs⚠️ DeprecatedOlder Ubuntu versions; removed in modern Docker
devicemapper⚠️ DeprecatedRHEL/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: overlay2

The 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.

DriverTypeUse Case
localBuilt-inDefault. Stores data on the host filesystem at /var/lib/docker/volumes/
ebsCloud (AWS)Mounts Amazon EBS volumes into containers
gceCloud (GCP)Mounts Google Compute Engine persistent disks
azure_fileCloud (Azure)Mounts Azure File Storage shares
vsphereEnterpriseVMware vSphere storage
portworxEnterpriseSoftware-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_data

Storage 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 \
  postgres

Both 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 myapp

This 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 \
  myimage

Volume vs Bind Mount Comparison

AspectNamed VolumeBind Mount
Host pathDocker-managed (/var/lib/docker/volumes/)Any user-specified directory
Creationdocker volume create or auto-createdMust exist on host before mounting
Managementdocker volume ls/rm/pruneHost filesystem only
PortabilityPortable across hosts (driver abstracts location)Host-path dependent
Best forDatabases, application state, shared dataDevelopment (live code reload), config injection
PerformanceNative filesystem speedNative 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 myimage

Data 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:

DockerKubernetesDescription
Container layer (ephemeral)emptyDir volumePod-scoped scratch space, deleted when Pod dies
Named volumePersistentVolume (PV)Cluster-wide storage resource
Volume driver (local, ebs, gce)StorageClassDefines provisioner (aws-ebs, gce-pd, local) and parameters
docker volume createPersistentVolumeClaim (PVC)Requests storage from the cluster; triggers dynamic provisioning
Bind mount (-v /host:/container)hostPath volumeMounts a host directory into a Pod (node-local only)
tmpfs mountemptyDir with medium: MemoryRAM-backed ephemeral storage
Shared volume across containersPod volumes + container volumeMountsAll 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

PitfallCauseFix
Data lost after docker rmUsed container layer only, no volume mountAdd -v or --mount to persist data outside the container
Permission denied inside containerContainer user ID mismatches host file ownerUse named volumes (Docker manages permissions) or set user in Dockerfile
Bind mount overwrites container filesBind mount target matches existing directory in imageMount to a different path, or ensure host directory has required files
Volume fills up diskUnbounded log/data growthImplement log rotation; use docker system prune for cleanup
”Permission denied” on Docker socketUser not in docker groupsudo 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 -v

See Also


Tags: docker storage volumes bind-mount layered-architecture overlay2 persistent-storage devops cka-prerequisite