Question bank — Docker — images, containers, Dockerfile, docker-compose
A quick refresher on the four words you will keep meeting (each is spelled out fully in the parent note):
- An image is the read-only recipe (stacked immutable layers).
- A container is a running instance of that image, plus one thin writable layer on top.
- A Dockerfile is the build script that produces the image.
- docker-compose is the YAML that runs many containers together.
Before the questions, four small pictures anchor the ideas the traps are built on. Refer back to them as you drill.

The layer-caching figure above shows why a bad instruction order is so costly: a red "edit" in one layer poisons every layer stacked below it.

The port-mapping figure shows the -p HOST:CONTAINER direction — traffic enters the host door on the left and is forwarded to the container room on the right.

The container vs VM figure shows why one is heavy and one is light: the VM stacks a whole guest OS on a hypervisor, the container shares the host kernel.

The volume / lifecycle figure shows the ephemeral writable layer dying with the container while a named volume survives outside it.
True or false — justify
A container is basically a lightweight virtual machine
Two containers from the same image each get their own private copy of every layer
Deleting a container also deletes the image it came from
docker run a fresh identical instance from the untouched image.Data written inside a running container survives after you docker rm it
EXPOSE 8000 in a Dockerfile makes the app reachable from your browser
EXPOSE only records metadata ("this app intends to use 8000") for humans and tools; it opens no host port, so you still need -p 8000:8000 (or a ports: entry) at run time to actually publish it.Every instruction in a Dockerfile creates a new filesystem layer
RUN, COPY, ADD — create real layers; metadata-only instructions like CMD, ENTRYPOINT, EXPOSE, ENV, LABEL, and WORKDIR add image config but no filesystem layer, so they are essentially free and never trigger a re-install cascade.docker build and docker run do the same job at different times
build reads a Dockerfile and produces an image (a static template); run takes an existing image and produces a live container (a process) — two distinct stages, and you can run the same image many times without ever building again.An image with tag nginx:1.25 and one with nginx:latest are always different images
latest can point at the same digest as 1.25, so both names may be two labels on one identical image.Containers on the same Compose network can talk to each other using the service name as a hostname
postgres://db:5432 works even though IPs change on restart.RUN and CMD both execute when the container starts
RUN runs once at build time and bakes its result into an image layer; CMD runs at container start time as the main (PID 1) process — build-time versus run-time is the whole distinction.Spot the error
COPY . . then RUN pip install -r requirements.txt — what's wrong with this order?
COPY layer's content hash; since a layer's cache key includes every layer above it, the install layer below is now considered stale and re-runs on every build. Copying only requirements.txt first means the install layer's inputs change only when dependencies change, so day-to-day code edits stay cached.docker run -p 8000:8080 myapp when the app listens on port 8000 inside — why does the browser fail?
-p is HOST:CONTAINER (figure 2), so this forwards host-8000 to container-8080, but the app is listening on container-8000 — the forwarded traffic arrives at a port where nothing is bound and the connection is refused. It should be -p 8000:8000.A Postgres service in Compose with no volumes: entry — what breaks on docker compose down
down deletes the containers, taking that layer — and all the data — with them. Mounting a named volume at /var/lib/postgresql/data stores the files outside the container so they survive teardown and re-creation.ENTRYPOINT ["python"] with CMD ["app.py"], then a user runs docker run img script.py — what happens?
ENTRYPOINT is the fixed executable and stays; CMD supplies default arguments that any trailing docker run arguments completely replace, so app.py is dropped and the container runs python script.py. This override-the-args-not-the-program behaviour is exactly why the pair is used.A Dockerfile ends with RUN python app.py instead of CMD — why won't the container stay up as a server?
RUN executes during the build and its process must finish before the next instruction, so the server would run (and be killed) at build time, leaving nothing to start at run time. With no main process, the container has nothing to keep alive and exits immediately — CMD ["python","app.py"] defers the launch to container start.Two services in Compose both map "8080:8000" on the host — why does one fail to start?
8081:8000) — the container side can safely repeat.FROM ubuntu chosen for a tiny Python API — what's the cost?
-slim or Alpine base strips that down to near the bare runtime.Why questions
Why does Docker boot in milliseconds while a VM takes seconds or minutes?
fork/exec plus isolation setup.Why should the Dockerfile be ordered from least-changing to most-changing instructions?
Why can ten containers from one image cost far less disk than ten separate copies?
Why use ENTRYPOINT + CMD together instead of one CMD?
ENTRYPOINT pins the program that always runs, while CMD supplies default arguments the user can override on docker run without retyping the executable. This gives the image a clean command-line interface — like a compiled tool with sensible defaults — instead of forcing users to know and repeat the full command.Why does Compose need depends_on for a web-plus-db stack?
web before db, and the web app's first connection attempt hits a database that is not yet accepting connections. depends_on fixes the start order of containers (though note it waits for the container to start, not for Postgres to be fully ready — a health check handles that).Why pass config like DATABASE_URL as an environment variable rather than hard-coding it in the image?
Why is docker exec -it <id> bash useful even though the image already has a CMD?
CMD launched the container's main process; exec spawns an additional process (an interactive shell) inside the same namespaces, so you land in the running container's filesystem and network to inspect logs, files, or processes — all without disturbing or restarting the main app.Edge cases
What runs if a container's image has no CMD and no ENTRYPOINT at any layer?
docker run fails with an error like "no command specified". In practice most images do inherit a default from their base — e.g. python:3.12 sets CMD ["python3"] and postgres:16 sets an ENTRYPOINT that launches the DB — so you only hit this error when you build FROM scratch (an empty base) or override the defaults away.What happens to a -d (detached) container whose main process exits immediately?
docker ps -a — the fix is to keep a long-running process as PID 1.If you docker build twice with zero file changes, what does the second build do?
What is a bind mount ./code:/app good for that a named volume is not?
Can two apps needing different versions of the same system library coexist on one host with Docker?
What survives if you delete a container but the image is still tagged?
docker run a brand-new identical container from the same image — the recipe is untouched.Recall One-sentence self-test
Cover every answer above and re-derive the reasoning; if you can only say "true/false" without the why, you have memorised the trap, not defused it.