Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Docker Logging Drivers

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.

Docker Logging Driver PipelineContainerstdout / stderrDockerdaemon capturejson-filelocal disksyslogrsyslog hostGELF / Fluentdcentral stackawslogsCloudWatchOne driver active per container — chosen at create time
Docker logging drivers receive captured stdout/stderr and route logs to local files or remote collectors

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"
Logging Configuration Layers/etc/docker/daemon.jsonGlobal log-driver + log-opts for all new containersdocker run --log-driver / --log-optCLI override at container create timeCompose logging: driver + optionsDeclarative per-service override in YAMLPer-container settings win over daemon defaults
Configure Docker logging drivers globally in daemon.json or override per container in Compose

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.

DriverBest ForProsCons
json-fileDefault local dev, small VPSWorks with docker logs, no extra infraFills disk without max-size
localSame as json-file, better perfRing-buffer style, lower overheadNot available on very old Docker builds
journaldsystemd-native Linux hostsIntegrates with journalctlHarder to ship off-host without forwarding
syslogExisting rsyslog/syslog-ngFamiliar ops toolingUDP loss, TLS setup can be fiddly
gelfGraylog, some ELK setupsStructured fields, UDP/TCPNeeds reachable GELF input
fluentdFluent Bit / Fluentd pipelinesFlexible routing and parsingExtra agent container or host service
awslogsAWS ECS / EC2 with CloudWatchNative AWS integrationAWS-only, IAM wiring required
noneBatch jobs, sensitive temp dataZero log I/O overheadNo 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.

Local vs Centralized Loggingjson-file / local• docker logs works• Needs max-size set• Good for dev / small VPS• Rs 1,500/mo host (~USD 11)• Single-server grepLow ops overheadGELF / Fluentd / awslogs• Search across all hosts• Retention policies• Alerts on error spikes• Needs collector infra• Better for 3+ servicesProduction standardMany teams use json-file locally and Fluentd in staging/production
Choose Docker logging drivers based on host count, retention needs, and available ops tooling

What Production Mistakes Break Docker Logging?

Developers write code. Operators inherit logs. These failures recur across client environments regardless of framework.

Logging Driver Gotchas

  1. Recreating containers without rotation. Fresh Compose files omit the logging block. Defaults apply. Disk grows quietly for weeks.
  2. Using none to "save performance". You lose audit trails. Payment gateway debug on a production Laravel app becomes guesswork.
  3. Structured apps logging JSON inside JSON. Double-encoded lines break grep and parsers. Log plain text or use a driver that preserves structure natively.
  4. Remote driver unreachable at start. Some drivers retry; others delay container start. Test failover before cutover.
  5. 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.

Common Logging FailuresDisk FullSet log retention limitsjson-file fills disk fastLost LogsRemote driver downUse local buffer driverParse ErrorsJSON inside JSON linesLog plain text insteadNo RotationMissing max-size optsAdd daemon.json defaultsFix Patterndaemon.json defaults + Compose override + central shipMonitor disk weekly on every Docker host
Avoid Docker logging driver failures with retention limits, health checks on collectors, and plain-text log lines

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-size and max-file in 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 logs shows 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

A pluggable backend that receives container stdout and stderr from the Docker daemon and writes them to json-file, syslog, journald, GELF, Fluentd, AWS CloudWatch, or other sinks.

json-file. It stores each line as JSON under /var/lib/docker/containers/, and docker logs reads those files through the Docker API. Docker Desktop on macOS and Windows also defaults to json-file, with rotation enabled in recent releases.

Every container process writes to stdout and stderr. Docker captures that stream before it reaches your terminal, then hands the bytes to whichever driver is active for that container. Think of the driver as a post office: your app writes a letter, Docker picks it up, and the driver decides whether it lands in a local JSON file, systemd journal, Fluentd collector, or Amazon CloudWatch. Remote drivers still buffer briefly on the host, and network blips can cause backpressure that blocks your app if the driver cannot flush.

Set a global default in /etc/docker/daemon.json with log-driver and log-opts, then restart Docker with systemctl. Existing containers keep their original driver until recreated, so run docker compose up -d --force-recreate after changing daemon defaults. Override per container at creation time with docker run --log-driver and --log-opt flags, or in Compose with a logging block under each service. Verify the active driver on a running container with docker inspect --format for LogConfig.Type and LogConfig.Config.

Driver choice depends on where logs must end up and who reads them. Use json-file or local with max-size and max-file rotation for dev and small VPS hosts where docker logs is enough. Choose journald on systemd-native Linux hosts already managed through journalctl. Use syslog if you already run rsyslog or syslog-ng. Ship to Graylog or ELK with GELF, or to Fluent Bit or Fluentd pipelines with the fluentd driver. Use awslogs on AWS ECS or EC2 with CloudWatch. Use none only for batch jobs or sensitive temp data where you accept having no logs at all.

Under /var/lib/docker/containers//. Each line is a JSON object with fields like log, stream, and time. The docker logs command reads these files through the Docker API rather than opening them directly. Check current log file size on disk with sudo du -sh /var/lib/docker/containers//-json.log. Without max-size and max-file caps, these files grow until they fill the host partition, which is the most common Docker logging failure on small VPS and EC2 instances.

No. The logging driver is fixed at container creation time. Stop the container, remove it, and recreate it with new --log-driver flags or an updated Compose logging block.

Set log-opts on the json-file or local driver. max-size caps each log file, for example 10m. max-file sets how many rotated files to keep, for example 5. Together those settings keep roughly 50 MB per container. Add compress: true to gzip rotated files and save more disk. Apply rotation globally in daemon.json or per service in Compose. Pair log caps with Docker container resource limits and disk monitoring in the same operational setup, because unchecked json-file output remains the most common Docker host failure I see on production stacks.

Inspect the running container with docker inspect --format '{{.HostConfig.LogConfig.Type}}' mycontainer to see the active driver name. For the full option set, run docker inspect --format '{{json .HostConfig.LogConfig.Config}}' mycontainer. Do this after deploys and after changing daemon.json defaults, because existing containers keep their original driver until recreated. Fresh Compose files that omit the logging block silently inherit defaults, which is a common reason rotation never gets applied on new services.

Yes, under heavy output. Synchronous writes to slow disks or unreachable remote collectors create backpressure. Apps blocking on stdout is rare but possible when the driver cannot flush. Use appropriate log levels in production, cap verbosity, and prefer remote drivers with local buffering agents like Fluent Bit on the host or as a sidecar. A debug flag left on in a WooCommerce plugin can write megabytes per hour across six containers, so rotation and log level discipline matter as much as driver choice.

journald sends logs to the systemd journal instead of Docker-managed JSON files under /var/lib/docker/containers/. Read them with journalctl CONTAINER_NAME=.... journald fits hosts already managed through systemd timers and journal-based monitoring, but shipping logs off-host requires extra forwarding. json-file fits teams that rely primarily on docker logs and simple file-based tooling. Both are local drivers; neither replaces a centralized stack when you run three or more services across multiple machines.

Laravel logs through Monolog. By default it writes to storage/logs/laravel.log inside the container filesystem, which is separate from stdout and disappears when the container is replaced. Set LOG_CHANNEL=stderr and configure the stderr channel in config/logging.php to send output to php://stderr. Then one docker logs -f tail shows request errors and queue failures together. On production stacks I run with Laravel 12 or 13 on PHP 8.3+, this merge makes Docker logging drivers actually useful for debugging payment callbacks and queue workers without exec-ing into the container to read files.

Some drivers retry; others delay container start. Network blips can cause backpressure even after startup, because remote drivers still buffer briefly on the host before shipping. Your app may block on a full pipe if the driver cannot flush. Test failover before cutover. Run Fluent Bit or Fluentd on the host or as a sidecar alongside your app stack, and confirm the collector is healthy. Health checks tell you a container is alive; they do not inspect log pipeline health.

Recreating containers without a logging block so defaults apply and disk grows quietly for weeks. Using the none driver to save performance and losing audit trails when you need payment gateway debug output. Double-encoding JSON inside JSON lines, which breaks grep and parsers. Forgetting log volume on GitLab Runner Docker executors, where json-file logs accumulate on the runner host. Mixing noisy queue worker output with checkout errors because tags and service names were not separated. Fix these with retention limits, plain-text or parser-friendly log lines, health checks on collectors, and explicit tag naming in Compose.

No. Only one driver runs per container. You cannot split stdout to json-file and Fluentd simultaneously without a sidecar or application-level duplication. For centralized logging, pick the driver that matches your collector: GELF for Graylog, fluentd for Fluent Bit or Fluentd pipelines, or json-file plus Filebeat as an equally valid alternative. Tag naming matters when you filter in Kibana or Grafana Loki later, especially when web request logs and queue worker logs must stay separate by service name on a multi-container Compose stack.

Share this article

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.

Quick Contact Options
Choose how you want to connect me: