Docker
Docker Volumes

Published: October 6, 2026 · 10 min read

Docker Volumes Explained — Named, Anonymous & Bind Mounts

Here's an experiment that hurts: run MongoDB in a container, insert a document, delete the container — and watch your database vanish. This guide gets Docker volumes explained through that exact pain, straight from our class session on EC2: first the data-loss problem with a real mongo:8.2 container, then the fix with a named volume that survives docker rm, the mystery of anonymous volumes (those hash-named things flooding docker volume ls), and bind mounts for folders you control — each with a clear diagram. The networks lesson covered how containers talk; this one covers how they remember.

In this guide, you will:

  • Watch a MongoDB container lose its data the moment it's removed — and understand exactly why
  • Fix it with a named volume: -v srinu-volume:/data/db, then destroy and resurrect the container with the data intact
  • See where volumes really live: /var/lib/docker/volumes/srinu-volume/_data, WiredTiger files and all
  • Solve the mystery of anonymous volumes — why mongo creates hash-named volumes you never asked for
  • Learn bind mounts and the one-character rule that separates them from named volumes
  • Master the 4 commands: volume ls, create, inspect, rm

Docker volumes explained overview — without a volume the mongodb container's data dies with docker rm, with a named volume the data survives on the host

Why Docker Volumes Exist — The Data Loss Problem

A container's filesystem has a thin writable layer on top of the image — every file your app writes lands there. The catch: that layer belongs to the container, and docker rm deletes them together.

Docker volumes data loss problem diagram — users database inside the mongodb container writable layer is deleted together with the container on docker rm

Here's the experiment exactly as we ran it in class:

terminal — MongoDB with NO volume (data lost)
# THE PROBLEM — watch MongoDB lose everything

docker run -d --name mongodb mongo:8.2    # no -v flag!
docker exec -it mongodb /bin/sh
mongosh

use users
db.users.insertOne({name:"srinu"})
db.users.find()                           # data is there ✓
exit

# destroy the container:
docker stop mongodb
docker rm mongodb

# start a FRESH one and look again:
docker run -d --name mongodb mongo:8.2
docker exec -it mongodb /bin/sh -c mongosh
show dbs                                  # users db... GONE ✗
🚫

This isn't a bug — containers are disposable by design. Deleting one is supposed to leave no trace. The problem is that databases are the opposite: their entire job is remembering. Volumes are how both stay true.

The 3 Ways to Keep Data

Type-v syntaxWho picks the locationVerdict
Named volume-v srinu-volume:/data/dbDocker (/var/lib/docker/volumes/...)✅ the production pattern
Anonymous volume(none — auto-created)Docker, with a random hash name⚠️ data gets orphaned
Bind mount-v /home/ubuntu/mongo-data:/data/dbYou — any host folder✅ dev & direct file access

Named Volumes — The MongoDB Practical

Docker volumes named volume diagram — mongodb container mounts srinu-volume which lives at /var/lib/docker/volumes/srinu-volume/_data on the host and survives container deletion

One flag changes everything — this is the exact command from class:

terminal — run MongoDB with a named volume
# THE FIX — a NAMED volume (the exact command from class)

docker run -d --name mongodb -v srinu-volume:/data/db mongo:8.2
#                            │  │            │
#                            │  │            └── where Mongo writes
#                            │  │                its data INSIDE the
#                            │  │                container
#                            │  └── the volume's NAME (yours to pick;
#                            │      created automatically on first use)
#                            └── -v = Volume: name:container-path

# no volume called srinu-volume yet? → Docker creates it.
# everything Mongo writes now lands OUTSIDE the container.

Now the full destroy-and-resurrect cycle, step by step:

mongosh — insert a document
# put real data in — through mongosh

docker exec -it mongodb /bin/sh
mongosh

use users
db.users.insertOne({name:"srinu"})
db.users.find()                    # ✓ saved
exit

And the part that makes it click — the volume is just a folder on the host. We went and looked:

terminal — inside /var/lib/docker/volumes
# WHERE does the data actually live? On the host — go look:

sudo -i
cd /var/lib/docker/volumes/srinu-volume/_data
ls -l
# → WiredTiger.wt, collection-*.wt, journal/ ... the REAL database files

# every named volume = a real folder:
#   /var/lib/docker/volumes/<volume-name>/_data

Memory hook: the container is a hotel room — stripped and reset for the next guest. The volume is the locker downstairs: your stuff stays until you empty it, no matter how many guests pass through the room.

Anonymous Volumes — The Mystery Hashes

In class, docker volume ls showed something strange: volumes we never created, named like 79a62b0dd3ce69a6d... Where did they come from?

Docker volumes anonymous volume diagram — the mongo image auto-creates hash-named volumes for /data/db and /data/configdb on every run making the data practically lost

terminal — the anonymous volume mystery
# THE MYSTERY — where did these hash-named volumes come from?

docker volume ls
# local     30b8f1cde27e697b664aa5b30dd0fb0ad9...   ← what are these?!
# local     79a62b0dd3ce69a6d239b9655bce9c1fb8...
# local     srinu-volume

# answer: the mongo image declares VOLUME /data/db and
# /data/configdb — so every container WITHOUT -v
# auto-creates hash-named ANONYMOUS volumes.

# the trap: every new run creates FRESH EMPTY ones —
# old data is never reattached. Always give your data a NAME.

Bind Mounts — Your Folder, Your Rules

Docker volumes bind mount diagram — a host folder /home/ubuntu/mongo-data you chose is mounted two-way into the mongodb container at /data/db

terminal — bind mount a host folder
# BIND MOUNT — mount a folder YOU chose, see the files yourself

mkdir /home/ubuntu/mongo-data

docker run -d --name mongodb \
  -v /home/ubuntu/mongo-data:/data/db mongo:8.2
#    │                       └── container path (same as before)
#    └── a REAL PATH on the host instead of a name

ls -l /home/ubuntu/mongo-data
# → the database files, in YOUR folder, visible with plain ls

The entire named-vs-bind decision comes down to one character — compare the two:

terminal — named volume syntax
# NAMED VOLUME — first part is a NAME (no slashes)

docker run -v srinu-volume:/data/db mongo:8.2
#             └── just a name → Docker manages it at
#                 /var/lib/docker/volumes/srinu-volume/_data

# best for: databases and production.

The Docker Volume Commands

Same noun-verb grammar as networks — docker volume <verb>:

terminal — docker volume ls
# docker volume ls — list every volume

docker volume ls

# careful: "docker volume ps" and "docker volumes" don't exist
terminal — docker volume create
# docker volume create — make a volume before using it

docker volume create app-data

# optional — "docker run -v app-data:/data/db" also
# creates it automatically on first use
terminal — docker volume inspect
# docker volume inspect — where does it live?

docker volume inspect srinu-volume

# the line that matters in the output:
# "Mountpoint": "/var/lib/docker/volumes/srinu-volume/_data"
# → the real folder on the host
terminal — docker volume rm
# docker volume rm — delete a volume (and its DATA!)

docker volume rm srinu-volume

# fails with "volume is in use" while a container
# still uses it → docker rm the container first

And the whole lesson as one practiceable block:

terminal — complete session
# COMPLETE SESSION — the whole lesson in one block

docker volume create srinu-volume                      # 1. create
docker volume ls                                       # 2. it's there
docker run -d --name mongodb -v srinu-volume:/data/db mongo:8.2   # 3. attach
docker exec -it mongodb /bin/sh -c mongosh             # 4. insert data
docker volume inspect srinu-volume                     # 5. see the Mountpoint
docker stop mongodb                                    # 6. kill the container
docker rm mongodb
docker run -d --name mongo-db -v srinu-volume:/data/db mongo:8.2  # 7. reborn — data back ✓
docker stop mongo-db && docker rm mongo-db             # 8. cleanup
docker volume rm srinu-volume                          # 9. volume (+ data!) gone

Docker Volumes Quick Reference

I want to...Command
List all volumesdocker volume ls (no volume ps!)
Create a volumedocker volume create app-data
See where it lives + detailsdocker volume inspect app-data
Attach it to a containerdocker run -v app-data:/data/db mongo:8.2
Bind-mount my own folderdocker run -v /home/ubuntu/mongo-data:/data/db mongo:8.2
Delete a volume (and its data)docker volume rm app-data
RememberRule
docker rm containervolume survives
docker volume rmdata is gone for real
name: before the colonnamed volume
/path: before the colonbind mount
hash names in volume lsanonymous volumes (an image's VOLUME instruction)

What's Next

You now own the full data story: why a container's writable layer dies with docker rm, how -v srinu-volume:/data/db moves the data to /var/lib/docker/volumes/srinu-volume/_data where it outlives any container, why mongo's VOLUME instruction floods your machine with anonymous hashes, and when a bind mount's /path: beats a named volume's name:. Full details live in the official volumes documentation (opens in a new tab) and bind mounts guide (opens in a new tab), and the mongo image page (opens in a new tab) documents the /data/db convention we used. Next up, the pieces come together: networks + volumes + multiple containers = real applications.

← Back to Docker Network Types

Continue to Docker Multi-Stage Builds →


Frequently Asked Questions

What is a Docker volume and why do containers lose data without one?

Every file a container writes goes into its writable layer — a thin disk glued to that one container — and docker rm deletes the writable layer together with the container. So a MongoDB container without a volume loses every database the moment it is removed: in our class demo, db.users.insertOne({name:"srinu"}) was there before docker rm and show dbs no longer listed users afterwards. A Docker volume solves this by storing the data outside the container, in a folder managed by Docker on the host (/var/lib/docker/volumes/...), mounted into the container at a path like /data/db. The container becomes replaceable; the volume is the permanent home of the data.

What is the difference between a named volume and a bind mount?

Both put data outside the container; the difference is who picks the location. A named volume (-v srinu-volume:/data/db) is managed by Docker — it lives under /var/lib/docker/volumes/srinu-volume/_data, and Docker handles permissions and location; best for databases and production. A bind mount (-v /home/ubuntu/mongo-data:/data/db) mounts an exact host folder you chose — you can ls the files directly, back them up, or live-sync source code during development. The syntax rule: if the part before the colon is a plain name it's a named volume; if it starts with / or ./ it's a bind mount.

What is an anonymous Docker volume?

An anonymous volume is one Docker creates automatically with a random hash name, like 79a62b0dd3ce69a6d2... It happens when an image declares VOLUME paths in its Dockerfile and you run it without -v: the mongo image declares /data/db and /data/configdb, so every mongo container silently creates up to two anonymous volumes — that is why docker volume ls in class showed a wall of hashes. They are a trap: each new docker run creates fresh empty ones (old data is never reattached), and after a while you cannot tell which hash held what. The data is technically saved but practically lost — always name your volumes.

Where does Docker store volume data on the host machine?

Named and anonymous volumes live under /var/lib/docker/volumes/ on the host — each volume is a folder named after the volume (or its hash), with the actual files inside a _data subfolder. In class we verified it with sudo: /var/lib/docker/volumes/srinu-volume/_data contained MongoDB's real storage files — WiredTiger.wt, collection-*.wt, index-*.wt, journal/. You can also find the exact path for any volume with docker volume inspect, which prints it as the Mountpoint field. Bind mounts are different: they live wherever you pointed them, like /home/ubuntu/mongo-data.

How do I persist MongoDB data in Docker?

Mount a named volume over MongoDB's data directory: docker run -d --name mongodb -v srinu-volume:/data/db mongo:8.2. Mongo writes everything to /data/db inside the container, and the volume redirects those writes to the host. The proof from class: insert a document, docker stop + docker rm the container completely, then start a brand-new container with the same -v srinu-volume:/data/db — show dbs lists users again and db.users.find() returns the same document with the same ObjectId. The container was destroyed and replaced; the data never moved.

How do I create, inspect, and delete a Docker volume?

Four commands, same noun-verb grammar as networks: docker volume ls lists all volumes (anonymous ones show as hashes); docker volume create app-data makes one explicitly (though -v app-data:/path also auto-creates it on first use); docker volume inspect app-data shows the details, most usefully Mountpoint — the real host folder; docker volume rm app-data deletes the volume and its data, and fails with "volume is in use" while a container still uses it, so remove the container first. Watch the spelling: docker volume ps and docker volumes are not commands — both failed live in class.

Does docker rm delete the volume too?

No — and that is the whole point. docker rm deletes the container and its writable layer, but every volume attached to it stays on disk: in class, docker rm mongodb removed the container while docker volume ls still showed srinu-volume, and a new container with the same -v got all the data back. Volumes are only deleted when you ask explicitly: docker volume rm <name> removes one (data included), and docker rm -v <container> also removes the container's anonymous volumes. That independence is what makes containers disposable and data permanent.