Docker Networks and Volumes Explained

In the previous post, you saw that a Node.js app could connect to PostgreSQL using @db:5432 instead of @localhost:5432. That works because of Docker networking. You also saw that database data survives container restarts because of volumes.
This post explains both — how containers communicate, and how data persists.
Part 1: Docker Networks
What is a Docker Network?
Docker creates a virtual network so containers can talk to each other. By default, containers are isolated — they can't reach each other unless they're on the same network.
When you use Docker Compose, all services are automatically placed on a shared default network. That's why services can reference each other by name — no IP addresses needed.
# List all networks
docker network ls
# Inspect a specific network
docker network inspect bridgeNetwork Drivers
Docker supports several network drivers for different use cases:
| Driver | Description |
|---|---|
bridge | Default — isolated virtual network on a single host |
host | Container shares the host's network directly, no isolation |
none | Container has no network access |
overlay | Connects containers across multiple Docker hosts (Docker Swarm) |
For most local development, bridge is what you'll use. Docker Compose uses it automatically.
How Containers Talk to Each Other
When containers are on the same network, they connect using the container or service name as the hostname. Docker resolves the name to an internal IP automatically — you never need to know the actual IP.
# docker-compose.yml
services:
app:
build: .
environment:
- DATABASE_URL=postgres://admin:secret@db:5432/mydb # "db" is the service name
depends_on:
- db
db:
image: postgres:16-alpineThe app service connects to db using hostname db. Docker handles the DNS resolution internally.
Creating Networks Manually
With plain docker run (without Compose), you manage networks yourself:
# Create a custom network
docker network create my-network
# Run containers on the same network
docker run --network my-network --name app my-app
docker run --network my-network --name db postgres:16
# Connect a running container to a network
docker network connect my-network my-containerCustom Networks in Docker Compose
For more control, you can define explicit networks in Compose and assign services to specific ones. This lets you isolate services from each other:
services:
app:
build: .
networks:
- frontend
- backend
db:
image: postgres:16-alpine
networks:
- backend # db is only on the backend network
networks:
frontend:
backend:Here, db is on backend only. If you add a public-facing proxy service on frontend only, it can't reach db directly — a useful security boundary.
Part 2: Docker Volumes
Why Volumes Exist
By default, data inside a container is lost when the container is removed. The container filesystem is ephemeral — it exists only as long as the container does.
Volumes solve this. A volume stores data outside the container's lifecycle, managed by Docker. Even if you delete and recreate a container, the volume (and its data) remains.
Volumes are used for:
- Database data (PostgreSQL, MySQL, MongoDB)
- Uploaded files
- Application logs
- Any data you can't afford to lose
# List all volumes
docker volume ls
# Inspect a volume
docker volume inspect my-volume
# Remove a volume
docker volume rm my-volume
# Remove all unused volumes
docker volume pruneNamed Volumes
Docker manages the storage location — you just give it a name. Ideal for persistent data like databases where you don't care about the exact path on the host.
services:
db:
image: postgres:16-alpine
volumes:
- db_data:/var/lib/postgresql/data # named volume
volumes:
db_data: # declare here so Docker creates and manages itData in db_data survives docker compose down. Only docker compose down -v deletes it.
Bind Mounts
A bind mount links a specific directory or file from your host machine into the container. Changes on the host are immediately visible inside the container — no rebuild needed.
This is the standard pattern for development hot-reload:
services:
app:
build: .
volumes:
- .:/app # Sync current directory into /app
- /app/node_modules # Keep container's node_modules intactThe second line — /app/node_modules — is a common trick. Without it, the bind mount of . would overwrite the container's node_modules with your host's (which might not exist or differ). This anonymous volume ensures the container keeps its own node_modules.
With docker run:
docker run -v $(pwd):/app my-appNamed Volume vs Bind Mount
| Named Volume | Bind Mount | |
|---|---|---|
| Managed by | Docker | You |
| Host path | Docker decides | You specify |
| Use case | Database, persistent data | Development, code sync |
| Performance | Good | Good (macOS can be slower) |
Putting It Together
Here's a complete Compose file using both custom networks and volumes:
services:
app:
build: .
ports:
- "3000:3000"
volumes:
- .:/app # Bind mount for hot-reload
- /app/node_modules # Preserve container's node_modules
networks:
- app-network
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: secret
POSTGRES_DB: mydb
volumes:
- db_data:/var/lib/postgresql/data # Persist database data
networks:
- app-network
volumes:
db_data:
networks:
app-network:docker compose up -d
# Stop containers — data in db_data volume is preserved
docker compose down
# Stop containers AND delete volumes — data is gone
docker compose down -vSummary
✅ Docker networks let containers communicate — isolated by default, connected when on the same network
✅ Docker Compose automatically puts all services on one network — connect via service name, not IP
✅ Use custom networks in Compose to isolate services that shouldn't talk to each other
✅ Container data is ephemeral — volumes keep data alive beyond container lifecycle
✅ Named volumes are Docker-managed, ideal for databases and persistent data
✅ Bind mounts sync host directories into containers — perfect for development hot-reload
✅ docker compose down removes containers; docker compose down -v also removes volumes
Series: Docker & Kubernetes Learning Roadmap
Previous: Docker Compose Basics
Next: Docker Networking & Volumes Deep Dive
Ready to go deeper? The next post covers Docker networking internals, overlay networks, production storage patterns, and more.
📬 Subscribe to Newsletter
Get the latest blog posts delivered to your inbox every week. No spam, unsubscribe anytime.
We respect your privacy. Unsubscribe at any time.
💬 Comments
Sign in to leave a comment
We'll never post without your permission.