Lesson 3 · Containers & Docker

Running containers

Ports, volumes, env vars, networks — the four dials that turn an image into a useful process.

Your win: know the four things you configure when you run a container — ports, volumes, environment, network — tell a named volume from a bind mount, and have a first move when a container won't start.

From image to running container

An image on its own does nothing. docker run turns it into a container, and the flags you pass are how it connects to the outside world.1 There are four you'll use constantly — the same four appear as keys in Compose (Lesson 4), so learning them here pays off twice.

1 · Ports — -p host:container

A container's network is private. To reach a port from your Mac, you publish it: -p 5432:5432 maps host port 5432 → container port 5432.

Anchor local/docker-compose.infra.yaml:5-6 — the db container publishes "5432:5432". That's the only reason your local psql or a GUI on the host can reach the containerised Postgres. Remove it and the DB still runs — other containers still reach it — but your host can't.

2 · Volumes — persist data, or mount host files

A container's writable layer is thrown away when the container is removed. To keep data, or to feed files in, you mount a volume. Two kinds, and the difference is a favourite interview question:2

Named volumeBind mount
Backed bystorage Docker managesa specific host path
Syntax-v myvol:/data-v ./cfg:/configs
Use fordata that must persistcode/config you edit on the host
Survives removal?yes, until you delete the volumelives on the host regardless
Anchor — both kinds, side by side local/docker-compose.infra.yaml:11-13 — the db uses both: a named volume postgres_data:/var/lib/postgresql/data (so your data survives a restart) and a bind mount of the migrations directory from the host (read-only seed SQL). A service like fink (docker-compose.service.macos.yaml:28-31) bind-mounts its configs and secrets in — config you can edit without rebuilding the image.

3 · Environment variables — -e KEY=value

Runtime configuration handed to the process. In the repo, services get GOOGLE_APPLICATION_CREDENTIALS and Postgres gets POSTGRES_PASSWORD this way (docker-compose.infra.yaml:7-8). Env vars are the standard way to keep config out of the image — the same image runs in local/stag/prod with different env.

4 · Networks — how containers find each other

Containers on the same Docker network reach each other by service name as a hostname — Docker runs an internal DNS.3 No IP juggling.

The insight that trips people up Port publishing (-p) is only for the host to reach a container. Container-to-container traffic doesn't need it — they just use the service name on their shared network. In this repo everything sits on local-manabie-network, so a service connects to Postgres at host db:5432, never localhost.

When a container won't start — first moves

Most "it won't start" cases are one of: bad command:/args, a missing bind-mounted file, a wrong env var, or a dependency that isn't ready yet — which is exactly what Lesson 4's healthchecks solve.

Read this next

Docker — running containers & managing data

The full run surface (ports, env, restart) and the volumes-vs-bind-mounts guide.

docs.docker.com — Running containers
docs.docker.com — Manage data (volumes vs bind mounts)

Check yourself (from memory)

Q1. Publishing a port with -p 5432:5432 is needed so that…

Publishing is host→container. Container→container uses the service name on a shared network, no -p required.

Q2. For a database's data files you should use a…

Named volumes are for persistent data (the repo's postgres_data); bind mounts are for host code/config you edit.

Q3. In this repo a service connects to Postgres at…

Same network → resolve by service name. Everything sits on local-manabie-network, so it's db:5432.
Name the four run-time dials, and named-volume vs bind-mount.
recall, then click to reveal
FOUR DIALS: (1) PORTS -p host:container — publish so the HOST can reach it (not needed container↔container). (2) VOLUMES — persist/attach storage. (3) ENV -e KEY=val — runtime config (keeps the image env-agnostic). (4) NETWORK — same network ⇒ reach peers by SERVICE NAME via Docker DNS. NAMED VOLUME = Docker-managed, for persistent DATA (repo postgres_data); BIND MOUNT = a HOST PATH, for config/code you edit (repo service configs/secrets). Debug a dead container with docker ps -alogsinspectexec -it … sh.
Want the difference between ENTRYPOINT and command: args, or why localhost inside a container isn't your Mac? Ask me.

1. Docker — Running containers.

2. Docker — Manage data in Docker (volumes & bind mounts).

3. Docker — Networking overview.