sehaj/learn
Docker/03 Running containers

03.04 · with code · 10 min

Logs and exec

Where container logs come from, how to read them without drowning, and how to get a shell inside a running container when you need one.

The core idea

Docker captures whatever a container's main process writes to stdout and stderr. That's all a container log is. So the rule for apps in containers is simple: log to the console, not to files, and let Docker (or whatever collects Docker's logs) handle storage.

Reading logs

❯terminal
$ docker run -d --name talky alpine sh -c 'for i in 1 2 3; do echo "out $i"; echo "err $i" >&2; sleep 1; done'
$ docker logs talky
out 1
err 1
out 2
err 2
out 3
err 3
$ docker logs talky 2>&1 >/dev/null
err 1
err 2
err 3
$ docker logs -t --tail 2 talky
2026-11-02T20:22:14.462506211Z out 3
2026-11-02T20:22:14.462563711Z err 3

docker logs keeps stdout and stderr separate, so your shell can split them. The flags you'll use most:

FlagDoes
-ffollow new lines as they arrive, like tail -f
--tail 50start from the last 50 lines instead of the beginning
--since 10monly lines from the last 10 minutes (also takes timestamps)
-tprefix each line with its timestamp

Logs survive a container stopping; they're deleted with docker rm.

Where logs are stored, and why they fill disks

By default Docker uses the json-file logging driver: one JSON file per container on the host, with no size limit. A chatty container left running for months can fill a server's disk.

❯terminal
$ docker info -f '{{.LoggingDriver}}'
json-file
$ docker inspect -f '{{.LogPath}}' talky
/var/lib/docker/containers/a6e2d74169.../a6e2d74169...-json.log

Cap it per container:

❯terminal
$ docker run -d --name rotated --log-opt max-size=10m --log-opt max-file=3 alpine true
$ docker inspect -f '{{json .HostConfig.LogConfig}}' rotated
{"Type":"json-file","Config":{"max-file":"3","max-size":"10m"}}

Or set the same defaults for every container in the daemon's daemon.json with "log-opts". Docker also offers a local driver that rotates and compresses by default, plus drivers that ship logs to journald, syslog or cloud services.

Getting inside: exec

docker exec runs an extra process in a running container's namespaces. With -it, that process is an interactive shell:

❯terminal
$ docker exec -it notes sh
$ docker exec notes whoami
node
$ docker exec -u root notes whoami
root

exec runs as the image's USER unless you pass -u. It only works on running containers, and only if the image has the program you ask for: distroless images have no sh at all (02.07 Choosing a base image).

To move files without a shell, use docker cp, which works on stopped containers too:

cp
docker cp notes:/app/package.json ./from-container.json
docker cp ./debug.json notes:/tmp/debug.json
Predict first

Your app writes its logs to /var/log/app.log inside the container. What does docker logs show?

Commit to an answer before you read on.

The official nginx image handles this by symlinking its log files to /dev/stdout and /dev/stderr, a handy trick for software you can't reconfigure.

CheckpointFollow, filter, and look inside

Run the notes API, then in a second terminal docker logs -f notes. Post a few notes and watch nothing appear (it only logs at startup and shutdown). Stop it and watch the SIGTERM received line arrive. Start it again with --log-opt max-size=1m and confirm the setting with docker inspect. Use docker exec to list /app/dist.

STUCK? ▸

docker exec says the container isn't running: start it first. "executable file not found": the image has no shell at that path; try sh instead of bash.

Recap

Interview questions

MidfreqMust-knowDesign

How do logs work for Docker containers, and how would you handle them in production?

Report a mistakeEdit this page