Lecture 3 of 1418 minInteractiveFree lecture

Running containers: ports, networks and volumes

A container on its own is sealed off. Learn how to publish ports to users, connect containers by name on a private network, keep data safe in volumes, and describe a whole multi-container app with Docker Compose.

In this lecture you will

  • Publish a container port to the host and explain port mapping
  • Connect containers on a user-defined network and reach them by name
  • Persist data with volumes and explain why the container file system is ephemeral
  • Describe a multi-container application with Docker Compose

An image is a sealed box. To be useful, a running container must receive traffic, talk to other services and store data that outlives it. Docker gives each of these a simple mechanism, and the same ideas reappear — at larger scale — in Kubernetes.

Publishing ports

Every container gets its own network namespace, so a server listening on port 80 inside a container isn’t reachable from outside by default. Publishing a port creates a forwarding rule from the host:

docker run -d --name web -p 8080:80 shop-web:1.4.2
#                          host:container

Now http://localhost:8080 on the host (or the host’s IP from elsewhere) reaches port 80 in the container. Only publish what should be reachable — a database, for instance, usually should not be.

Who can actually connect depends on two settings: how the port is published, and which address the app listens on inside the container. Try the combinations:

InteractiveWho can reach the container?Change the publishing and the listen address
Loading visual…

The most common surprise: an app that listens on 127.0.0.1 inside its container can’t be reached through any published port, because that loopback belongs to the container alone. Servers in containers should listen on 0.0.0.0.

Networks: containers talking to containers

Containers attached to the same user-defined network can reach each other directly, using the container name as a hostname. Docker’s built-in DNS resolves it to the container’s current IP address, which changes every time the container is recreated.

docker network create shop-net
docker run -d --name db  --network shop-net -v pgdata:/var/lib/postgresql/data postgres:16
docker run -d --name web --network shop-net -p 8080:80 \
  -e DATABASE_URL=postgres://db:5432/shop shop-web:1.4.2

Step through the setup below: traffic from a browser to the web container, a private network, name-based discovery, and a volume.

InteractivePorts, networks and volumes
Loading visual…
  1. Publish a portThe browser connects to port 8080 on the host. Docker forwards the traffic to port 80 inside the web container.
  2. Create a private networkBoth containers join a user-defined network. Containers on it can talk to each other, but nothing from outside can reach the database unless you publish its port.
  3. Find services by nameThe web container connects to db:5432. Docker’s DNS resolves the name db to the database container — no IP addresses in configuration.
  4. Store data in a volumeThe database writes its files to a named volume, pgdata, managed by Docker outside the container’s own file system.
  5. Containers are disposable, data is notDelete and recreate the database container — to upgrade it, say — and reattach the same volume. The data is still there.

Volumes: data that survives

A container’s file system is ephemeral: writes go to a thin writable layer that is deleted with the container. That’s perfect for stateless services and fatal for databases. The options:

Named volume
Storage managed by Docker (-v pgdata:/var/lib/postgresql/data). The usual choice for databases and persistent app data.
Bind mount
A host directory mounted into the container (-v ./src:/app). Handy in development for live code reloading.
tmpfs
In-memory storage that disappears when the container stops — for scratch files and sensitive temporary data.

Describing the whole app with Compose

Typing long docker run commands doesn’t scale. Docker Compose describes services, networks and volumes in one file:

services:
  web:
    image: shop-web:1.4.2
    ports:
      - "8080:80"
    environment:
      DATABASE_URL: postgres://db:5432/shop
    depends_on:
      - db
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    volumes:
      - pgdata:/var/lib/postgresql/data
volumes:
  pgdata:
docker compose up -d      # create the network, volume and both containers
docker compose logs -f web
docker compose down       # stop and remove containers (volumes are kept)

Compose creates a private network for the project automatically, so web reaches db by name.

Run the lifecycle yourself and watch which objects survive each command:

Interactiveup, down and down -vStart the app, add data, then take it down
Loading visual…

Compose names everything after the project (here, the directory name shop). down keeps named volumes, so data survives upgrades and restarts; down -v removes them. Output is abridged.

Compose is excellent on a single machine. When you need many machines, automatic recovery and zero-downtime releases, you need an orchestrator. Next: Kubernetes.

Key takeaways

  • -p 8080:80 forwards traffic from port 8080 on the host to port 80 inside the container.
  • Containers on the same user-defined network can reach each other by container name through built-in DNS.
  • A container’s own file system disappears with it; data that must survive belongs in a volume.
  • Docker Compose describes services, networks and volumes in one file, so the whole stack starts with one command.

Check your understanding

3 questions · answers explained

Q1A container runs a web server on port 80, started with -p 8080:80. Which URL reaches it from the host’s browser?

The mapping is host-port:container-port. Traffic to port 8080 on the host is forwarded to port 80 inside the container.

Q2How should a web container find a database container on the same user-defined network?

User-defined networks include DNS, so the name db resolves to whatever address the database container currently has. IP addresses change when containers are recreated; names don’t.

Q3A database container is deleted and recreated from the same image. Where did its data go, if it wasn’t in a volume?

Data written to a container’s own file system lives in its writable layer, which is removed with the container. Volumes exist independently and can be attached to the new container.