Back to Blog

Docker Volumes: How They Work and How to Use One

Greg Lazarus

September 28, 2026 • 10 min read

Docker Volumes: How They Work and How to Use One

Docker volumes are what you end up with when a container gets removed or rebuilt. Everything written to that container's filesystem should vanish along with it, yet the important data actually stays put, sitting in a volume rather than in the container itself.

Most developers encounter Docker volumes for the first time through a database container, then want to understand how a volume differs from a bind mount, how to back one up, or how to move one to another server entirely.

This guide covers what a Docker volume actually is and how it compares to a bind mount, then goes into creating and managing one. From there, it walks through the three tasks that trip people up most: backing up a volume's data, moving a volume between hosts, and mounting a volume to real persistent storage.

What is a Docker volume?

A Docker volume is a persistent storage mechanism managed by Docker that stores data generated by and used by containers, independent of any single container's lifecycle. It's directly related to how Docker containerization works: a container's own filesystem, the writable layer, gets deleted the instant that container is removed. Anything that needs to survive a restart, a rebuild, or a full container removal has to live somewhere else – that somewhere is in a Docker volume.

When you reference a volume that doesn't already exist, Docker automatically creates it for you before the container starts. From that point on, the volume's contents exist outside the container's file system entirely, in a location on the host that Docker itself manages rather than one you pick by hand.

This separation means that volumes are useful for anything stateful: a database, an upload directory, a session store, or a cache that needs to survive a deploy.

The container can be stopped, replaced with a new image, or scaled to a different host, and the data written to its volume stays intact throughout, as the volume's lifecycle was never tied to the container's in the first place.

Writing to volumes is also a better option than writing to a container's writable layer directly.

A storage driver has to manage that writable layer through a union filesystem, which adds a layer of abstraction that a volume skips entirely by writing straight to the host filesystem. For applications with real I/O demands, that difference in write performance adds up.

Docker volumes vs. bind mounts

Docker gives you two main ways to get data in and out of a container: volumes and bind mounts. They solve a similar problem in different ways.

A Docker volume is a location on the host filesystem that Docker creates and manages entirely. You don't pick the path yourself, and non-Docker processes shouldn't touch it directly.

A bind mount maps a directory or file that already exists on the host straight into the container, at whatever path you specify.

Here is a comparison table of the two:

Docker volumesBind mounts
Storage locationManaged by Docker, in a dedicated area of the host filesystemAny path you choose on the host
ManagementDocker CLI commands or the Docker Engine APIManual, tied to the host's directory structure
PortabilityEasy to back up, migrate, and share among multiple containersTied to the specific host machine and its layout
Typical use casePersistent app data, databases, production workloadsLocal development, syncing source code into a container in real time

Bind mounts are useful during development, when source code sitting on the host needs to appear inside a container immediately, without a rebuild.

Volumes are a better choice when working with persistent data in production containerized applications, since they don't depend on a specific host directory structure existing on every machine you deploy to, and they are safer to share among multiple containers at once.

Named volumes vs. anonymous volumes

There are further distinctions to be made, as a Docker volume can be either named or anonymous.

Named volumes

As the user, you define the name of a named volume at creation, either explicitly through the docker volume create command or implicitly through a -v flag that references a name Docker hasn't seen before.

From then on, you can reference that same volume name across multiple containers, migrations, and Compose files without needing to track down an ID.

Anonymous volumes

An anonymous volume is created without a name. Docker assigns it a long, random ID instead, which makes it far harder to reference intentionally later.

Anonymous volumes tend to show up when you mount a container path without specifying a source, or when an image's Dockerfile declares a VOLUME instruction that a docker run command doesn't override.

Both named and anonymous volumes persist after the container using them is removed, with one exception: if the container was started with the --rm flag, Docker destroys its anonymous volumes along with it. Named volumes are never removed this way automatically.

For anything that is deliberately set to persist, a named volume with a clear naming convention should be created at the start. Anonymous volumes are fine for short-lived or throwaway state, but left unmanaged, they tend to pile up as unused volumes that nobody remembers creating.

Managing Docker volumes

Here is a handful of docker volume commands that cover almost everything you'll need day to day:

  • docker volume create [name] creates a named volume explicitly, before any container references it.
  • docker volume ls lists every volume Docker currently knows about, named and anonymous.
  • docker volume inspect [name] returns a volume's mount point, driver, and any labels attached to it.
  • docker volume rm [name] removes a specific volume, and fails if a container is currently using it.
  • docker volume prune removes every unused volume not referenced by any container, which is a useful routine cleanup step.

Here's a runnable example that creates a named volume, mounts it into a container, and confirms the data lands where it should:

docker volume create my_data

docker run -d --name my_app -v my_data:/app/data alpine tail -f /dev/null

docker exec my_app sh -c 'echo "hello" > /app/data/test.txt'

docker volume inspect my_data

The Mountpoint field in that inspect output shows exactly where on the host filesystem the volume's data is stored, confirming it's sitting in Docker's managed storage rather than the container's writable layer.

Docker offers two flags for mounting a volume: -v (or --volume) and --mount. The -v flag packs everything into one colon-separated string, which is quick to write but easy to misread. --mount spells out each option as its own key-value pair, and is required if you need to specify volume driver options or mount a volume subdirectory.

Both flags produce the same result for a straightforward mount, so which one you opt for is mostly a matter of preference until you need --mount's extra options.

Using Docker volumes in Docker Compose

Docker Compose defines volumes the same way it defines services: declaratively, in the compose.yaml file itself. A top-level volumes key declares named volumes, and a volumes key inside a service definition mounts one into that service.

services:

  db:

    image: postgres:16

    volumes:

      - db_data:/var/lib/postgresql/data

    environment:

      POSTGRES_PASSWORD: examplepassword

volumes:

  db_data:

Running docker compose up creates the db_data volume automatically if it doesn't exist yet, and reuses the same volume on every subsequent run – keep that in mind when you go looking for a missing docker volume create step, as compose handles it for you.

docker compose down does not remove named volumes by default. Volume removal is a separate, deliberate action, run with docker compose down --volumes when you actually mean to delete the data, not just stop and remove the containers.

Mounting the same volume name into more than one service in the same file is also a straightforward way to share data between them, which is useful for a backup service that needs read access to another service's data directory.

The best way to back up data using Docker volumes

Backing up a Docker volume involves getting its data into an archive that lives independently of both the volume and the container using it. The standard approach uses a short-lived container to do the archiving:

docker run --rm \

  --volumes-from my_app \

  -v $(pwd)/backup:/backup \

  alpine \

  tar czf /backup/my_data-backup.tar.gz -C /app/data .

This code starts a temporary container that mounts the same volumes as my_app via --volumes-from, along with a local backup directory on the host, and runs tar to compress the volume's contents into that directory.

Once the command finishes, my_data-backup.tar.gz sits on the host, independent of both the container and the volume.

Restoring reverses the process. Create or reuse a target volume, then extract the archive back into it through the same kind of short-lived container:

docker run --rm \

  -v my_data:/app/data \

  -v $(pwd)/backup:/backup \

  alpine \

  sh -c "cd /app/data && tar xzf /backup/my_data-backup.tar.gz"

A backup that's never been restored is, essentially, guesswork. Periodically restore a backup file into a throwaway volume and check that the data actually looks right if you want to catch a broken backup process before it costs you something.

This manual, tar-based approach is integral to backing up a volume's raw data. It's a different job from backing up the database running inside a container: a platform's dedicated database backup features back up the database itself, on a schedule, which is better when the data in question is a Postgres, MySQL, or similar database rather than an arbitrary volume.

How to move Docker volumes between different hosts

To move a volume to a new host, whether that's a fresh remote server or a replacement machine, you can follow the backup process above with an extra transfer step in the middle.

Start by backing up the volume on the source host, exactly as covered above. Transfer the resulting archive to the destination host using scp or an equivalent tool:

scp backup/my_data-backup.tar.gz user@new-host:/tmp/

On the destination host, create a new named volume to receive the data:

docker volume create my_data

Then restore the archive into that volume using the same short-lived-container approach, this time running against the new host's Docker daemon.

Two things you should keep in mind:

  1. First, the volume name on the destination doesn't have to match the source volume's name, but keeping it consistent saves confusion later, especially if the same Compose file or app configuration references it by name on both hosts.
  2. Second, a smooth restore assumes a compatible Docker version and storage driver on the destination host. Mismatched storage drivers between old and new hosts are a rare but real source of restore failures you'll want to rule out before you rely on this for anything critical.

This same approach is applicable when you move an application's data volumes to a new server during a Dokploy deployment migration, not just for a manual container.

How to mount a Docker volume to persistent storage

The docker run -v and Compose examples cover how you mount a volume manually: specify a volume name and container path, and Docker handles where the underlying persistent storage actually lives on the host.

Volume drivers extend this further. A driver lets a volume's data live on external storage systems, like NFS or cloud block storage, instead of the local disk, which is great for setups where an entire host might get replaced, and the data needs to survive.

The deployment platform affects where the manual work happens rather than what's actually going on underneath.

Deploying an application through a platform like Dokploy still requires you to configure a named volume for a service, either through a mount setting in the dashboard or a volumes entry in a Docker Compose file, rather than typing out a docker run -v flag by hand.

Once that named volume exists, Dokploy can schedule automated backups of it to an S3 destination directly, on a cron schedule, without a manual tar and scp process. 

The moment something needs a custom mount path, a specific volume driver, or a manual restore, you will find yourself benefiting from that underlying knowledge.

Common Docker volume pitfalls

Here are a few common mistakes you should avoid:

  • Don't edit files under the host's Docker-managed volume directory by hand. That location is managed by Docker's storage driver, and touching it outside Docker CLI commands or the Docker Engine API risks data corruption or Docker being left with an inconsistent view of a volume's state.
  • Watch for permission mismatches. A container running as a non-root user can hit permission denied errors against a volume with files that were written by a different user ID. This issue often shows up right after you restore a backup that was created by a different container.
  • Remember that removing a Compose stack doesn't remove its volumes. docker compose down leaves named volumes in place by default. That's usually the behavior you want, but it also means unused volumes can pile up unless you clean them up with docker volume prune.
  • Don't rely on an anonymous volume sticking around. Since anonymous volumes are hard to reference intentionally once created, any data you want to persist belongs in a named volume instead.
  • Watch out for bind-mounting files straight from your app's repository on platforms that re-clone on deploy. Some platforms, Dokploy included, wipe and re-clone the repository directory on every deployment, so a bind mount pointing at a path inside that repository can go missing or empty on the next release. A dedicated file mount or a named volume helps you avoid the problem, as neither depends on the repository directory still existing after a redeploy.

Conclusion

A Docker volume is a simple idea: storage that outlives the container using it. To use volumes well, build up a handful of habits: understanding when to use a named volume instead of an anonymous one, backing up data properly and actually testing the restore, and knowing how to move a volume when a host changes.

If you'd rather not script backup and restore commands by hand for every named volume you manage, you can sign up for Dokploy and configure scheduled volume backups to S3 directly from the dashboard. As a result, you can spend more time on the application itself and less on the storage underneath it.

Docker volume FAQs

Can multiple containers use the same Docker volume at once?

Yes. A volume can be mounted read-write or read-only into more than one container simultaneously – it is a common way for containers to share data with each other.

What happens to a Docker volume when its container is deleted?

A named volume persists after the container using it is removed. An anonymous volume is only destroyed automatically if the container was started with the --rm flag.

Do Docker volumes work the same way on Windows containers?

Volumes work on both Linux and Windows containers, though the host-side storage location and some driver options differ between the two platforms.