Docker Volumes vs Bind Mounts: Test What Survives Container Removal
Compare a container's writable layer, a named volume, and a bind mount with a reproducible deletion experiment, real screenshots, and precise cleanup steps.

You save a file inside a Docker container, remove the container, and create another one from the same image. The file is gone. Elsewhere, another container is removed and its files remain. The difference is where the data was written.
A named volume is storage managed by Docker. A bind mount exposes a chosen path from the Docker host. Both can outlive a container. Files written only to that container’s writable layer disappear when the container is removed. Stopping a container is a different operation: its writable layer remains available when you start that same container again.
We will write the same note in three locations, remove the containers, and inspect the replacements. The goal is to predict the result before reaching for another -v option.
The three locations to distinguish
| Storage location | Who selects the underlying location? | Survives stop/start of the same container? | Survives removal of that container? |
|---|---|---|---|
| Container writable layer | Docker, as part of the container | Yes | No |
| Named volume | Docker; you refer to the volume by name | Yes | Yes, unless the volume is separately removed |
| Bind mount | You specify a host path | Yes | Yes, unless the host files are separately removed |
These are lifecycle statements, not backup guarantees. Deleting a mounted file deletes the underlying file; recreating the container does not undo that operation. Losing the Docker host’s storage can also lose a local volume.
Docker’s storage overview separates the writable container layer from mounted storage. Its volume documentation describes storage managed independently of an individual container.
Prepare an isolated directory and volume
Use a Linux terminal with a working Docker Engine. Run docker version and confirm that both Client and Server information are available. These commands were verified in a LabEx Ubuntu 22.04 instance VM with Docker Engine 20.10.21 on September 7, 2026.
The example uses alpine:3.23, which includes the small shell utilities needed here. The tag can receive updates. All container and volume names below are dedicated to this experiment; choose unused names if you have already run it.
mkdir -p docker-storage-demo/bind-data
cd docker-storage-demo
docker pull alpine:3.23
docker volume create storage-demo-data
The current directory contains a host folder called bind-data. The named volume is a separate Docker resource. Do not create a folder named storage-demo-data and assume that it represents the Docker volume.
For the bind mount, $PWD is evaluated by your shell. The resulting absolute path must exist on the Docker daemon’s host. This walkthrough assumes your terminal and daemon use the same Linux machine. With a remote daemon, a path on your laptop is not automatically a path on the remote server. Docker documents that restriction in its bind mount reference.
If you are practicing in a LabEx VM, run all of the commands in that VM terminal. Your laptop’s local directory is not the bind-data directory used here.
Start three containers with different storage
Create the writable-layer example:
docker run -d --name storage-layer alpine:3.23 sleep 3600
Create a container with a named volume at /data:
docker run -d --name storage-volume \
--mount type=volume,src=storage-demo-data,dst=/data \
alpine:3.23 sleep 3600
Create a container with the host folder mounted at the same container path:
docker run -d --name storage-bind \
--mount type=bind,src="$PWD/bind-data",dst=/data \
alpine:3.23 sleep 3600
The sleep process keeps these disposable containers available for an hour. It is a convenient test process, not a suggestion for keeping an application alive when its actual server has failed.
For both mounts, src identifies the storage source and dst identifies the path visible inside the container. The destination is /data in both cases; using the same destination does not make the underlying storage equivalent.
Inspect the mount types:
docker inspect storage-layer storage-volume storage-bind \
--format '{{.Name}}: {{range .Mounts}}{{.Type}} -> {{.Destination}} {{end}}'
The first container has no mount to list. The others report volume -> /data and bind -> /data.
The mount metadata identifies the storage boundary. The host-side note shown underneath belongs to the bind-mounted folder.
Write a note, then test stopping before deleting
Write identical content into each container:
for name in storage-layer storage-volume storage-bind; do
docker exec "$name" sh -c 'mkdir -p /data; printf "saved by the first container\n" > /data/note.txt'
done
The text is intentionally the same. We are testing whether the storage survives, not whether one program formats a file differently.
Read the bind-mounted note from the host:
cat bind-data/note.txt
You should see saved by the first container. No docker cp was needed: the container wrote through the mount into the host folder.
Now stop and restart only the container without a mount:
docker stop storage-layer
docker start storage-layer
docker exec storage-layer cat /data/note.txt
The note still exists. This is the same container and the same writable layer. If a tutorial says “container data is ephemeral,” find out whether it means a stop, a removal, an automatic replacement, or the expiration of an entire practice environment. Those are different events.
Remove the containers and recreate them
The following command removes only the three test containers. It also stops them if they are running:
docker rm -f storage-layer storage-volume storage-bind
Recreate all three using the earlier definitions:
docker run -d --name storage-layer alpine:3.23 sleep 3600
docker run -d --name storage-volume \
--mount type=volume,src=storage-demo-data,dst=/data \
alpine:3.23 sleep 3600
docker run -d --name storage-bind \
--mount type=bind,src="$PWD/bind-data",dst=/data \
alpine:3.23 sleep 3600
The names are reused, but these are new containers. A familiar name does not mean Docker restored the old writable layer.
Read the three notes individually:
docker exec storage-layer cat /data/note.txt
docker exec storage-volume cat /data/note.txt
docker exec storage-bind cat /data/note.txt
The first command fails because the new writable layer has no note. The volume and bind examples print the saved text.
The same image and the same container path produce different results because the storage resources have different lifecycles.
If you put these commands into a script with set -e, handle the first failure explicitly: it is an expected observation, not a failed experiment. In an interactive terminal, run the three commands separately so you can see each result.
When should you choose a named volume?
A named volume is a useful default when an application needs persistent data but you do not need to edit that data as ordinary host files. Your runtime configuration names the volume, and Docker manages its location.
In this experiment, the important relationship is storage-demo-data mounted at /data. The container can be replaced while that relationship is recreated. You can inspect the resource with:
docker volume inspect storage-demo-data
Do not use the displayed internal mountpoint as an invitation to manipulate Docker’s storage directories manually. Prefer a container mounting the volume or the application’s own tools. The storage driver, environment, and operating system can affect how that location is represented.
Also distinguish named from anonymous volumes. This article deliberately names its volume so the replacement command can reference it. Anonymous-volume creation and removal rules are different enough that you should not generalize this example into “Docker never deletes volumes.” Check the lifecycle section of the official volume reference before using automatic removal options.
When does a bind mount make more sense?
A bind mount is useful when a specific host file or folder is part of your workflow: source code you edit, a configuration file you inspect, or a report you need outside the container.
That convenience creates coupling. A different host needs a suitable path and the right files. Host ownership and permissions matter. A read/write mount allows the container to change those files; it is not a copy created for the container.
Use read-only access when the process only needs to read. Test it with a short-lived container:
docker run --rm \
--mount type=bind,src="$PWD/bind-data",dst=/data,readonly \
alpine:3.23 cat /data/note.txt
Reading succeeds. This attempted write should fail:
docker run --rm \
--mount type=bind,src="$PWD/bind-data",dst=/data,readonly \
alpine:3.23 sh -c 'echo changed >> /data/note.txt'
We observed a read-only filesystem error. That limits writes through this mount; it does not stop another process on the host from modifying the source file.
The example uses --mount because its explicit fields help inspection. For bind mounts, it also reports a missing source path instead of silently creating a directory in the common -v case. The bind mount syntax documentation describes the relevant options and exceptions.
Two surprises this experiment does not cover
First, mounting over an existing container directory can obscure files already present at that path. If an application file seems to vanish after adding a bind mount, inspect the source directory before rebuilding the image. The mount may be showing a different set of files at the same path.
Second, a new empty volume mounted over a populated image directory can be initialized with the image’s existing content. Our experiment avoids that complication by starting with a plain /data directory created by the test. When results differ, compare whether the volume was new or reused and whether the destination already contained files.
These behaviors are documented in the volume mounting section and the bind mount reference. They explain why “I mounted the correct path” is not yet a complete diagnosis.
Persistence is not a recovery plan
Both persistent examples survived container removal. Neither experiment produced a backup. An accidental overwrite would affect the persistent file, and a host failure could affect both the volume and bind directory.
A recovery exercise needs a separate copy and a verified restoration. For live databases, use the database’s supported backup procedure and consistency requirements; archiving a changing directory is not automatically a valid database backup.
To practice the storage workflow further, Working with Docker Volumes includes volume creation, sharing, and an archive backup/restore sequence. Docker Volume Mounting is the related course activity to investigate next. Check that you can explain the underlying resource before repeating the commands.
Remove only the experiment’s resources
When you have finished observing the results:
docker rm -f storage-layer storage-volume storage-bind
docker volume rm storage-demo-data
rm -f bind-data/note.txt
rmdir bind-data
Run the host-file commands from the docker-storage-demo directory. The targeted removal makes each destructive step visible: containers first, then the named volume, then the note in the host folder. If you added other files to bind-data, rmdir refuses to remove the nonempty directory.
For the next container you run, identify its data resource before removing anything. Record the container path, mount type, source, and a read command that proves the data is still available after recreation.
References
- Docker storage overview — writable layers and mount types.
- Docker volumes — lifecycle, named volumes, and initialization behavior.
- Docker bind mounts — host paths, permissions, and read-only mounts.
- Docker container inspect — inspect mount configuration.
- LabEx: Working with Docker Volumes — guided storage, sharing, and recovery practice.

