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.
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 volume | Bind mount | |
|---|---|---|
| Backed by | storage Docker manages | a specific host path |
| Syntax | -v myvol:/data | -v ./cfg:/configs |
| Use for | data that must persist | code/config you edit on the host |
| Survives removal? | yes, until you delete the volume | lives on the host regardless |
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.
-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
docker ps -a— is itExited? What code?docker logs <name>— the process's own error output. Start here.docker inspect <name>— resolved env, mounts, network.docker exec -it <name> sh— shell inside a running container to poke around (this is why the repo's image shipsbash/netcat).
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.
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…
-p required.
Q2. For a database's data files you should use a…
postgres_data); bind mounts are for host code/config you edit.
Q3. In this repo a service connects to Postgres at…
local-manabie-network, so it's db:5432.
-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 -a → logs → inspect →
exec -it … sh.ENTRYPOINT and command: args, or why
localhost inside a container isn't your Mac? Ask me.
1. Docker — Running containers.