Choosing the Right Docker Log Driver: json-file vs. journald
AI generated
FROM
RUN
Docker · Log Driver · journald · json-file
Choosing the Right Docker Log Driver: json-file vs. journald
rotation, storage consumption and integration compared

Docker's default setting is rarely the right choice for production environments. The log driver json-file only rotates logs after explicit configuration and exists as a plain text file, while journald integrates logs directly into the systemd journal infrastructure, with structured metadata and central querying via journalctl.

16 min read json-file · journald · log-opts · log rotation Docker Engine · systemd

1. Why the choice of log driver matters at all

Every Docker container writes its output to stdout and stderr, but what happens to that data is decided by the configured log driver. Without deliberate configuration, Docker uses json-file by default, which writes every line as a JSON object into a local file, originally without any rotation at all. On a production host with long lived containers and chatty applications, this unnoticeably leads to log files that fill up the disk until the entire host becomes unresponsive.

The alternative journald instead integrates container logs into the systemd journal, the same infrastructure also used for system and service logs on modern Linux distributions. The choice between these two log driver options is not merely a matter of taste, it has direct consequences for storage consumption, performance under load and the integration options with centralized logging. The following sections show the concrete differences and when which log driver is the better choice.

Besides json-file and journald, Docker offers further log drivers such as syslog, gelf or fluentd, which forward logs directly to an external system without local buffering. These drivers are deliberately left out of this topic, because they represent a different category of decision: they replace local storage entirely with immediate delivery, while json-file and journald both store locally first and are only picked up by a collector afterward. Exactly this local storage path, with its consequences for disk usage and diagnostic capability directly on the host, makes the difference between the two log driver options compared here so practically relevant.

2. The json-file log driver in detail

The log driver json-file writes every log line as an individual JSON object with the fields log, stream and time into a file under /var/lib/docker/containers/<container-id>/<container-id>-json.log. This format is simple to parse, which is why it forms the basis for tools like Promtail and Filebeat that expect exactly this JSON format by default. The big advantage: json-file works everywhere, regardless of the operating system, regardless of whether systemd is even present.

The decisive disadvantage without explicit configuration is the missing automatic rotation in older Docker versions and the need to set limits yourself. Without max-size and max-file as log options, the log file grows unbounded as long as the container runs. For chatty applications, for example a web server with access logs on stdout, a single log file can reach several gigabytes within a few days and, in the worst case, exhaust the entire host's available disk capacity.

Another practical point with json-file: since every log line is individually JSON encoded, the actual storage need roughly doubles compared to plain text due to the additional JSON structural characters and escape sequences, especially for logs with many special characters or embedded quotes. When planning log storage capacity, this overhead of roughly 20 to 30 percent over plain text should be factored in, so the chosen max-size values realistically match the actually available disk capacity.


# docker-compose.yml — json-file with explicit rotation limits
services:
  web:
    image: nginx:1.27
    logging:
      driver: "json-file"
      options:
        max-size: "10m"     # rotate after 10 MB per file
        max-file: "5"       # keep at most 5 rotated files (50 MB total)
        compress: "true"    # gzip rotated files to save disk space

# Inspect the raw log format directly on disk
# cat /var/lib/docker/containers/<id>/<id>-json.log | head -1
# {"log":"GET /health 200\n","stream":"stdout","time":"2026-07-30T10:15:03.421Z"}

# Set json-file with limits as the daemon-wide default
# /etc/docker/daemon.json
# {
#   "log-driver": "json-file",
#   "log-opts": { "max-size": "10m", "max-file": "5" }
# }

3. The journald log driver in detail

The log driver journald instead sends container logs to the systemd journal daemon, which stores them together with all other system and service logs in a binary, indexed database. Instead of a separate file per container, all logs end up in the same central journal structure, tagged with additional metadata like container name, image and container ID as searchable journal fields.

The practical advantage: rotation, compression and size limiting of the journal are controlled centrally via /etc/systemd/journald.conf for the entire host, instead of having to be configured for each container individually. Additionally, container logs can be searched via journalctl with the same powerful filtering capabilities administrators already know from system logs, such as time ranges, priority levels or field based filters, without needing a separate tool.


# docker-compose.yml — journald as the log driver
services:
  web:
    image: nginx:1.27
    logging:
      driver: "journald"
      options:
        tag: "{{.Name}}"    # custom SYSLOG_IDENTIFIER for easier filtering

# Query container logs directly via journalctl
# journalctl CONTAINER_NAME=web -f
# journalctl CONTAINER_NAME=web --since "1 hour ago" -p err

# Set journald as the daemon-wide default
# /etc/docker/daemon.json
# {
#   "log-driver": "journald"
# }

4. Configuring rotation and storage consumption

Getting rotation settings right early avoids a painful retrofit later, since changing them always requires recreating the affected containers rather than adjusting a running instance in place.

With json-file, responsibility for rotation lies entirely with the administrator. The options max-size and max-file together limit the maximum size per container: with max-size: 10m and max-file: 5, at most 50 megabytes of logs per container are created before the oldest file is overwritten. These limits apply per container and must be set either per service in the compose file or globally via daemon.json, where the container specific setting overrides the global one.

With journald, size limiting happens centrally via SystemMaxUse and RuntimeMaxUse in journald.conf, independent of the number of containers. This significantly simplifies management, because a single value limits the total size of the journal for all logs, not just container logs. The downside of this central limit: a single very chatty container can, with a tight journal size, push out older logs from other, more important services from the journal, if no prioritization is set up.

Both log drivers additionally support a labels or tag field to embed container metadata into the log output, which is especially helpful for later evaluation by external collectors. With json-file, additional Docker labels can be embedded as fields in every JSON line via log-opts, with journald the tag parameter plays a similar role for the SYSLOG_IDENTIFIER. This metadata enrichment is independent of the actual rotation and storage question, but plays an important role in later filtering in both worlds.

5. Performance differences under load

Under high log load, measurable differences show up between the two log driver options. json-file writes synchronously to disk, which can lead to noticeable I/O pressure under very high log volume, especially on hosts with slower disks or many containers logging in parallel. Since every line is individually serialized as JSON, some CPU overhead per line arises additionally, which is negligible at low log volume but measurable at extreme throughput.

journald internally buffers write operations and can thus better absorb brief load spikes, but also causes overhead due to the additional indexing for the journal database, which becomes noticeable at very high log rates. For the vast majority of production Docker environments with normal application logging, these performance differences are not noticeable, only at extremely high log volume, for example several thousand lines per second per container, does the choice of log driver become a measurable performance factor.

Another practical difference concerns handling of very long, unbroken output without a line break. With json-file, Docker buffers such output up to an internal upper limit before writing it as a single line, which can occasionally lead to unexpectedly split log entries with applications that have exotic output behavior. journald behaves slightly differently here, because the journal API brings its own rules for maximum field size, so extremely long single entries may be truncated or split differently than with json-file. For the vast majority of structured application logs with normal line lengths, this difference plays no role in practice, but deserves attention with applications that output very large stack traces or binary data on stdout.

In practice, a short load test on a representative staging host is worthwhile before a final decision: a script generating several thousand log lines per second across multiple containers simultaneously for a few minutes shows more reliably than any theoretical consideration whether the chosen log driver causes noticeable CPU or I/O wait times under the expected production load. Such tests also reveal whether the configured rotation limits actually kick in when it matters, before the disk hits its limits.

6. Integration with centralized logging and journalctl

For teams already running Promtail, Filebeat or Fluentd, json-file is often the simpler choice, because these tools are configured by default for the JSON line format under /var/lib/docker/containers and work without additional adjustment. If journald is used instead, these collectors need either a special journal input plugin, for example Promtail's journal scrape configuration, or an additional export step.

Conversely, teams that already operate a systemd centric infrastructure and use journalctl for system logs in daily operations benefit significantly from journald as a log driver, because container logs then appear without a context switch in the same interface alongside system logs. This decision therefore depends less on a technical superiority and more on which logging infrastructure is already established in the respective team and which tools should be connected.

7. Switching log drivers: procedure and pitfalls

The log driver of a running container cannot be changed afterward, it is fixed at the time the container is created. A switch therefore requires recreating the container with the new configuration, for example via docker compose up -d --force-recreate, after adjusting the logging section. Existing logs in the old format remain untouched by this and may need to be archived separately before the container is recreated and the old log file disappears from view.

A common stumbling block when switching to journald: the command docker logs continues to work, but reads the data internally via the journal, which in rare cases can lead to slightly different line behavior for very long, unbroken log output. Before a broad rollout of the log driver switch across all hosts, a test run with a few, less critical services is recommended, to identify these detail differences before production critical containers are affected.

For teams wanting to combine both worlds, a pragmatic middle path exists: journald as the primary log driver for systemd integration, complemented by a periodic export of selected container logs into a JSON based archive for further processing by existing collectors. This approach requires additional operational effort but can make sense when a single step migration seems too risky and both access paths are needed in parallel for a transition period.

8. Common mistakes when choosing a log driver

The most common mistake is running json-file without max-size and max-file in production. This omission gradually leads to full disks, often only noticed once the host already shows other problems from lack of disk space, for example failing deployments or a MySQL instance that can no longer perform new writes.


# Common mistakes when choosing a Docker log driver

# WRONG: json-file without any size limits — grows unbounded
# logging:
#   driver: "json-file"
#   # no options — log file can fill the entire disk

# RIGHT: always cap size and file count explicitly
# logging:
#   driver: "json-file"
#   options:
#     max-size: "10m"
#     max-file: "5"

# WRONG: switching to journald without checking journald.conf limits
# journald.conf might have SystemMaxUse too small for container volume

# RIGHT: size the journal for the actual container log volume
# /etc/systemd/journald.conf
# SystemMaxUse=2G
# RuntimeMaxUse=512M

# WRONG: assuming docker logs behaves identically after switching drivers
# some drivers (e.g. journald) don't support all `docker logs` flags identically

# RIGHT: verify docker logs --since/--tail behavior after switching

A second mistake is assuming that all log drivers support the same docker logs options identically. Some drivers, for example ones that forward logs exclusively to an external system, no longer support docker logs at all, because the data leaves the local host before Docker itself could access it. A third mistake is an undersized journal on journald, causing container logs to push out older, important system logs, if not enough space was planned for both purposes.

9. json-file and journald in direct comparison

An often overlooked aspect when choosing the log driver is traceability across container restarts and deployments. With json-file, a container's log file disappears together with the container, unless a separate volume or log shipping is set up, which makes troubleshooting the last state before a restart harder after a rolling update. With journald, entries remain in the central journal as long as journal rotation does not push them out, regardless of whether the associated container still exists. This difference becomes especially relevant with frequent deployments in CI/CD pipelines, where containers are replaced at short intervals and troubleshooting a failed deployment would otherwise come up empty.

The decision between the two most common log driver options depends on existing infrastructure and requirements for integration and performance.

Criterion json-file journald Recommendation
Rotation Per container, manually configured Centralized via journald.conf journald simpler with many containers
Tool compatibility Standard for Promtail, Filebeat Needs journal input plugin json-file with existing log pipeline
Systemd integration None Native via journalctl journald for systemd centric teams
Portability Everywhere, platform independent Only available with systemd json-file for mixed operating systems

For most Docker environments with centralized logging already established via Loki or Elasticsearch, json-file with explicit rotation limits remains the most pragmatic choice. journald pays off mainly when the infrastructure already relies heavily on systemd and journalctl is already the central point of reference for diagnostics.

10. Summary

Choosing the right Docker log driver between json-file and journald has direct consequences for storage consumption, performance and integration effort. json-file is portable, easy to understand and the standard foundation for most log collectors, but requires explicit max-size and max-file limits to avoid filling disks uncontrollably. journald natively integrates container logs into the systemd infrastructure, centralizes rotation and size limiting, but requires adapted collector configurations.

The right decision is rarely universal, it depends on the existing logging landscape. Anyone already running Loki or Elasticsearch with JSON based collectors usually stays with json-file. Anyone relying heavily on systemd and using journalctl in daily operations benefits from the seamless integration through journald. In both cases: configure explicit limits instead of relying on default values.

Finally, mixed setups are worth a look: nothing prevents running a critical database container with json-file and tight rotation limits, while a legacy service already monitored via journalctl continues to use journald. The decision does not have to be made uniformly across a host or project, as long as every container is deliberately configured individually and nobody blindly relies on the Docker default setting.

Docker Log Driver json-file vs. journald — Key Takeaways

json-file

Portable, JSON line format, standard for Promtail and Filebeat, needs explicit max-size/max-file limits.

journald

Integrated into the systemd journal, central rotation, searchable via journalctl, needs journal input plugins.

Rotation

Configure json-file per container, journald centrally via journald.conf for the entire host.

Decision guide

Existing log pipeline and team familiarity with systemd usually matter more than raw performance.

Whichever driver a team ultimately settles on, the underlying discipline stays the same: log volume should always be a deliberate, sized decision rather than an afterthought discovered only once a disk fills up.

The following questions summarize the most common uncertainties in practice around choosing and operating the right log driver, and serve as a quick reference for day to day decisions.

Anyone who documents this decision process cleanly once saves the repeated fundamental discussion for every new service or host, and ensures that all containers in an environment are configured according to the same traceable criteria.

11. FAQ: Docker Log Driver json-file vs. journald

1Docker's default log driver?
json-file, unless configured otherwise. Without limits, the log file grows unbounded.
2Protecting disk from json-file?
With max-size and max-file as log options, per container or globally in daemon.json.
3Changing log driver live?
No, the container must be recreated with the new configuration.
4docker logs with journald?
Still works, reads internally via the systemd journal.
5Is journald faster?
Barely any difference at normal log volume, only measurable at extreme throughput.
6Searching logs across containers?
Via journalctl with field filters like CONTAINER_NAME, combined with time and priority filters.
7Promtail with journald?
Yes, but needs the special journal scrape configuration instead of Docker service discovery.
8Old logs after switching?
Stay in the old format, should be archived separately before the switch.
9Multi host setups?
Both work, json-file is usually simpler to integrate with existing collectors.
10Multiple drivers at once?
Yes, configurable per container in the logging section, independent of other services.