Docker logs and journald: choosing a logging driver
When a container crashes, logs are the first thing you need to see. docker logs looks simple, but under the hood different logging drivers are at work, and the choice affects how logs are stored, rotated, and accessed. Here is what you should know before trusting the default.
How docker logs works
The docker logs <container> command reads the container’s stdout/stderr stream and outputs it to the terminal. Behind this sits a logging driver — a component that determines where the data actually goes. By default it is json-file: each container gets a JSON file on the host into which every output line is written.
docker logs does not read logs from inside the container directly — it queries the driver, which already stores the data in its own format and location.
The driver is configured at the Docker daemon level or per container. The choice affects log rotation, access via journalctl, and integration with centralized collection systems.
Driver json-file (default)
json-file is the built-in driver with no external dependencies. Each container creates a file at /var/lib/docker/containers/<container-id>/<container-id>-json.log. The format is JSON lines: each entry contains log, stream (stdout or stderr), and time.
Rotation is controlled by two flags:
| Flag | Description |
|---|---|
max-size | Maximum size of a single log file (e.g., 10m) |
max-file | Number of rotated files to retain |
Without these flags, log files grow without limits. In production this is a direct path to filling the disk.
If max-size and max-file are not explicitly set, Docker does not limit log size. On a host with many containers this will result in unexpected disk exhaustion.
Logs can be read via docker logs or directly from the file path on the host, but the latter is not recommended because files may be held open by the daemon.
Driver journald
journald sends container logs into the systemd journal. This means logs are accessible through journalctl, all of journald’s rotation and compression mechanisms apply, and there are no separate JSON files growing on disk.
This requires systemd and the systemd-journal-remote package (on some distributions). The container must be started with the driver specified:
The tag flag sets the identifier in journald — without it the tag is an empty string and finding the right container becomes difficult. The template {{.Name}} substitutes the container name.
Use tag={{.Name}} or tag={{.ID}} so that logs in journald are immediately tied to a specific container. Without a tag, filtering by CONTAINER_NAME does not work.
Reading logs:
More precisely, through journald filters:
Actual filtering depends on which metadata Docker passes into journald. Check journalctl -o verbose for a specific container to see what fields are available.
Comparing json-file and journald
| Parameter | json-file | journald |
|---|---|---|
| Storage location | /var/lib/docker/containers/... | /var/log/journal/ |
| Rotation | Via --log-opt | Via journald.conf |
| Search | docker logs --since, grep | journalctl --grep, --since |
| Dependencies | None | systemd |
| Centralization | Via fluentd, gelf, awslogs | Via journalctl --remote or forward |
| Compression | No (manual) | Yes, configurable in journald.conf |
| Access without Docker | Direct file access | Only via journalctl |
journald does not support all log-opt flags available for json-file. For example, max-size and max-file do not work — rotation is controlled by journald’s own settings (SystemMaxUse, SystemMaxFileSize, etc.).
Configuring the driver in daemon.json
The global setting goes in /etc/docker/daemon.json:
After changing it, restart Docker:
Changing the driver in daemon.json affects all new containers. Already running containers continue using their current driver until restarted.
Per-container override is possible via --log-driver and --log-opt at startup — this takes precedence over daemon settings.
To check the current driver for a specific container:
Practical recommendations
For local development, json-file with explicit max-size and max-file is sufficient and simple to use. For production with dozens of containers on a single host, journald is preferable: a unified search space, built-in compression, and integration with systemd and monitoring.
If you already use systemd to orchestrate containers (via systemd unit files or Podman), journald is the natural choice. Container logs and service logs end up in one place.
For centralized collection, both drivers support log forwarding through intermediate drivers (fluentd, gelf, splunk). But journald adds an extra step: first into journald, then the forwarder. For simple cases, direct json-file + fluentd may be a shorter path.
Monitor disk space regularly regardless of the driver. journalctl --disk-usage and du -sh /var/lib/docker/containers/*/ are the minimum set for this.