Introduction to Docker Volumes for Data Persistence and Sharing

Containers are ephemeral by nature: they start fast, stop cleanly, and can be replaced without leftover state. However, real applications need data that survives container restarts, crashes, or updates. Docker volumes solve this by storing data outside the container's writable layer while keeping it accessible to one or more containers. Containers can share the same volume to exchange files, synchronize configuration, or serve as a common data store. This article covers everything you need to know about using Docker volumes for data sharing between containers, from creation and management to advanced patterns like volume drivers and integration with Docker Compose.

Understanding Docker Volumes vs. Bind Mounts

Before diving into volumes, it's important to distinguish them from bind mounts. A bind mount maps a host directory or file into a container. The host controls the path and permissions, and the container works with that existing filesystem. In contrast, a volume is a special directory managed entirely by Docker. Volumes reside in the Docker area on the host filesystem (/var/lib/docker/volumes/ on Linux) and offer advantages:

  • Portability: Volumes can be backed up, migrated, and managed using Docker CLI commands.
  • Security: Only Docker and containers with explicit volume mounts can access volume data.
  • Performance: On macOS and Windows, volumes are faster than bind mounts because Docker can use native filesystem integration.
  • Driver abstraction: Volume drivers allow storing data on remote hosts, cloud providers, or encrypted filesystems.

For data sharing between containers, volumes are the recommended approach. Bind mounts are more suited for development scenarios where you need to share source code between host and container.

Creating and Managing Volumes

Creating a Volume

Use docker volume create to create a named volume:

docker volume create my_data

You can also specify options like driver, labels, and size limits (if the driver supports them):

docker volume create --driver local --label env=prod bigdata

Listing and Inspecting Volumes

List all volumes with docker volume ls. To see details about a specific volume:

docker volume inspect my_data

The output shows the mountpoint on the host filesystem, the driver, and labels. This is useful when you need to locate data for backup or manual checks.

Removing Volumes

To delete a volume, use docker volume rm my_data. If a volume is still in use by a running or stopped container, you must stop and remove the container first, or force removal with -f. To clean up all unused volumes (orphans), run:

docker volume prune

This command prompts for confirmation and removes any volume not referenced by a container.

Mounting Volumes in Containers

When you run a container, you mount a volume using the -v or --mount flag. The -v syntax is shorter: -v volume_name:/container/path. The --mount syntax is more explicit and preferred in scripts because each option is a key‑value pair:

docker run -d --name myapp \
  --mount source=my_data,target=/app/data \
  my_image

If the volume doesn't exist, -v will automatically create it; --mount will fail unless you create it beforehand. Both methods allow setting read‑only access:

docker run -d --name config-reader -v my_data:/config:ro alpine cat /config/settings.json

Sharing Data Between Multiple Containers

The real power of volumes is that multiple containers can mount the same volume simultaneously. They read and write to the same underlying directory, enabling data exchange. Here's a practical scenario: a database container and a backup container sharing a volume.

Example: Database and Backup Containers

Create a volume for database storage:

docker volume create db_data

Start a MySQL container that writes data to the volume:

docker run -d --name mysql-db \
  -e MYSQL_ROOT_PASSWORD=secret \
  -v db_data:/var/lib/mysql \
  mysql:8.0

Now run a backup container that mounts the same volume and performs a dump:

docker run --rm \
  -v db_data:/var/lib/mysql:ro \
  -v $(pwd):/backup \
  alpine \
  tar czf /backup/mysql_backup.tar.gz /var/lib/mysql

The backup container reads the MySQL data directory (read‑only) and creates a compressed archive in the current host directory. This pattern keeps the database container untouched and uses the volume as a shared data bridge.

Configuration Files Shared Across Services

Another common use case is sharing configuration files. Create a volume with default configs, then mount it in multiple service containers. For example, a reverse proxy and an application server can both access the same SSL certificates or shared settings.

docker volume create shared_config
docker run -d --name nginx-proxy \
  -v shared_config:/etc/nginx/ssl:ro \
  nginx
docker run -d --name app-server \
  -v shared_config:/etc/app/ssl:ro \
  myapp

Volume Drivers and Advanced Use Cases

By default, volumes use the local driver, which stores data on the Docker host. For distributed or cloud environments, you can install volume drivers that store data on network storage, S3, or other backends. Popular drivers include:

  • Rex-Ray: Supports Amazon EBS, EFS, S3, and many others.
  • Portworx: Provides storage for containerized databases in Kubernetes and Docker Swarm.
  • NetApp Trident: Integrates with NetApp storage arrays.
  • GlusterFS and Ceph: Distributed filesystems for cloud-native storage.

To use a non‑local driver, specify it when creating the volume:

docker volume create --driver rexray/ebs --opt size=20 ebs_volume

Containers then mount the volume as usual, but the data lives on the remote storage, enabling high availability and data migration across hosts.

Backing Up and Restoring Docker Volumes

Since volumes contain critical data, regular backups are essential. The simplest method is to use a utility container that mounts the volume and creates an archive:

docker run --rm \
  -v my_data:/source \
  -v $(pwd):/backup \
  alpine \
  tar czf /backup/my_data_backup_$(date +%F).tar.gz -C /source .

To restore, copy the archive into a container and extract:

docker run --rm \
  -v my_data:/target \
  -v $(pwd):/backup \
  alpine \
  tar xzf /backup/my_data_backup_2025-03-30.tar.gz -C /target

For databases, use their native dump tools (e.g., mysqldump, pg_dump) to create logical backups instead of raw filesystem archives. This ensures consistency and portability across versions.

Best Practices for Using Docker Volumes

  • Always use named volumes. Anonymous volumes are created automatically when you omit a name (-v /data). They are hard to track and reuse. Named volumes make your intentions clear.
  • Apply labels. Label your volumes with metadata like env=production, backup=true to manage them programmatically.
  • Use read‑only mounts when possible. If a container only needs to read shared data, set the mount to ro to prevent accidental modifications.
  • Back up volumes regularly. Automate backups with cron jobs or orchestrated tasks as shown above.
  • Separate data from application logic. Keep volume data out of the container image. Store configuration, database files, and user uploads in volumes.
  • Consider volume drivers for production. For multi‑host deployments, use a volume driver that supports replication and failover.
  • Clean up unused volumes. Run docker volume prune periodically to reclaim disk space.

Troubleshooting Common Volume Issues

Permission Errors

If a container cannot write to a volume, check the user ID inside the container. For example, MySQL runs as user ID 999. If the host directory (for bind mounts) or the volume's default permissions block that user, you may see errors. Solutions include:

  • Setting the container to run as root (temporary).
  • Using a Dockerfile to adjust permissions on the mount point.
  • For named volumes, Docker sets permissions to the container's user automatically, but if you manually manipulate the volume's backing directory, permissions may break.

Data Not Surviving Container Recreation

If data disappears after removing and recreating a container, ensure you mounted a named volume, not an anonymous or bind mount. Verify with docker container inspect <container_id> under "Mounts".

Conflicting Mounts

Two containers should never concurrently write to the same file without a locking mechanism. For databases, the database engine handles mutual exclusion internally. For shared configuration files, consider using read‑only mounts for non‑writers or design a leader‑follower pattern.

Using Docker Compose with Volumes

In multi‑container applications, Docker Compose simplifies volume management. Define volumes in the top‑level volumes section, then reference them in service definitions:

version: "3.9"
services:
  db:
    image: mysql:8.0
    volumes:
      - db_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: secret
  app:
    image: myapp
    volumes:
      - db_data:/app/db_data:ro
    depends_on:
      - db
volumes:
  db_data:
    driver: local

Compose automatically creates the db_data volume when you run docker compose up. You can also specify driver options, labels, and external volumes (volumes created outside Compose). This makes reproducible environments easy to share across development, staging, and production.

Volumes with Docker Swarm and Kubernetes

In an orchestrated environment, volumes become even more powerful. Docker Swarm supports volume drivers and does not require any special configuration—services mount volumes the same way as individual containers. Kubernetes uses PersistentVolumeClaims (PVCs) and StorageClasses, which abstract the underlying volume driver. While the Docker CLI differs from kubectl, the underlying concept of a decoupled, shared storage resource remains the same.

Security Considerations

Volumes can expose sensitive data if not properly restricted. Follow these security practices:

  • Use read‑only mounts for sidecars that only need to read secrets or configuration.
  • Never run a container with privilege escalation if it only needs volume access.
  • Encrypt volume data at rest when using drivers that support it (e.g., local with encryption via filesystem‑level tools).
  • Regularly audit volumes: remove unused ones and restrict access to necessary containers.
  • For production, avoid storing secrets in plaintext inside volumes. Use Docker secrets or external secret stores.

Conclusion

Docker volumes are indispensable for building stateful containerized applications. They provide a clean, portable way to persist data and share it across containers without coupling to the host filesystem. By mastering named volumes, volume drivers, backup strategies, and integration with orchestration tools, you can design robust and scalable architectures. Start with simple named volumes, then explore advanced patterns like backup containers, configuration sharing, and remote storage drivers. Proper volume management ensures your data remains safe, accessible, and independent of container lifecycles.

For further reading, consult the official Docker volumes documentation, the Compose file volumes reference, and volume driver plugins.