Skip to main content
Docker Fundamentals·Lesson 4 of 5

Volumes & Networks

Containers are ephemeral by default — when you remove a container, its data is gone. Volumes solve this by providing persistent storage. Networks let containers communicate with each other securely.

Why Volumes?

A container's filesystem is a writable layer on top of the image. This layer is:

  • Temporary — deleted when the container is removed
  • Tied to the container — not easily shared between containers
  • Slower — the writable layer uses a storage driver with overhead

Volumes solve all three problems.

Types of Storage in Docker

TypeDescriptionUse Case
VolumesManaged by Docker, stored in Docker's directoryDatabases, persistent app data
Bind mountsMap a host directory into the containerDevelopment (live code reload)
tmpfs mountsStored in memory onlySensitive data, temp files

Docker Volumes

# Create a named volume
docker volume create my-data

# List volumes
docker volume ls

# Inspect a volume
docker volume inspect my-data

# Remove a volume
docker volume rm my-data

# Remove all unused volumes
docker volume prune

Using Volumes with Containers

# Mount a named volume
docker run -d \
  --name db \
  -v pgdata:/var/lib/postgresql/data \
  postgres:16

# The volume persists even after the container is removed
docker stop db
docker rm db

# Start a new container with the same volume  data is still there
docker run -d \
  --name db-new \
  -v pgdata:/var/lib/postgresql/data \
  postgres:16

Bind Mounts for Development

Bind mounts map a directory on your host machine directly into the container. Changes on either side are reflected immediately:

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

This is essential for development — you edit code on your host, and the container picks up changes instantly.

# Read-only bind mount (container cannot modify host files)
docker run -d \
  -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \
  nginx

Bind Mount vs Volume Syntax

Docker supports two syntaxes for mounts:

# Short syntax (-v)
docker run -v myvolume:/app/data my-app
docker run -v $(pwd):/app my-app

# Long syntax (--mount)  more explicit
docker run --mount type=volume,source=myvolume,target=/app/data my-app
docker run --mount type=bind,source=$(pwd),target=/app my-app

The --mount syntax is more verbose but clearer, especially for complex configurations:

docker run --mount \
  type=volume,\
  source=mydata,\
  target=/app/data,\
  readonly \
  my-app

Practical Volume Patterns

Database persistence:

docker run -d \
  --name postgres \
  -e POSTGRES_PASSWORD=secret \
  -v pgdata:/var/lib/postgresql/data \
  -p 5432:5432 \
  postgres:16

Sharing data between containers:

# Container 1 writes logs
docker run -d --name writer -v shared-logs:/logs alpine \
  sh -c "while true; do date >> /logs/app.log; sleep 5; done"

# Container 2 reads logs
docker run --rm -v shared-logs:/logs alpine tail -f /logs/app.log

Docker Networks

By default, Docker creates three networks:

docker network ls
# NETWORK ID   NAME      DRIVER    SCOPE
# abc123       bridge    bridge    local
# def456       host      host      local
# ghi789       none      null      local
NetworkDescription
bridgeDefault network for containers. Isolated from host.
hostContainer shares the host's network stack
noneNo networking

Creating Custom Networks

Custom bridge networks provide automatic DNS resolution — containers can find each other by name:

# Create a custom network
docker network create my-network

# Run containers on the same network
docker run -d --name web --network my-network nginx
docker run -d --name api --network my-network node-api

# Containers can reach each other by name
docker exec web curl http://api:3000/health

On the default bridge network, containers can only communicate by IP address. On custom networks, they use container names as hostnames.

Network Commands

# Create a network
docker network create app-net

# List networks
docker network ls

# Inspect a network
docker network inspect app-net

# Connect a running container to a network
docker network connect app-net my-container

# Disconnect a container from a network
docker network disconnect app-net my-container

# Remove a network
docker network rm app-net

# Remove all unused networks
docker network prune

Multi-Container Application Example

Here is a complete example running a web app with a database and cache:

# Create a network
docker network create webapp-net

# Start PostgreSQL
docker run -d \
  --name db \
  --network webapp-net \
  -e POSTGRES_USER=app \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_DB=myapp \
  -v pgdata:/var/lib/postgresql/data \
  postgres:16

# Start Redis
docker run -d \
  --name cache \
  --network webapp-net \
  redis:7-alpine

# Start the application
docker run -d \
  --name app \
  --network webapp-net \
  -e DATABASE_URL=postgres://app:secret@db:5432/myapp \
  -e REDIS_URL=redis://cache:6379 \
  -p 3000:3000 \
  my-web-app

The app container reaches the database at db:5432 and Redis at cache:6379 using container names as hostnames.

Network Isolation

You can create separate networks for security. Containers on different networks cannot communicate:

# Frontend network
docker network create frontend

# Backend network
docker network create backend

# Nginx only on frontend
docker run -d --name nginx --network frontend -p 80:80 nginx

# API on both networks (bridges frontend and backend)
docker run -d --name api --network frontend my-api
docker network connect backend api

# Database only on backend (not accessible from frontend)
docker run -d --name db --network backend postgres:16

This way, the database is only reachable from the API, not from the Nginx container.

tmpfs Mounts

For sensitive or temporary data that should never be written to disk:

docker run -d \
  --name secure-app \
  --tmpfs /app/tmp:size=100m \
  my-app

Inspecting Container Networking

# Find a container's IP address
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my-container

# See which ports are mapped
docker port my-container

# Test connectivity between containers
docker exec web ping -c 3 api
docker exec web curl http://api:3000

Summary

Volumes provide persistent storage for containers — use named volumes for databases and bind mounts for development. Custom networks enable containers to discover each other by name and provide network isolation for security. In the next lesson, you will learn Docker Compose, which simplifies managing multi-container applications.