What is Docker's default log driver, and where does it write a container's stdout on the host?
answer
- Two streams, one component in the daemon
- The default writes files, not a database
- One JSON object per line, on the host
- Under /var/lib/docker/containers/<id>/
- Frozen into HostConfig at container creation
basics
~20 sDocker's default log driver is json-file: the daemon captures whatever a container writes to stdout and stderr and appends it as JSON lines to /var/lib/docker/containers/<id>/<id>-json.log on the host. A per-container --log-driver flag overrides that default.
solid answer
~40 sThe Docker daemon does not let a container's `stdout` and `stderr` go to a terminal — it captures both streams and hands every line to a **logging driver**. Unless you configure otherwise, that driver is **json-file**, which appends one JSON object per line (fields `log`, `stream`, `time`) to `/var/lib/docker/containers/<container-id>/<container-id>-json.log`. Only stdout and stderr are captured: anything the process writes to a file inside the container is invisible to the driver, which is why containerised apps are expected to log to the console. The default can be changed daemon-wide with the `log-driver` and `log-opts` keys in `/etc/docker/daemon.json`, or per container with `docker run --log-driver=local --log-opt max-size=10m`. The choice is resolved when the container is *created* and stored in its `HostConfig.LogConfig`, so changing the daemon default never re-configures containers that already exist.
code
bash · 3 linesdocker run -d --name tiles \
--log-driver=local --log-opt max-size=10m --log-opt max-file=3 \
tileserv:2.4go deeper
Be ready to say that the default driver is json-file, that it captures only stdout and stderr, and that it writes a JSON-lines file under /var/lib/docker/containers. Knowing the path is a genuine plus in a screening round.
Explain that the daemon reads the two streams and hands them to a driver, that the driver choice is stored per container at creation time, and how the daemon.json default relates to a --log-driver flag on a single run.
Show you check HostConfig.LogConfig rather than trusting daemon.json, and that you treat 'the app logs to a file inside the container' as a design defect to fix at the application rather than a bind mount to bolt on.
Own the policy: which driver is the fleet default, who may override it per container, and what that choice implies for retention, host disk and whether on-call can still read a container's output during an incident.
## What a log driver actually is When `dockerd` starts a container it does not attach the container's `stdout` and `stderr` to your terminal. It attaches them to pipes (or a pseudo-terminal when the container is created with `-t`), reads them inside the daemon, and passes each message to a **logging driver** — a small component compiled into the engine whose only job is to decide where that message goes. The message carries three things: the text of the line, which stream it came from (`stdout` or `stderr`), and a timestamp. This is the first thing to internalise: the driver only ever sees the two console streams. If a Django app configured with a `FileHandler` writes to `/var/log/app.log` inside the container, no log driver on earth will see it — the bytes land in the container's writable layer and disappear when the container is removed. The convention that containerised processes log to the console is not style advice, it is the only path into this machinery. ## The default: json-file With no configuration at all, the driver is **json-file**. It appends one JSON object per line to a file on the host: ``` /var/lib/docker/containers/<container-id>/<container-id>-json.log ``` Each line looks like `{"log":"listening on 8000\n","stream":"stdout","time":"2026-09-03T10:14:02.118Z"}`. The file is owned by root and lives outside the container's writable layer, on whatever filesystem holds Docker's data root. Two consequences follow immediately: you need host root to read it directly, and it is *not* part of the container's own storage — a common misconception is that a container with a read-only root filesystem cannot fill the disk with logs, when in fact the daemon is doing the writing on the host's behalf. By default json-file applies **no rotation** at all, so the file grows for as long as the container runs. That is the single most common way a Docker host runs out of disk. ## The other drivers The engine ships several drivers besides json-file: - **local** — a compact, purpose-built file format that rotates and compresses by default; the sensible modern choice when you just want files on the host. - **journald** — hands each message to the systemd journal on the host, where `journalctl` can filter it by container name or id. - **syslog**, **gelf**, **fluentd**, **awslogs**, **gcplogs**, **splunk** — shipping drivers that send messages off the host to a collector or a cloud log service. - **none** — throws the messages away. The container still runs; it simply produces no logs anywhere. ## Setting the default, and overriding it per container The daemon-wide default lives in `/etc/docker/daemon.json`: ```json { "log-driver": "local", "log-opts": { "max-size": "10m", "max-file": "3" } } ``` and takes effect after the daemon is restarted. The crucial detail is *when* it is applied: the driver and its options are resolved at **container creation** and frozen into that container's configuration. Restarting the daemon with a new default does not re-configure the containers already on the host, and neither does `docker restart` — you have to remove and recreate the container for a new default to apply. `docker inspect` will show you what a given container actually got, under `HostConfig.LogConfig`. An individual container can always opt out: ``` docker run -d --log-driver=none noisy-batch-job:1.9 ``` Per-container overrides are how you handle the exception — a chatty batch job you do not want to keep, or one service that must go straight to a collector — without changing the policy for the whole host. Compose exposes the same two settings on a service under its `logging` key. ## Why the choice matters early Picking a driver is not just plumbing. It decides whether logs survive the container's removal, whether `docker logs` can show them to an on-call engineer, whether a slow log backend can stall the application, and how much host disk the fleet can consume. Leaving the default in place is a decision too — it means unbounded JSON files on the host, one per container, with no retention policy other than the lifetime of the container. There is also a cost consideration that is easy to miss. json-file stores each message as escaped JSON with a full RFC3339 timestamp and a stream name on every line, so a short log line can more than double in size on disk. For a service emitting a line per request that overhead is the dominant term in the log volume, and it is paid on the host's filesystem, on every read, and again in any compression the driver does. The `local` driver's binary format exists specifically to remove it. Finally, note that the lifetime of the log is the lifetime of the *container object*, not of the process. Restarting a container appends to the same file; removing the container deletes its directory, log files and all. Anyone who has run a container with `--rm` while debugging a crash has discovered the second half of that rule the hard way — the container exits, the engine removes it, and the evidence goes with it.
- You changed log-driver in daemon.json and restarted the daemon, but a running container still uses the old driver. Why?The driver and its options are resolved when the container is created and stored in that container's `HostConfig.LogConfig`. A daemon restart changes only the default applied to containers created *afterwards*; `docker restart` reuses the existing configuration. You must remove and recreate the container — or pass `--log-driver` explicitly on the new run — for the change to take effect. `docker inspect` on the container shows which driver it is actually using.
- An application writes its logs to a file inside the container. What do you change, and why not just bind-mount the log directory out?Point the application's log handler at stdout instead, so the engine's log driver sees it. A bind mount works mechanically but reintroduces everything containers avoid: host paths, permissions and UID mapping, per-host rotation you have to run yourself, and logs that no longer flow through `docker logs` or the configured driver. Console output is the one path the whole toolchain understands.
- What does --log-driver=none do, and when is it defensible?It discards the container's stdout and stderr entirely — nothing is written anywhere, and reading the container's logs back is impossible. It is defensible for a container whose output is genuinely worthless and high volume, such as a benchmark or a job that already ships its own telemetry. It is a bad default, because the first incident on that container leaves you with nothing to look at.
The driver is the pipe fitting on the back of the container: the process just shouts into the room, and whatever fitting you bolted on decides whether that ends up in a file on the host, in the system journal, or on a wire to a collector.
saying these in an interview costs you the question
- Thinks docker logs streams straight from the process
- Believes container log files live in the writable layer
- Expects logs written to a file inside the container to appear
- Assumes changing daemon.json reconfigures existing containers
- Thinks json-file rotates on its own by default
- Cannot name any driver other than the default