Subtopic•Volume Basics

What Are Volumes

You have already seen that a container's writable layer is ephemeral. Remove the container, and all data written inside it disappears. For stateless applications — APIs, web servers, workers — this is perfectly fine. But for stateful data — database records, user uploads, session state — you need storage that survives beyond the container's lifecycle.

Docker volumes are the answer. They are the recommended mechanism for persisting data generated by and used by Docker containers. A volume is a dedicated piece of storage that exists outside the container's writable layer and is managed entirely by Docker.

This file explains what volumes are, how they work at the filesystem level, and why they are the correct choice for production persistent storage.

The Core Problem Volumes Solve

Consider a PostgreSQL database container without a volume:

docker run -d --name db postgres:16

PostgreSQL stores all its data in /var/lib/postgresql/data inside the container. This path is part of the container's writable layer. Every row you insert, every table you create, every index you build — it all lives in that writable layer.

Now you need to update PostgreSQL from version 16 to 17:

docker stop db
docker rm db
docker run -d --name db postgres:17

When you removed the old container, Docker deleted its writable layer. All your data is gone. Every row, every table, every index — permanently deleted.

With a volume:

docker run -d --name db -v pgdata:/var/lib/postgresql/data postgres:16

PostgreSQL writes data into the volume at /var/lib/postgresql/data. The volume lives outside the container. When you remove the container and recreate it with the new image:

docker stop db && docker rm db
docker run -d --name db -v pgdata:/var/lib/postgresql/data postgres:17

The new container mounts the same volume. All the data is there. The upgrade is complete.


How Volumes Work Internally

A Docker volume is a directory on the Docker host's filesystem, managed by Docker in a dedicated location:

/var/lib/docker/volumes/
│
├── pgdata/
│   └── _data/         ← The actual volume contents
│       ├── PG_VERSION
│       ├── base/
│       ├── global/
│       └── pg_wal/
│
└── myapp-uploads/
    └── _data/
        ├── 2024/
        └── 2023/

When you mount a volume into a container, Docker bind-mounts the _data directory into the container's filesystem at the path you specify. The container writes to /var/lib/postgresql/data — Docker transparently routes those writes to /var/lib/docker/volumes/pgdata/_data on the host.

The critical difference from a regular bind mount: Docker fully manages this path. You never need to know or care about the /var/lib/docker/volumes/ path. Docker handles creation, permissions, ownership, and cleanup.


Volumes vs. the Writable Layer

Understanding why volumes are architecturally separate from the writable layer is essential.

PropertyWritable LayerDocker Volume
LocationOverlay filesystem (host disk)Host disk (/var/lib/docker/volumes/)
Survives docker rmNo — deleted with containerYes — persists independently
PerformanceSlower (OverlayFS copy-on-write overhead)Faster (direct filesystem access, no CoW)
Shared between containersNoYes
Managed by DockerYesYes
PortableNoYes (can be moved between hosts)

The performance difference is real. A database writing to a volume avoids the OverlayFS copy-on-write overhead of the writable layer. On write-intensive workloads like databases, this can make a significant difference. PostgreSQL on a volume is measurably faster than PostgreSQL writing to the writable layer.


Types of Volumes

Docker has three main types of volumes based on how they are named and created.

Named Volumes

The recommended approach. You give the volume a specific name. Docker creates and manages the storage location.

docker run -v pgdata:/var/lib/postgresql/data postgres:16

The volume named pgdata persists until you explicitly delete it with docker volume rm pgdata. It can be mounted into any container at any time.

Anonymous Volumes

Created when you mount a path without giving it a name. Docker generates a random UUID as the name.

docker run -v /var/lib/postgresql/data postgres:16

Docker creates a volume with an auto-generated name like 3f4a5b6c7d8e9f0a.... Anonymous volumes survive docker rm but are easy to lose track of. They are cleaned up when you run docker volume prune. Use named volumes instead — they are always explicitly named and intentional.

Declaring Volumes in a Dockerfile

The VOLUME instruction in a Dockerfile declares that a path inside the container should be backed by a volume:

FROM postgres:16
VOLUME /var/lib/postgresql/data

When a container is created from this image without an explicit -v flag, Docker automatically creates an anonymous volume mounted at /var/lib/postgresql/data. This ensures the container's data directory is never accidentally stored in the writable layer.

The official postgres:16 image uses this pattern. It is why running docker run postgres:16 without -v still creates a volume — the Dockerfile mandates it.


When to Use Volumes

Always use volumes for:

Databases — PostgreSQL, MySQL, MongoDB, Redis. Any data that must survive container restarts, deployments, and upgrades.

docker run -d \
  --name postgres \
  -v postgres-data:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=secret \
  postgres:16

User-generated content — File uploads, profile pictures, documents. Data created by users that must persist.

docker run -d \
  --name api \
  -v user-uploads:/app/uploads \
  myapp:1.0

Application state — Application-level caches, job queues, session stores (when using a file-based backend).

Do not use volumes for:

Application code — Use the image (COPY in Dockerfile). Volumes are for data, not code.

Configuration files — Use bind mounts or environment variables for config.

Temporary scratch space — Use tmpfs mounts for fast, non-persistent temporary data.


Volumes in AWS ECS

On AWS ECS, volumes behave differently depending on the deployment type.

ECS on EC2 — Standard Volumes

Volume data is stored on the EC2 instance's local storage. When the ECS task moves to a different instance (during autoscaling or instance replacement), it loses its local volume data.

For this reason, local ECS volumes are only appropriate for truly ephemeral data — build caches, temp files. Use EFS for persistent ECS data.

ECS + EFS (Elastic File System)

For persistent, shared storage in ECS, you use AWS EFS. EFS is a managed NFS filesystem that mounts into ECS tasks just like a Docker volume, but the data is stored redundantly in AWS and accessible from any instance.

{
  "volumes": [
    {
      "name": "postgres-data",
      "efsVolumeConfiguration": {
        "fileSystemId": "fs-0a1b2c3d4e5f6789",
        "rootDirectory": "/postgres",
        "transitEncryptionPort": 2049,
        "transitEncryption": "ENABLED",
        "authorizationConfig": {
          "accessPointId": "fsap-0987654321",
          "iam": "ENABLED"
        }
      }
    }
  ],
  "containerDefinitions": [
    {
      "name": "db",
      "mountPoints": [
        {
          "containerPath": "/var/lib/postgresql/data",
          "sourceVolume": "postgres-data"
        }
      ]
    }
  ]
}

The ECS task mounts the EFS filesystem into the container exactly where PostgreSQL expects its data. Whether the task runs on EC2 instance A or instance B, it mounts the same EFS data. This is the production-grade persistent storage pattern on AWS ECS.


Summary

  • Docker volumes are storage managed by Docker that exists outside the container's ephemeral writable layer — volume data persists when containers are stopped, removed, or replaced
  • Internally, a volume is a directory under /var/lib/docker/volumes/ that Docker mounts into the container at the specified path
  • Volumes bypass the OverlayFS copy-on-write overhead of the writable layer — writes go directly to the host filesystem, making databases significantly faster
  • Named volumes are the recommended approach — you specify the name, Docker manages the location
  • Always use volumes for stateful data: databases, user uploads, application state
  • The VOLUME instruction in a Dockerfile causes Docker to automatically create an anonymous volume at container start if no explicit -v is provided
  • On AWS ECS, use EFS volumes instead of local volumes for data that must survive instance replacements

In the next file, we cover how to create and use volumes in detail — the complete CLI and Docker Compose workflow.

←Previous ModuleDocker Networking