Mantra Networking Mantra Networking

Docker: Volumes

Docker: Volumes
Created By: Lauren R. Garcia

Table of Contents

  • Overview
  • Volume Commands
  • Dockerfile Volume Declaration
  • Volume Usage in Docker Compose
  • Populating and Sharing Volumes
  • Backup and Restore
  • Storage Drivers & Plugins
  • Volume Cleanup
  • Best Practices
  • Conclusion

Docker Volumes: Overview

What Is a Docker Volume?

A Docker volume is a persistent data storage mechanism managed by Docker, designed specifically for use with containers. Unlike the ephemeral filesystem inside a container—which is lost when a container stops or is removed—a volume stores data independently of the container lifecycle. This allows data to survive container restarts, updates, and removals.

Why Should You Care About Docker Volumes?

Understanding Docker volumes is critical for anyone running applications in containers, especially when:

  • Data Persistence is required: Databases, user uploads, and application state need to be preserved beyond a single container’s life.
  • Stateful Applications need to be Dockerized: Tools and servers that must retain logs, user data, or configuration files benefit from volumes.
  • Sharing Data between containers: Volumes enable simultaneous access to the same data from multiple containers.
  • Safe Backups and Migration: Volumes can be efficiently backed up and migrated independently of containers.

Neglecting to use volumes can result in data loss, complicated recovery procedures, and unreliable operations, especially in production environments or multi-container orchestration.

How Do Docker Volumes Work?

  • Volume Creation: Volumes can be created manually via the Docker CLI, automatically through Docker Compose, or declared in a Dockerfile.
  • Mounting Volumes: When a container starts, you can mount a volume to a specific path in the container’s filesystem. Any data written to this path is stored in the volume on the Docker host.
  • Data Isolation: Volumes are managed by Docker, isolated from the underlying host system, and can leverage different storage backends and plugins.
  • Sharing and Portability: The same volume can be reused by multiple containers, making it ideal for sharing files or database storage among application components.
  • Safety and Restore: Volumes can be backed up or restored using standard tools, providing a clear path for disaster recovery.

Docker volumes are a core part of managing persistent, production-ready containers, allowing you to decouple storage management from the container’s lifecycle and maintain robust, scalable architectures.

Volume Commands

Docker provides CLI commands for managing volumes independently from containers. These let you create, inspect, mount, and clean up volumes with ease.

  • Step 1 – Create a Volume: Use this to provision a persistent storage volume on the Docker host.
    docker volume create my_volume
  • Step 2 – List Volumes: Displays all volumes currently available on the host.
    docker volume ls
  • Step 3 – Inspect a Volume: Shows detailed metadata for a specific volume, including mount point, driver, and labels.
    docker volume inspect my_volume
  • Step 4 – Mount a Volume to a Container: Attaches the volume to a container to persist application state across restarts.
    docker run -it -v my_volume:/data alpine
  • Step 5 – Mount as Read-Only: Protects volume data from modification. Useful for config sharing between containers.
    docker run -it -v my_volume:/app:ro alpine
  • Step 6 – Remove a Volume: Deletes a volume that is no longer in use.
    docker volume rm my_volume
  • Step 7 – Prune Unused Volumes: Automatically removes all unused (dangling) volumes to reclaim space.
    docker volume prune

Dockerfile Volume Declaration

Declaring volumes in a Dockerfile allows you to specify mount points that will persist data outside the container’s writable layer. This practice ensures important data is stored independently of container lifecycle events.

  • Step 1 – Use the VOLUME instruction: Add a VOLUME statement followed by the path inside the container where the volume should be mounted.
    VOLUME /data
  • Step 2 – Understand automatic volume creation: When a container starts from an image with a VOLUME declaration, Docker automatically creates an anonymous volume for the specified path if you do not specify one explicitly at runtime.
  • Step 3 – Override the volume mount: You can manually mount a named or existing volume to the declared path when running the container:
    docker run -v my_volume:/data my_image
  • Step 4 – Build an example Docker image: Here's a simple Dockerfile snippet that declares a volume:
    FROM ubuntu:latest
    VOLUME /app_data
    CMD ["bash"]
  • Step 5 – Run a container from this image: Starting a container will create a volume mounted at /app_data automatically:
    docker build -t example-volume .
    docker run -it example-volume
  • Step 6 – Benefit from volume behavior: Data written to /app_data inside the container persists across container restarts and is stored separately from the container’s writable layer.

Volume Usage in Docker Compose

Using volumes in Docker Compose allows you to simplify persistent storage management across multi-container applications. The volumes section ensures that data survives container rebuilds and restarts while keeping configuration centralized.

  • Step 1 – Define the volume at the top level: In your docker-compose.yml file, declare volumes under the volumes: root section.
    volumes:
      app_data:
  • Step 2 – Mount the volume into a service: Under the services section, use the service’s volumes attribute to mount the named volume to a directory inside the container.
    services:
      web:
        image: nginx:latest
        volumes:
          - app_data:/usr/share/nginx/html
  • Step 3 – Launch the stack: Run the Docker Compose deployment. The volume will be automatically created and attached.
    docker compose up -d
  • Step 4 – Inspect created volumes: You can verify that the volume has been successfully created using:
    docker volume ls
  • Step 5 – Use an existing volume: To reference a pre-existing volume, define it and add the external: true property.
    volumes:
      shared_data:
        external: true
    Then reference it in a service:
    services:
      app:
        image: my_app
        volumes:
          - shared_data:/data
  • Step 6 – Teardown the environment: When you're done, bring everything down and optionally remove the volumes.
    docker compose down -v

Populating and Sharing Volumes

Docker volumes can be preloaded with data and shared across multiple containers to enable collaboration and persistent state sharing. This is especially helpful for applications that require shared configurations, datasets, or initialization steps.

  • Step 1 – Start with a container that writes to the volume: When mounting a new volume to a path that already contains data in the image, Docker will copy the existing data into the volume automatically.
    docker run --name seed-container -v shared_vol:/data busybox \
    sh -c "echo 'hello world' > /data/hello.txt"
  • Step 2 – Launch another container that shares the same volume: The second container will mount the same volume and be able to access the data written by the first container.
    docker run --rm -v shared_vol:/data alpine cat /data/hello.txt
  • Step 3 – Use Docker Compose to share volumes across services: Define a shared volume and attach it to multiple services in your docker-compose.yml setup.
    volumes:
      shared_data:
    
    services:
      app:
        image: alpine
        volumes:
          - shared_data:/shared
    
      worker:
        image: alpine
        volumes:
          - shared_data:/shared
  • Step 4 – Verify content consistency: Any files written to the volume from one container will be visible and editable from other containers that mount the same volume path.
    docker exec -it app_container sh -c "echo 'from app' > /shared/info.txt"
    docker exec -it worker_container cat /shared/info.txt
  • Step 5 – Avoid data conflicts with read-only mounts: To prevent write collisions, you can attach the volume as read-only on containers that should not modify the data.
    docker run -v shared_vol:/data:ro alpine cat /data/hello.txt

Backup and Restore

Backing up and restoring Docker volumes ensures your application data can be preserved and recovered across container lifecycle events. This is essential for disaster recovery, migrations, and replicating environments.

  • Step 1 – Create a volume and populate it with data: Start by preparing a volume with some sample data.
    docker volume create my_volume
    docker run --rm -v my_volume:/data alpine sh -c "echo 'backup me' > /data/file.txt"
  • Step 2 – Backup the volume into a tar file: Mount the volume into a temporary container and archive its content using the tar command.
    docker run --rm \
      -v my_volume:/volume \
      -v $(pwd):/backup \
      alpine \
      tar czvf /backup/my_backup.tar.gz -C /volume .
  • Step 3 – View the backup archive: The archive my_backup.tar.gz is now available in your current working directory.
    ls -lh my_backup.tar.gz
  • Step 4 – Create a new volume for restore test: Set up a new volume to receive your backup data.
    docker volume create restored_volume
  • Step 5 – Restore the archive into a volume: Extract the tarball into the target volume using the same approach.
    docker run --rm \
      -v restored_volume:/volume \
      -v $(pwd):/backup \
      alpine \
      tar xzvf /backup/my_backup.tar.gz -C /volume
  • Step 6 – Validate the restored data: Use a container to check that the restored content is intact.
    docker run --rm -v restored_volume:/data alpine cat /data/file.txt

Storage Drivers & Plugins

Docker supports a variety of storage drivers and plugins to meet different workload and infrastructure requirements. Drivers determine how Docker stores volume data, while plugins extend storage backend options to network, cloud, and custom solutions.

  • Step 1 – Understand the default storage driver: By default, Docker uses the local driver for volumes, storing data on the host under /var/lib/docker/volumes (Linux systems).
  • Step 2 – Specify a different storage driver: When creating a volume, you can use the -d flag to set a different driver, such as NFS, CIFS, or third-party options.
    docker volume create -d local myvolume
    docker volume create -d nfs --name nfs_data \
      -o type=nfs \
      -o o=addr=10.0.0.20,nolock,soft,rw \
      -o device=:/path/to/dir
  • Step 3 – Add and use storage plugins: Plugins expand volume support to cloud providers or custom backends. Install a plugin with:
    docker plugin install --grant-all-permissions rclone/docker-volume-rclone --alias rclone
  • Step 4 – Create a volume with a plugin: Use driver-specific options to configure the volume as required.
    docker volume create \
      -d rclone \
      --name remote_volume \
      -o type=sftp \
      -o path=remote \
      -o sftp-host=1.2.3.4 \
      -o sftp-user=user
  • Step 5 – Attach plugin-backed volume to a container: Run a container that leverages your custom or remote storage configuration.
    docker run -d \
      --name plugin-test \
      --mount type=volume,volume-driver=rclone,src=remote_volume,target=/data \
      alpine
  • Step 6 – Review available drivers and plugins: Use the Docker CLI to see installed plugins and supported local drivers.
    docker plugin ls
  • Step 7 – Remove a plugin if no longer needed: Uninstall with:
    docker plugin disable rclone
    docker plugin remove rclone

Volume Cleanup

Over time, unused Docker volumes can accumulate and consume disk space. Regular cleanup helps maintain a tidy container environment and prevent unnecessary storage usage.

  • Step 1 – List all volumes: This shows both used and unused volumes currently present on the host.
    docker volume ls
  • Step 2 – Identify dangling volumes: Dangling volumes are not referenced by any container. Use the filter option to locate them.
    docker volume ls -f dangling=true
  • Step 3 – Remove a specific volume manually: You can remove a named volume that is no longer in use by any container.
    docker volume rm my_old_volume
  • Step 4 – Run a one-command cleanup for unused volumes: This safely removes all dangling volumes (not actively used by any container).
    docker volume prune
    You will be prompted to confirm the action before the cleanup proceeds.
  • Step 5 – Perform a full system prune (optional): This command removes unused containers, networks, images, and volumes in one sweep.
    docker system prune --volumes
    Use with caution, especially in production environments.
  • Step 6 – Automate volume cleanup in CI or scripts: Add volume pruning to maintenance tasks or CI workflows to keep disk usage minimal.
    docker volume prune -f

Best Practices

Following volume best practices helps ensure data persistence, simplifies container management, and avoids common pitfalls in production and development environments.

  • Step 1 – Use named volumes instead of anonymous volumes: Named volumes are easier to track, manage, and reuse across containers.
    docker run -v data_volume:/app/data my_app
  • Step 2 – Avoid writing to the container’s writable layer: Always write persistent data to a mounted volume instead of relying on internal file system changes.
  • Step 3 – Mount data volumes to consistent paths: Standardizing mount paths within containers improves maintainability and consistency across teams.
  • Step 4 – Use read-only mounts for shared config: When sharing config files or secrets between containers, use read-only mounts to enforce safety.
    docker run -v config_volume:/app/config:ro my_app
  • Step 5 – Backup volumes regularly: Schedule backups using simple archive-based workflows or integrate them into external storage solutions.
  • Step 6 – Include volume cleanup in automation: Embed volume pruning into routine scripts or CI/CD jobs to prevent buildup of unused data.
    docker volume prune -f
  • Step 7 – Document volume usage in projects: Make volume dependencies clear in your documentation or README files to ensure smooth onboarding and deployments.

Conclusion

Throughout this blog post, we have explored the essential aspects of Docker volumes, understanding how they provide persistent and portable storage solutions that outlast container lifecycles. We examined commands to create, inspect, and manage volumes effectively, and saw how to declare volumes within Dockerfiles to enforce data persistence. We dived into Docker Compose configurations to streamline volume usage across multi-container applications, and discovered how to preload volumes with data and share them between containers for seamless collaboration.

We also covered practical backup and restore workflows to safeguard your data, explored various storage drivers and plugins that extend Docker’s capabilities to diverse environments, and learned about maintaining a healthy system through volume cleanup. Finally, practical advice was shared on best practices to ensure reliable, secure, and maintainable volume management in your projects.

Whether you’re developing simple applications or orchestrating complex containerized systems, mastering volume management is essential for data integrity and operational success. Happy containerizing, and may your storage always be persistent and your deployments smooth!