Docker Volumes and Networking: A Practical Guide to Persistent Data and Container Communication
Docker becomes much easier once two questions are clear: where does my application data actually live? and how do my containers find each other? Those questions explain most mysterious data-loss incidents, database connection failures, and port-exposure mistakes people run into while self-hosting.
Keep important application state outside the container layer. Use named volumes for data managed by an application and bind mounts for host files you want to edit directly. For communication, put related containers on a user-defined bridge network—or let Docker Compose create one automatically—so services can resolve each other by name. Publish only the ports that truly need outside access.
Image
A reusable blueprint used to start containers.
Container
A running instance of an image. Designed to be replaceable.
Volume
Persistent storage that can survive container replacement.
Bind mount
A host directory mounted directly into a container.
Bridge network
A private virtual network connecting containers on one host.
Published port
A mapping that exposes a container service through the host.
- Why containers are disposable
- The three common places data can live
- Where Docker named volumes live
- Named volumes vs bind mounts
- What survives container deletion
- Docker Compose and volume deletion
- What happens during an image update
- How Docker networking works
- Why container names sometimes fail
- Why Compose usually fixes networking
- Published ports and internal-only services
- When host networking makes sense
- Three rules to remember
- Common Docker volume and networking questions
1. Why containers are disposable by design
A container should not be treated like a traditional server that you carefully modify forever. A better mental model is a replaceable process packaged with the files and dependencies it needs. When you update an application, change its configuration, or deploy a new image, replacing the container is normal.
That design has a major implication: files written only to the container’s writable layer are temporary. Restarting the same container normally keeps those files. Removing that container and creating a new one does not.
docker restart, so it must be persistent.” A restart and a replacement are not the same event.
2. The three common places Docker data can live
Convenient, automatic, and temporary. Remove the container and this layer disappears with it.
Docker manages a persistent directory and mounts it into the container.
You choose a host path and Docker presents that directory inside the container.
# No persistent mount docker run -d --name demo alpine sleep 3600 # Named volume docker run -d --name demo \ -v app_data:/data \ alpine sleep 3600 # Bind mount mkdir -p /srv/demo-data docker run -d --name demo \ -v /srv/demo-data:/data \ alpine sleep 3600
The small syntax difference before the colon matters. app_data:/data refers to a Docker-managed volume. /srv/demo-data:/data refers to a real directory you selected on the host.
3. Where Docker named volumes actually live
Named volumes feel abstract until you inspect one. Docker can tell you exactly where the volume is stored on the host.
docker volume create app_data docker volume inspect app_data
On a typical Linux installation using Docker’s local volume driver, the mount point will usually look similar to:
/var/lib/docker/volumes/app_data/_data
You can prove that the path inside the container and the volume directory on the host represent the same persistent data.
docker run --rm \ -v app_data:/data \ alpine sh -c 'echo persistent-data > /data/hello.txt' sudo cat /var/lib/docker/volumes/app_data/_data/hello.txt
docker inspect CONTAINER_NAME lets you see the mounts attached to a running container, including source, destination, type, and read/write mode.
4. Named volumes vs bind mounts: which should you use?
| Feature | Named Volume | Bind Mount |
|---|---|---|
| Example | app_data:/data | /srv/app:/data |
| Who chooses the host location? | Docker | You |
| Survives container removal? | Yes | Yes |
| Easy to edit directly? | Less convenient | Very convenient |
| Good for databases? | Usually yes | Possible, but less portable |
| Good for config/code files? | Sometimes | Excellent |
| Managed with Docker CLI? | Yes | No |
A practical rule is simple: use named volumes for state the application owns, such as database data, user uploads, queues, or application state. Use bind mounts for files you own and want to manipulate directly, such as an Nginx configuration, application source code during development, TLS certificates, or static site files.
5. What survives docker rm?
Here is a small experiment that makes the difference obvious. We create three containers and write a marker file in three different storage locations.
docker volume create survival_data mkdir -p /srv/survival-bind docker run -d --name layer-demo alpine sleep 3600 docker exec layer-demo sh -c 'echo keep-me > /marker.txt' docker run -d --name volume-demo \ -v survival_data:/data alpine sleep 3600 docker exec volume-demo sh -c 'echo keep-me > /data/marker.txt' docker run -d --name bind-demo \ -v /srv/survival-bind:/data alpine sleep 3600 docker exec bind-demo sh -c 'echo keep-me > /data/marker.txt'
Now remove the containers and create new ones using the same named volume and bind mount. The marker in the writable container layer is gone. The volume and bind-mount markers remain.
| Storage type | After container removal | Why |
|---|---|---|
| Container layer | Deleted | Belongs to the removed container |
| Named volume | Preserved | Exists independently of the container |
| Bind mount | Preserved | Files are in your host directory |
The anonymous-volume trap
If you mount only a container path—for example -v /data—Docker creates a volume without a friendly name. It can outlive the container, but later it may be hard to identify which application owns it. Over time, anonymous volumes can become clutter or, worse, mysterious storage that nobody feels safe deleting.
docker volume ls # Show unused volumes docker volume ls -f dangling=true
For long-lived applications, explicit named volumes make infrastructure easier to understand, migrate, and back up.
6. Docker Compose: down vs down -v
Compose makes persistent storage convenient, but one option deserves special attention. Consider this minimal file:
services:
app:
image: alpine
command: sleep 3600
volumes:
- appdata:/data
volumes:
appdata:
Running docker compose down removes the containers and Compose network but normally preserves the named volume. Running docker compose down -v also removes volumes declared by the project.
docker compose down -v as a casual “clean restart” command on a production application unless deleting its persistent volumes is intentional and you have a verified backup.
7. Does persistent data survive an image update?
Yes—provided the application’s data directory is stored on a persistent mount and the new application version is compatible with that data format.
# Existing container uses a named volume docker run -d --name database \ -e POSTGRES_PASSWORD=example-password \ -v pgdata:/var/lib/postgresql/data \ postgres:16 # Later... docker rm -f database docker pull postgres:16 docker run -d --name database \ -e POSTGRES_PASSWORD=example-password \ -v pgdata:/var/lib/postgresql/data \ postgres:16
The container changed; the named volume did not. That is why containers can safely be treated as replaceable.
8. How Docker container networking works
Picture a Docker bridge network as a private virtual switch inside your server. Containers connected to that network receive private IP addresses and can exchange traffic without exposing those services to the public internet.
Both services can communicate internally without publishing the database port.
Docker provides a default bridge network, but user-defined bridge networks are generally more useful for application stacks because they provide automatic DNS-based name resolution between containers.
docker network create appnet docker run -d --name database \ --network appnet \ alpine sleep 3600 docker run -d --name web \ --network appnet \ alpine sleep 3600
9. Why a container sometimes cannot reach a database by name
One of the most common Docker errors sounds like “host not found,” “database unreachable,” or “connection refused.” Sometimes the application and database are both healthy. The missing piece is simply how the hostname is being resolved.
On a user-defined network, containers can typically resolve one another by container name or network alias. That makes a stable hostname such as database much better than hard-coding a private IP address that may change after recreation.
docker exec web getent hosts database docker exec web ping -c 1 database
localhost refers to that same container. If your database is a separate Compose service named db, the application normally needs to connect to db, not localhost.
10. Why Docker Compose networking usually “just works”
Docker Compose creates a project network automatically unless you configure networking differently. Services in the same Compose project can therefore communicate by service name.
services:
web:
image: nginx:alpine
worker:
image: alpine
command: sleep 3600
After docker compose up -d, the worker service can reach the web server at http://web. There is no need to discover or store the web container’s current IP address.
This behavior becomes especially valuable in real stacks:
DATABASE_HOST=dbREDIS_HOST=redisSEARCH_HOST=opensearchAPI_URL=http://api:3000
11. Publish a port—or keep it internal?
Containers connected to the same network do not need public port mappings to communicate. Port publishing is only needed when something outside that Docker network—such as a browser, reverse proxy on the host, another machine, or the internet—must reach the service.
docker network create stacknet docker run -d --name db \ --network stacknet \ -e POSTGRES_PASSWORD=example-password \ postgres:16 docker run -d --name web \ --network stacknet \ -p 8080:80 \ nginx:alpine
In that example, the web application is reachable through port 8080 on the host. The database remains internal to the Docker network. The web application can still connect to it by the hostname db.
- If the public internet needs the service, publish or proxy it deliberately.
- If only another container needs the service, keep it on the internal network.
- If only administrators need occasional access, consider binding to
127.0.0.1and using an SSH tunnel. - For production web stacks, a reverse proxy is often the only component that needs public web ports.
Docker’s interaction with host firewall systems can be subtle, so do not assume an exposed Docker port is protected exactly the way a normal host process would be. Review Docker’s official packet-filtering and firewall guidance for the version and distribution you run.
docker run -d --name db \ -p 127.0.0.1:5432:5432 \ postgres:16
12. What about --network host?
Host networking removes much of the network isolation that bridge mode provides. Instead of receiving its own bridge-network address and using port mappings, the container shares the host’s network namespace.
docker run -d --name host-nginx \ --network host \ nginx:alpine
This can be useful for specialized software such as monitoring agents, network tools, or applications that need direct access to host interfaces. For ordinary web applications and databases, bridge networking is usually easier to reason about and provides cleaner isolation.
13. The entire guide in three rules
Persist important state
Put databases, uploads, and application state on named volumes or intentionally selected bind mounts. Never rely on a replaceable container’s writable layer for irreplaceable data.
Use service names, not changing IPs
Put related containers on a user-defined network. With Docker Compose, service discovery is already built into the normal project network.
Expose less
Do not publish database, cache, queue, or internal API ports simply because you can. Expose only the services that need outside access.
Recommended Docker references
For production deployments, pair this guide with Docker’s official documentation. That is the best place to confirm syntax and security behavior for your current Docker release.
Docker Volumes Docs Docker Networking DocsFrequently Asked Questions
Where are Docker volumes stored?
For Docker’s local volume driver on a standard Linux installation, named volumes are commonly stored below /var/lib/docker/volumes/. Use docker volume inspect VOLUME_NAME to see the actual mount point on your system.
Does docker rm delete a named volume?
Removing a container does not normally remove an independently created named volume. The volume persists until it is explicitly removed, pruned, or deleted by an orchestrated command such as a Compose operation that includes volume deletion.
What does docker compose down -v do?
It tears down the Compose project and removes the project’s named volumes as well. That can permanently delete application data stored in those volumes.
Why can’t one Docker container resolve another by name?
Verify that both containers share a user-defined network. Name-based service discovery is one of the main advantages of user-defined bridge networks and normal Docker Compose project networks.
Should I publish PostgreSQL port 5432 to the internet?
Usually no. If an application container is the only client, keep PostgreSQL private on the Docker network. If administrative access is needed, a localhost binding plus SSH tunnel is often safer than a public database port.
What is an anonymous Docker volume?
It is a Docker-managed volume without a meaningful user-selected name. Anonymous volumes can survive the container that created them, which is why long-lived stacks are usually easier to maintain with explicitly named volumes.
Are named volumes better than bind mounts?
Neither is universally better. Named volumes are typically convenient for application-owned persistent state. Bind mounts are excellent when you want a specific host file or directory to remain visible and editable outside Docker.
Is localhost the Docker host?
Inside a container, localhost normally means that container itself. A separate database container should usually be addressed by its service or container name on the shared Docker network.
Final checklist before you deploy
- Identify every directory containing data you cannot afford to lose.
- Move application state to named volumes or intentional bind mounts.
- Test your backup and restore process before you need it.
- Use a user-defined network or Docker Compose for multi-container applications.
- Use service names rather than container IP addresses.
- Keep databases and other private services off public ports.
- Review major-version upgrade instructions for stateful services.
Reference: This tutorial was independently written using Docker’s documented storage/networking behavior and inspired by the topic coverage of LearnWithHasan’s Docker volumes and networking guide. Always verify commands against the current official Docker documentation before using them on production systems.