Skip to main content

Docker Logging Driver

The mechanism Docker uses to capture and route container stdout and stderr output. The default json-file driver writes to the host filesystem — production systems typically use awslogs, fluentd, or splunk drivers to ship logs to centralised aggregation systems.

What a logging driver does

Every container writes its output to stdout and stderr. The logging driver decides where that output goes: a local file, a cloud service such as CloudWatch, a log aggregator, or nowhere at all.

The default driver, json-file, stores logs under /var/lib/docker/containers/ on the host. It does not rotate logs by default, so a busy container can slowly fill the disk.

%%{init: {'themeVariables': {'fontSize': '18px'}, 'flowchart': {'nodeSpacing': 40, 'rankSpacing': 45, 'padding': 14}}}%% flowchart TD A["Container
stdout and stderr"] --> B["Logging driver"] B --> C["Host disk
json-file, local"] B --> D["Remote system
awslogs, fluentd, splunk"] B --> E["Discarded
none"] class A,C,D,E base class B key classDef base fill:#f8fafc,stroke:#94a3b8,stroke-width:1.5px,color:#0f172a classDef key fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#0f172a

Common drivers

Driver Description Typical use
json-file Default. JSON files on the host Always configure rotation
local Compact, rotated-by-default binary format Better performance than json-file
awslogs AWS CloudWatch Logs AWS-based infrastructure
fluentd Forwards to a Fluentd collector EFK and similar stacks
journald systemd journal Linux hosts using systemd
splunk Splunk HTTP Event Collector Enterprise Splunk setups
none Discards all logs Batch jobs that need no logs

Configuring log rotation

Set rotation in /etc/docker/daemon.json so it applies to every new container. This caps each container at 3 files of 100MB, or 300MB in total:

JSON
{
"log-driver": "json-file",
"log-opts": { "max-size": "100m", "max-file": "3" }
}

Restart the daemon to apply it. Existing containers keep their old settings until they are recreated.

Bash
sudo systemctl restart docker
# Per-container override
docker run -d \
--log-driver json-file \
--log-opt max-size=200m \
--log-opt max-file=5 \
payment-api:latest

Shipping logs to CloudWatch

Bash
docker run -d \
--log-driver awslogs \
--log-opt awslogs-region=ap-south-1 \
--log-opt awslogs-group=/production/payment-api \
--log-opt mode=non-blocking \
registry.example.com/payment-api:v3.1.0

mode=non-blocking prevents the application from stalling if the log endpoint is slow or unreachable, at the cost of possibly dropping messages when the buffer fills.

Using docker logs

docker logs always works with json-file and local. Since Docker 20.10, remote drivers such as awslogs and fluentd also keep a small local cache, so docker logs generally works with them too. The cache is for convenience only; the remote system remains the source of truth.

Remember

Configure rotation before running production workloads. Without it, json-file logs grow without limit. With max-size: 100m and max-file: 3, each container is capped at 300MB.

Frequently Asked Questions

Why does the default json-file logging driver become a problem in production?

json-file writes every line of container stdout/stderr to a file on the host disk with no rotation configured out of the box, so a chatty service can silently fill the disk over weeks, eventually taking down every container on that host, not just the noisy one. It also means logs live and die with the host. If you replace the instance, the logs go with it, which defeats centralized debugging across an autoscaled fleet.

How should logging drivers actually be configured for production containers?

Two common approaches: set `max-size` and `max-file` on json-file to bound disk usage while still writing locally (good when a separate agent like Filebeat tails the files), or switch the driver itself to something like `awslogs`, `gelf`, or `fluentd` to ship logs directly off-host. A frequent mistake is using a driver such as `awslogs` in blocking mode without also setting `mode=non-blocking`, which can stall container writes if the logging endpoint is slow or unreachable.