15.1.1.3 Log Driver Handling
A focused guide to Log Driver Handling, connecting core concepts with practical Docker and container operations.
Log driver handling covers how Docker's pluggable logging driver system is configured, which specific driver is appropriate for a given deployment, and the practical consequences of switching between drivers, since the choice affects not just where logs end up but which Docker tooling continues to work against a given container afterward.
The default: json-file
json-file is Docker's default logging driver, storing each container's captured stdout and stderr as a JSON-formatted file on the host's local disk:
docker run -d --log-driver=json-file my-api
docker inspect --format='{{.HostConfig.LogConfig.Type}}' my-api
Its main advantages are simplicity and full compatibility with docker logs, since the driver and the command both operate on the same local file; its main drawback is that it provides no cross-host aggregation and, left unbounded, can grow until it exhausts host disk space.
The local driver
The local driver is a more storage-efficient alternative to json-file, using a compressed, binary format rather than verbose JSON text, while still supporting docker logs against it:
docker run -d --log-driver=local --log-opt max-size=10m my-api
For deployments that want the simplicity of local storage with less disk overhead than json-file, local is generally the better default, though it sacrifices the easy human-readability of inspecting the raw log files directly on disk, which json-file's plain JSON format provides.
Syslog and journald
The syslog and journald drivers integrate container logs with a host's existing system logging infrastructure, which is useful when a host already centralizes its own system logs through one of these mechanisms and the operator wants container logs to flow through the same pipeline:
docker run -d --log-driver=syslog --log-opt syslog-address=udp://logs.example.com:514 my-api
docker run -d --log-driver=journald my-api
journalctl CONTAINER_NAME=my-api
The journald driver specifically integrates well with systemd-managed hosts, allowing journalctl to filter directly by container name or ID without needing a separate tool to access container-specific log output.
Remote-native drivers: gelf, fluentd, awslogs, splunk
Several drivers send log output directly to a specific remote destination, removing the need for a separate node-level collector to forward json-file output onward:
docker run -d --log-driver=gelf --log-opt gelf-address=udp://graylog-host:12201 my-api
docker run -d --log-driver=awslogs --log-opt awslogs-group=my-app-logs --log-opt awslogs-region=us-east-1 my-api
These drivers trade local docker logs access (which stops working once logs are no longer also stored locally) for simpler infrastructure, since no separate log-shipping agent process needs to run on the host at all; the container's log output goes directly to its final destination.
The none driver
The none driver disables log capture entirely, which is appropriate only for containers whose output is genuinely never needed, such as certain short-lived utility containers, since any captured output is permanently discarded the moment it is written, with no docker logs access and no possibility of later recovery:
docker run -d --log-driver=none my-api
Choosing none for an application container is rarely correct, since it removes the most basic observability signal available, but it is occasionally appropriate for containers performing extremely high-frequency, low-value output where even local storage overhead is undesirable and no other observability mechanism is being relied on instead.
Setting a daemon-wide default
Configuring the default logging driver at the daemon level, rather than relying on every individual docker run invocation or Compose file to specify it consistently, ensures new containers get sensible logging behavior even if a specific deployment forgets to configure it explicitly:
{
"log-driver": "local",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
systemctl restart docker
This daemon-level default applies to any container that does not explicitly override the logging driver in its own configuration, which is why setting a sensible, bounded default at this level is one of the more impactful, low-effort production hardening steps available for a Docker host.
Consequences of switching drivers on existing containers
Changing the logging driver requires recreating the container, since the driver is fixed at container creation time and cannot be changed on a container that already exists:
docker stop my-api
docker rm my-api
docker run -d --log-driver=local --name my-api my-api:1.4.0
Existing log history captured under the previous driver is not automatically migrated or merged into the new driver's storage; switching drivers effectively starts a new, separate log history for the container going forward, which is worth planning around if continuity of historical logs across a driver change matters.
Per-container overrides within a Compose stack
A Compose stack can set a default driver for most services while overriding it for one that has different needs, such as a high-volume service that should bypass local storage entirely in favor of a direct remote driver:
services:
api:
logging:
driver: local
high-volume-worker:
logging:
driver: awslogs
options:
awslogs-group: high-volume-logs
Common mistakes
- Leaving the daemon-level logging driver and options unconfigured, relying entirely on individual container definitions to set bounds consistently.
- Choosing a remote-native driver without realizing it disables local
docker logsaccess, then being unable to quickly inspect a container's recent output during an incident. - Using the
nonedriver on an application container, removing the most basic observability signal with no equivalent replacement in place. - Assuming a logging driver can be changed on a running container without recreating it.
- Forgetting that switching drivers effectively discards continuity with the previous driver's log history, which matters if historical log access across the transition is needed.
Log driver handling is fundamentally about matching the driver's behavior, local storage with docker logs compatibility, remote-native delivery, or host system log integration, to the deployment's actual operational needs, set consistently at the daemon level as a sensible default and overridden only where a specific service's requirements genuinely differ.