Skip to content

Join the Seedly owners community →

Docker

Volumes and Networks

Learn how to persist data with volumes and enable container communication with networks

Written by 13 min read1 activity
Sprout, your presenter

Sprout presents

Containers forget everything when they stop. Volumes preserve your data and networks let containers communicate, two problems solved with admirable tidiness.Containers forget everything when they stop. Volumes preserve your data and networks let containers communicate, two problems solved with admirable tidiness.

Sprout guards a chest of scrolls on a dock while a shipping container is lifted away
Volumes keep your data safe when a container goes away

Your containers can run apps now, which is great. But they've got two pretty big hangups. Any data created inside a container VANISHES when the container gets removed, and out of the box, containers have no easy way to find each other. This lesson fixes both, using volumes (for data) and networks (for talking).

The Problem: Containers Are Ephemeral#

By default, everything inside a container lives in a temporary filesystem. Remove the container and the data goes right along with it.

# Start a PostgreSQL database
docker run -d --name my-db -e POSTGRES_PASSWORD=mysecret postgres:16
 
# Add some data to the database...
# ...
 
# Remove the container
docker rm -f my-db
# ALL YOUR DATA IS GONE!

That's a real problem for databases, file uploads, logs and anything else you actually want to keep.

Docker Volumes: Persistent Storage#

A volume is storage that Docker manages, and it lives outside of any container. It sticks around even after containers are stopped or removed.

Named Volumes#

Named volumes are the go-to way to keep data around.

# Create a named volume
docker volume create my-data
 
# Run a container with the volume attached
docker run -d \
  --name my-db \
  -e POSTGRES_PASSWORD=mysecret \
  -v my-data:/var/lib/postgresql/data \
  postgres:16

The -v my-data:/var/lib/postgresql/data flag hooks the volume my-data up to the PostgreSQL data folder inside the container. Now even if you delete the container and make a new one, your database data survives.

# Remove the container
docker rm -f my-db
 
# Start a new container with the same volume
docker run -d \
  --name my-db-2 \
  -e POSTGRES_PASSWORD=mysecret \
  -v my-data:/var/lib/postgresql/data \
  postgres:16
 
# Your data is still there!

Bind Mounts: Share Files with Your Host#

Bind mounts connect a specific folder on your computer straight into the container. They're awesome for development, because any change you make on your machine shows up inside the container right away.

# Mount the current directory into /app in the container
docker run -d \
  --name dev-app \
  -v $(pwd):/app \
  -p 3000:3000 \
  node:24-alpine \
  sh -c "cd /app && npm start"

Here's what that setup buys you.

  • Edit a file on your laptop and it changes inside the container instantly
  • Your app (with a file watcher like nodemon) reloads on its own
  • You don't have to rebuild the image every time you change some code

Volume Commands#

# List all volumes
docker volume ls
 
# Inspect a volume's details
docker volume inspect my-data
 
# Remove a specific volume
docker volume rm my-data
 
# Remove all unused volumes (be careful!)
docker volume prune

Docker Networks: Container Communication#

Out of the box, containers don't know how to find each other by name. If your web app needs to connect to a database container, you want a network.

The Problem#

# Start a Redis container
docker run -d --name my-redis redis:7
 
# Start your web app
docker run -d --name web-app myapp
 
# web-app tries to connect to "localhost:6379"...
# FAILS! Each container has its own localhost.

Every container gets its own network namespace. localhost inside a container means THAT container and nothing else.

User-Defined Bridge Networks#

The fix is a user-defined bridge network. Containers on the same network can talk to each other using their container names as hostnames.

# Create a network
docker network create my-app-network
 
# Start Redis on the network
docker run -d \
  --name my-redis \
  --network my-app-network \
  redis:7
 
# Start your app on the same network
docker run -d \
  --name web-app \
  --network my-app-network \
  -p 3000:3000 \
  myapp

Now your web app can reach Redis at my-redis:6379, and Docker handles the DNS lookup for you. Zero IP addresses required!

# Inside web-app, this connection works:
# redis://my-redis:6379
 
# Docker resolves "my-redis" to the Redis container's IP automatically

Network Commands#

# List all networks
docker network ls
 
# Create a network
docker network create my-network
 
# Connect an existing container to a network
docker network connect my-network my-container
 
# Disconnect a container from a network
docker network disconnect my-network my-container
 
# Remove a network
docker network rm my-network
 
# Inspect a network (see connected containers)
docker network inspect my-network

Putting It All Together#

Sprout listens to a paper cup telephone strung between two shipping containers
A network lets containers talk to each other by name

Here's a realistic dev setup with a Node.js app, a PostgreSQL database and a Redis cache.

# Create a network for the app
docker network create fullstack
 
# Start PostgreSQL with a named volume for data persistence
docker run -d \
  --name db \
  --network fullstack \
  -v pgdata:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=mysecret \
  postgres:16
 
# Start Redis
docker run -d \
  --name cache \
  --network fullstack \
  redis:7
 
# Start the app with bind mount for live development
docker run -d \
  --name app \
  --network fullstack \
  -v $(pwd):/app \
  -p 3000:3000 \
  -e DATABASE_URL=postgresql://postgres:mysecret@db:5432/postgres \
  -e REDIS_URL=redis://cache:6379 \
  myapp

See how the app connects to db and cache by name? Docker's DNS sorts that out on its own.

Production Volume Strategy#

In production, when you're running several copies of a container, local volumes stop working, because each server has its own storage. For user uploads and shared files, reach for external object storage instead.

# Development: named volumes work great
docker run -v uploads:/app/uploads myapp
 
# Production: use cloud storage (S3, R2, etc.)
# Your app connects to cloud storage via SDK
# All replicas can read and write the same files

TL;DR#

  • Containers are ephemeral, so data disappears when a container gets removed
  • Named volumes keep data around after containers are gone (great for databases)
  • Bind mounts share folders from your machine with containers (great for development)
  • Containers can't find each other by name out of the box, and each one has its own localhost
  • User-defined bridge networks let containers talk to each other by name
  • In production with multiple copies running, use external storage for shared files

What's Next?#

Setting up a bunch of containers with networks and volumes by hand gets old QUICK. Imagine typing all of those commands every single time (no thanks). Next lesson is Docker Compose, a tool that lets you describe your whole multi-container setup in one file and fire it all up with one command...

This lesson ends with a short activity.