Storage

Container filesystems are disposable. Store important state in volumes, bind mounts, external services, or object storage.

Storage choices

Docker storage choices

Storage

Use it for

Watch out for

Image filesystem

Files baked into the image.

Changes disappear when the container is removed.

Named volume

Database data, caches, local persistent state.

Ownership must match the runtime user.

Bind mount

Source code and host-controlled files.

Host path layout and OS behavior leak into the container.

tmpfs

Temporary sensitive or high-churn files.

Data disappears when the container stops.

Named volumes

Use named volumes for container-owned persistent state.

1docker volume create db_data
2docker run -d \
3    --name db \
4    -e MYSQL_ROOT_PASSWORD=change-me \
5    -v db_data:/var/lib/mysql \
6    mysql:latest

Compose example:

1services:
2  db:
3    image: mysql:latest
4    volumes:
5      - db_data:/var/lib/mysql
6
7volumes:
8  db_data:

Bind mounts

Use bind mounts when the host owns the files, such as local source code.

1docker run --rm \
2    --mount type=bind,source="$(pwd)",target=/workspace,readonly \
3    alpine:latest ls /workspace

Prefer --mount over short -v syntax for scripts because it is explicit.

Compose example:

1services:
2  docs:
3    image: python:3-slim
4    working_dir: /workspace
5    volumes:
6      - type: bind
7        source: .
8        target: /workspace
9        read_only: true

tmpfs mounts

Use tmpfs for temporary files that should not be written to disk.

1docker run --rm \
2    --tmpfs /run/secrets:rw,noexec,nosuid,size=1m \
3    alpine:latest sh

Compose example:

1services:
2  api:
3    image: api:local
4    tmpfs:
5      - /tmp

Non-root ownership

Non-root containers need writable mounts owned by the runtime UID or GID. Do not solve ownership failures by running as root.

For files copied into the image, set ownership during the build.

1RUN useradd --uid 10001 --create-home app
2WORKDIR /app
3COPY --chown=10001:10001 . .
4USER 10001:10001

For Kubernetes volumes, use fsGroup when the workload needs group write access.

1securityContext:
2  runAsNonRoot: true
3  runAsUser: 10001
4  runAsGroup: 10001
5  fsGroup: 10001

Back up a volume

Back up a named volume by mounting it into a one-off container.

1docker run --rm \
2    -v db_data:/data:ro \
3    -v "$(pwd)":/backup \
4    alpine:latest \
5    tar czf /backup/db_data.tgz -C /data .

Restore into a new volume.

1docker volume create db_data_restore
2docker run --rm \
3    -v db_data_restore:/data \
4    -v "$(pwd)":/backup \
5    alpine:latest \
6    tar xzf /backup/db_data.tgz -C /data

Practical rules

  • Treat the image filesystem as read-only application code.

  • Use named volumes for container-owned state.

  • Use bind mounts for host-owned source files.

  • Use read-only mounts by default.

  • Put temporary writes in /tmp or an explicit tmpfs mount.

  • Back up named volumes before destructive changes.

  • Fix UID/GID ownership rather than running the container as root.

References