
September 10, 2026
11 min read
By Kokil Thapa | Last reviewed: September 2026
Docker logging drivers decide where every line your container prints actually lands. stdout and stderr leave the app, hit the Docker daemon, and get forwarded by the active driver. That path sounds trivial until a Laravel queue worker fills a VPS disk at 3 a.m. On production stacks I maintain with Docker Compose multi-container setups, logging is not an afterthought. It is part of how you debug deploys, trace payment callbacks, and keep a host alive.
What Are Docker Logging Drivers and How Do They Work?
Every container process writes to file descriptors 1 and 2. Docker captures that stream before it ever reaches your terminal. The daemon hands bytes to whichever logging driver is active for that container.
Think of the driver as a post office. Your app writes a letter. Docker picks it up. The driver decides whether it goes to a local JSON file, systemd journal, a Fluentd collector, or Amazon CloudWatch.
On a typical Ubuntu host running PHP-FPM apps in containers, I still see teams treat docker logs as infinite storage. It is not. The default json-file driver appends to a file on the host filesystem unless you cap it.
The official Docker documentation lists every built-in driver and its options. That page is the source of truth when you verify option names before a deploy.
Only one driver runs per container. You cannot split stdout to json-file and Fluentd simultaneously without a sidecar or application-level duplication.
What Docker Actually Stores Locally
With json-file, logs live under /var/lib/docker/containers/<container-id>/. Each line is a JSON object with fields like log, stream, and time. The docker logs command reads these files through the API.
Remote drivers still buffer briefly on the host. Network blips can cause backpressure. Your app may block on a full pipe if the driver cannot flush.
How Do You Configure Docker Logging Drivers?
Configuration happens at two levels. The daemon sets a global default. Individual containers can override it at creation time.
For hosts I manage under Linux system administration contracts, I set sane defaults in daemon.json first. Then I override only when a workload needs centralized shipping.
Global Default in daemon.json
Edit /etc/docker/daemon.json on the host. Restart Docker after changes. A typical json-file setup with rotation looks like this:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "5",
"compress": "true"
}
}
Apply the change:
sudo systemctl restart docker
Existing containers keep their original driver until recreated. Run docker compose up -d --force-recreate after changing daemon defaults.
Per-Container Override
Pass flags at docker run time:
docker run -d \
--name api \
--log-driver=json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
myapp:latest
In Compose, use the logging key:
services:
web:
image: nginx:alpine
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
compress: "true"
Verify the active driver on a running container:
docker inspect --format '{{.HostConfig.LogConfig.Type}}' mycontainer
docker inspect --format '{{json .HostConfig.LogConfig.Config}}' mycontainer
If you are new to the host setup itself, start with the Install Docker on Ubuntu guide before tuning drivers.
Which Docker Logging Driver Should You Choose?
Driver choice depends on where logs must end up and who reads them. Local dev wants simplicity. Production wants rotation or central aggregation.
The table below covers drivers I see most often on real projects. Niche drivers like splunk or gcplogs follow the same pattern: point at an endpoint and pass credentials.
| Driver | Best For | Pros | Cons |
|---|---|---|---|
json-file | Default local dev, small VPS | Works with docker logs, no extra infra | Fills disk without max-size |
local | Same as json-file, better perf | Ring-buffer style, lower overhead | Not available on very old Docker builds |
journald | systemd-native Linux hosts | Integrates with journalctl | Harder to ship off-host without forwarding |
syslog | Existing rsyslog/syslog-ng | Familiar ops tooling | UDP loss, TLS setup can be fiddly |
gelf | Graylog, some ELK setups | Structured fields, UDP/TCP | Needs reachable GELF input |
fluentd | Fluent Bit / Fluentd pipelines | Flexible routing and parsing | Extra agent container or host service |
awslogs | AWS ECS / EC2 with CloudWatch | Native AWS integration | AWS-only, IAM wiring required |
none | Batch jobs, sensitive temp data | Zero log I/O overhead | No logs at all — debug nightmare |
For a centralized stack, pair the right driver with the pipeline described in centralized logging with the ELK stack. GELF or Fluentd feeds Logstash or OpenSearch. json-file plus Filebeat is equally valid.
Example: GELF to Graylog
services:
app:
image: mylaravel:latest
logging:
driver: gelf
options:
gelf-address: "udp://graylog.internal:12201"
tag: "laravel-app"
Example: Fluentd Forward
services:
worker:
image: myworker:latest
logging:
driver: fluentd
options:
fluentd-address: "localhost:24224"
tag: "worker.{{.Name}}"
Run Fluent Bit or Fluentd on the host or as a sidecar alongside your app stack. Tag naming matters when you filter in Kibana or Grafana Loki later.
How Do You Rotate and Limit Docker Container Logs?
Unchecked json-file logs destroyed more than one small EC2 instance I inherited. A debug flag left on in a WooCommerce plugin can write megabytes per hour. Multiply that by six containers and you get a full root partition.
The fix is explicit rotation via log-opts. These two options are non-negotiable on production hosts:
max-size— cap each log file (example:10m)max-file— number of rotated files to keep (example:5)
With those settings, Docker keeps roughly 50 MB per container. Add compress: "true" to gzip rotated files and save more disk.
Check current log file size on disk:
sudo du -sh /var/lib/docker/containers/*/*-json.log
Pair log caps with limiting Docker container resources. Disk, CPU, and memory limits belong in the same Compose file. Treat them as one operational unit.
What Production Mistakes Break Docker Logging?
Developers write code. Operators inherit logs. These failures recur across client environments regardless of framework.
Logging Driver Gotchas
- Recreating containers without rotation. Fresh Compose files omit the logging block. Defaults apply. Disk grows quietly for weeks.
- Using
noneto "save performance". You lose audit trails. Payment gateway debug on a production Laravel app becomes guesswork. - Structured apps logging JSON inside JSON. Double-encoded lines break grep and parsers. Log plain text or use a driver that preserves structure natively.
- Remote driver unreachable at start. Some drivers retry; others delay container start. Test failover before cutover.
- Forgetting log volume in CI. GitLab Runner Docker executors accumulate json-file logs on the runner host. Schedule pruning.
On a booking platform like Adventure Third Pole Trek, queue worker logs and web request logs must stay separate by tag or service name. One noisy worker should not bury checkout errors.
Validate JSON log lines during development with a JSON formatter if your app emits structured events. Make sure the outer Docker wrapper and inner payload stay parser-friendly.
Health checks tell you a container is alive. They do not inspect log pipeline health. Read Docker healthchecks explained alongside logging setup. A healthy container can still drown in its own output.
How Do Docker Logging Drivers Fit Laravel and PHP Stacks?
Most of my production work runs Laravel 12 or 13 on PHP 8.3+. Local dev often uses Laravel Sail and Docker. Sail containers inherit json-file like any other image.
Laravel logs through Monolog. By default it writes to storage/logs/laravel.log inside the container filesystem. That file is separate from stdout. Many teams set LOG_CHANNEL=stderr in Docker so application logs merge into the Docker logging driver stream.
LOG_CHANNEL=stderr
LOG_LEVEL=debug
In config/logging.php, the stderr channel sends everything to php://stderr. One docker logs -f tail shows request errors and queue failures together.
For PostgreSQL sidecars in dev, the database container gets the same rotation policy as the app. See run PostgreSQL in Docker for development for a full Compose pattern.
When apps move from Docker Compose on a single VPS to orchestrated clusters, logging strategy moves too. The Kubernetes vs Docker Swarm comparison covers how log collection differs at scale. Drivers change names; the need for central search does not.
If you evaluate alternatives, Podman vs Docker migration notes that Podman supports compatible logging drivers through containers.conf. Config syntax differs slightly; concepts transfer.
Twelve-Factor Alignment
Treat logs as event streams. Do not treat container filesystem logs as durable storage. Use volumes for data that must survive restarts. Application log files on ephemeral layers disappear when containers are replaced.
For ongoing ops after launch, support and maintenance should include log review playbooks. Know where logs go before an incident, not during one.
Key Takeaways
- Docker logging drivers capture stdout/stderr and write to json-file, syslog, journald, GELF, Fluentd, awslogs, or other backends — one driver per container.
- Set
max-sizeandmax-filein daemon.json globally; override per service in Compose for production safety. - Use json-file or local for dev and small VPS hosts; ship logs with GELF or Fluentd when you run three or more services across multiple machines.
- Point Laravel and PHP apps at stderr in Docker so
docker logsshows application errors without reading files inside the container. - Test remote logging endpoints before cutover; an unreachable Fluentd address can block or delay container starts.
- Pair logging config with disk monitoring and resource limits — unchecked json-file output remains the most common Docker host failure I see.
People Also Ask
What is the default Docker logging driver?
The default driver is json-file on Linux. It stores each log line as JSON under /var/lib/docker/containers/. The docker logs command reads from these files. Docker Desktop on macOS and Windows also defaults to json-file with rotation enabled in recent releases.
Can you change the logging driver on a running container?
No. The logging driver is fixed at container creation time. You must stop the container, remove it, and recreate it with new --log-driver flags or an updated Compose logging block. Existing log files on disk remain until manually deleted or rotated out.
Does Docker logging affect application performance?
Yes, under heavy output. Synchronous writes to slow disks or unreachable remote collectors create backpressure. Apps blocking on stdout is rare but possible. Use appropriate log levels in production. Cap verbosity and prefer remote drivers with local buffering agents like Fluent Bit.
How is journald different from json-file?
journald sends logs to the systemd journal instead of Docker-managed JSON files. Use journalctl CONTAINER_NAME=... to read them. It fits hosts already managed through systemd timers and journal-based monitoring. json-file fits teams that rely primarily on docker logs and simple file-based tooling.
Ship Logging Before You Need It at 2 a.m.
Docker logging drivers are cheap insurance. Ten lines in daemon.json and a logging block in Compose prevent disk emergencies and make production debugging possible. Start with json-file rotation everywhere. Add Fluentd or GELF when your stack outgrows one server.
If you want help wiring Compose logging for a Laravel app, reviewing a full Docker host, or connecting containers to a central log stack, see enterprise application development or reach out via contact us. Good logs do not impress clients until the day everything breaks — then nothing else matters.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

