Understanding Docker Logging Drivers
When a containerized process writes to stdout or stderr, Docker captures that output and routes it through a configurable logging driver. A logging driver is essentially a plugin that defines where container logs are sent and in what format. The default driver is json-file, which writes log lines to individual files on the host's disk under /var/lib/docker/containers/<container-id>/. However, Docker ships with many built-in drivers, and you can also use third-party driver plugins.
The logging driver can be set per container at run time, or globally in the Docker daemon configuration. Understanding how to select and configure the right driver is fundamental to operating containers in production. It directly impacts observability, troubleshooting, disk usage, and integration with centralised logging systems.
Why Logging Driver Configuration Is Critical
🚀 Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →
Logs are often the first place engineers look when something goes wrong. In ephemeral container environments, losing logs means losing the ability to debug a crash or an unexpected behaviour. The default json-file driver, while convenient for local development, introduces several risks when used without tuning:
- Disk exhaustion: Logs grow indefinitely, potentially filling the host's disk and affecting other containers or the Docker daemon itself.
- Performance overhead: Writing every log line to disk synchronously (or with frequent flushes) can add latency and I/O contention on busy hosts.
- No built-in rotation: Without explicit log options, the JSON file grows without bound, making log retrieval and parsing slow.
- Poor integration: Scraping log files from each container's directory is fragile and doesn't scale well across multiple hosts.
By consciously choosing a logging driver and configuring it properly, you gain control over log lifecycle, delivery guarantees, and format. This is the foundation of a reliable observability stack.
How to Use Docker Logging Drivers
You can specify a logging driver at container creation via the --log-driver flag, and pass driver-specific options with --log-opt. The same configuration can be defined in a docker-compose.yml file under the logging key. To change the default for all containers on a host, edit the daemon.json file and restart Docker.
Common Built-in Drivers
json-file(default) – writes line-by-line JSON to a flat filesyslog– sends logs to the local syslog daemonjournald– writes to systemd journal (common on Linux hosts)gelf– writes to a Graylog or other GELF-compatible endpointfluentd– forwards logs to a Fluentd collectorawslogs– sends logs to Amazon CloudWatch Logssplunk– streams logs to Splunk HTTP Event Collectoretwlogs(Windows) – Windows Event Tracinggcplogs– Google Cloud Logginglogentries– Rapid7 Logentries
Each driver exposes its own set of options. Always refer to the official Docker documentation for the exact list, but the examples below illustrate the typical patterns.
Example: Using syslog driver with options
docker run -d \
--name my-app \
--log-driver syslog \
--log-opt syslog-address=udp://logs.example.com:514 \
--log-opt syslog-facility=daemon \
--log-opt tag="my-app/{{.Name}}" \
my-image:latest
Example: Docker Compose logging block
version: "3.8"
services:
web:
image: nginx:alpine
logging:
driver: fluentd
options:
fluentd-address: localhost:24224
fluentd-async: "true"
fluentd-retry-wait: 1s
tag: "nginx.{{.Name}}"
Setting a global default driver in daemon.json
{
"log-driver": "journald",
"log-opts": {
"tag": "docker/{{.Name}}"
}
}
After saving the file, restart Docker (systemctl restart docker) and all newly created containers will use journald unless overridden.
Configuring log rotation for json-file
Even if you stick with json-file, you must enable rotation. This is done via --log-opts:
docker run -d \
--name rate-limited-app \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=5 \
my-app:latest
The same options can be set globally in daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "5"
}
}
Best Practices for Docker Logging
1. Prefer structured logging from the application
The driver transports bytes, but it’s the application that decides the shape. Encourage your applications to emit JSON or key-value pairs to stdout. Structured logs are easier to parse, index, and query in downstream systems. For example, a Node.js service might log:
console.log(JSON.stringify({
level: "info",
message: "User login successful",
userId: "abc123",
timestamp: new Date().toISOString()
}));
2. Use a centralized logging driver in production
Avoid relying on local files in multi-host clusters. Use a driver that ships logs directly to a centralized store (like fluentd, gelf, splunk, awslogs, or gcplogs). This ensures logs survive container deletion and are accessible across the entire fleet. If you must keep local files, pair them with a log shipping agent like Filebeat, but the Docker driver approach reduces moving parts.
3. Always enforce log rotation or retention limits
For any driver that writes to disk (especially json-file), set max-size and max-file. For drivers that send logs over the network, configure buffer limits and retry policies to avoid backpressure blocking the container. For example, with fluentd you might use fluentd-async and a short fluentd-retry-wait.
4. Tag logs with container metadata
Most drivers support Go templates in the tag option. Include {{.Name}}, {{.ID}}, or custom labels to identify the source container. This is critical when logs from hundreds of containers flow into a single stream.
--log-opt tag="docker.{{.Name}}.{{.ImageName}}"
5. Do not log sensitive data to stdout/stderr
Logs captured by the driver are transmitted and often stored with minimal access control. Treat container stdout as a low-trust channel. Strip secrets, tokens, and personally identifiable information before writing.
6. Monitor the logging pipeline
Whether you use a cloud service or a self-hosted Fluentd, monitor its health. A stalled log collector can cause containers to block on log writes (depending on driver delivery mode). Set alerts for dropped messages, buffer fullness, and delivery latency.
7. Match the driver to your infrastructure
- Cloud VMs –
awslogs(AWS),gcplogs(GCP),logentries, orsplunkif already adopted. - On-premise Kubernetes – often
json-filewith a node-level logging agent, but you can also usefluentdorjournald. - Systemd-based hosts –
journaldintegrates nicely withjournalctl. - Legacy syslog infrastructure –
syslogdriver.
8. Test driver behaviour under failure
Some drivers block container stdout when the remote endpoint is unreachable (synchronous mode). Others drop logs or buffer them. Read the driver’s delivery semantics and test what happens when the collector is stopped. This directly influences whether a network blip can cause application hangs.
Common Pitfalls and How to Avoid Them
Pitfall 1: Running out of disk space with default json-file
Symptom: Docker daemon becomes unresponsive, containers fail to start, or the host partition reaches 100%. The culprit is often unbounded JSON log files. Fix: Always set max-size and max-file. For existing containers, you can add a global default and then recreate them, or use a log cleanup script as a temporary measure.
Pitfall 2: Dual logging (stdout and file) inside the container
Developers sometimes run an internal log file plus write to stdout. This duplicates disk usage and can cause confusion about which log stream is authoritative. Recommendation: Choose one: either log only to stdout/stderr and rely on Docker’s logging driver, or use a volume-mounted log file and disable Docker logging for that container (--log-driver none). Mixing them without coordination leads to split brain in debugging.
Pitfall 3: Ignoring delivery guarantees
The fluentd driver, for example, can be set to synchronous or asynchronous mode. In synchronous mode (default), if the Fluentd endpoint is down, the container’s write to stdout may block indefinitely. This can look like an application hang. Fix: Understand the mode. Use fluentd-async if you accept dropping logs during an outage, and combine it with fluentd-retry-wait and fluentd-max-retries.
--log-opt fluentd-async=true
--log-opt fluentd-retry-wait=1s
--log-opt fluentd-max-retries=10
Pitfall 4: Overlooking container log level filtering
Some drivers support log level filtering via --log-opt (e.g., gelf has gelf-log-level). Not setting this can flood your log system with debug or trace messages. Configure the driver to match your environment’s verbosity.
Pitfall 5: Running a logging sidecar that competes with the driver
A common pattern is to run a Fluentd or Filebeat container that tails the host’s Docker log files. If you also configure the Docker daemon to use fluentd driver, you end up with duplicated log streams and potential loops. Fix: Choose one path: either use the Docker driver to push directly, or use json-file with rotation and a separate shipper that reads those files. Do not mix both unless you fully understand the topology.
Pitfall 6: Forgetting to update global defaults after a daemon restart
Modifying daemon.json only affects containers created after the Docker service reloads. Running containers keep the driver they were started with. After changing the default, plan to recreate long-lived containers or set the driver explicitly in their orchestration config.
Pitfall 7: Using a driver that is not supported on your OS
Some drivers, like etwlogs, only work on Windows. Others, like journald, are Linux-specific. Always verify driver availability with docker info --format '{{json .LoggingDrivers}}' on the target host.
Conclusion
Docker logging drivers are a powerful abstraction that decouples container output from the log storage and transport layer. By moving beyond the default json-file without rotation, you prevent disk exhaustion, improve performance, and integrate seamlessly with centralised logging platforms. The key takeaways are: always enforce log limits, prefer structured output, select a driver that aligns with your infrastructure, and thoroughly test failure modes. With these practices in place, container logs become a reliable pillar of your observability strategy, rather than a silent operational hazard.