Seeing inside a running container
A detached container from lesson 2-2 runs silently in the background, and two commands make it observable.
docker logs web # everything the container has printed docker logs -f web # keep following new output live (Ctrl-C to stop)
Containers do not write log files you go hunting for. Whatever the program prints to standard output becomes docker logs, which is the whole logging story here.
When logs are not enough, get a shell inside the container:
docker exec -it web bash
| Piece | Effect |
|---|---|
exec | run an extra command inside an already-running container |
-it | interactive, with a terminal, as in lesson 2-1 |
web bash | the container name, then the command |
Inside, ls, cat, and ps show the container's private world. Typing exit leaves the shell and the container keeps running, because exec added a process rather than replacing the main one.
The lifecycle
Every container moves through the same states:
docker run -d --name web nginx # created + running docker stop web # running -> stopped (politely, then forced) docker start web # stopped -> running again docker rm web # stopped -> gone (rm -f skips the stop)
A stopped container still exists: its writable layer, its name, its settings all remain, which is why docker ps -a from lesson 2-1 shows exited containers. docker rm is what actually deletes it.
The habit to build: never hand-repair a container. Any fix you apply with exec lives only in that container's writable layer, is recorded nowhere, and dies with the container, so the next machine (or the next deploy) has the bug again. Put the fix in the Dockerfile or config instead and recreate: stop, rm, run a fresh one from the image. Ops people compress this into "cattle, not pets": individual containers get no special care. exec is for diagnosing, not for repairing.
A recorded session
This session walks the full lifecycle: starting a web server, watching it, looking inside it, and tearing it down.
Each step below shows the command and the output it printed.
Step 1. Start an nginx container in the background, named web, publishing host port 8080 to container port 80.
you@laptop $ docker run -d --name web -p 8080:80 nginx 9be51c48a2d0f7e33e01d3a41f22b8e0a9c3a7d05b6f2e41a8cd90b7f31a55c2
Step 2. Confirm it is running.
you@laptop $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9be51c48a2d0 nginx "/docker-entrypoint.…" 5 seconds ago Up 4 seconds 0.0.0.0:8080->80/tcp web
Step 3. Check what the server has printed so far.
you@laptop $ docker logs web /docker-entrypoint.sh: Configuration complete; ready for start up 2026/07/05 18:12:03 [notice] 1#1: nginx/1.27.0 2026/07/05 18:12:03 [notice] 1#1: start worker processes
Step 4. Open a shell inside the running container.
you@laptop $ docker exec -it web bash
root@9be51c48a2d0:/#Step 5. You are inside now. Leave the container shell.
you@laptop $ exit
you are back on the host, container still runningStep 6. Stop the container, then remove it.
you@laptop $ docker stop web && docker rm web web
The same thing can be written as docker rm -f web.
Step 7. Verify nothing is left, not even stopped containers.
you@laptop $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Getting a shell in a running container
For a container named api that is running and behaving oddly, the command is docker exec -it api bash.
exec runs an extra command inside an existing running container, which is why it needs a container name rather than an image.
| Command | What it actually does |
|---|---|
docker exec -it api bash | a shell inside the running container |
docker run -it api bash | starts a new container from an image named api |
docker logs api | shows output only |
docker start api | restarts a stopped container |
The run version is the common mistake, and it is a different operation entirely. It looks for an image by that name, and even if one exists, the resulting shell is in a brand-new container that does not have the problem you were investigating.
The command that shows what a container printed
The command is docker logs web.
Containers log by printing to stdout, and Docker captures it, so container output is not in a file you have to hunt for. Adding -f follows live output.
| Variant | Use |
|---|---|
docker logs web | everything printed so far |
docker logs -f web | follow new output live |
docker logs --tail 50 web | just the last 50 lines |
This works on stopped containers too, which makes it the first tool for figuring out why a container died. The logs outlive the process and disappear only when the container itself is removed.