✦ For everyone, free.

Practical knowledge for real and everyday life

Home

19.2.4.1 Logs Container Output

A focused guide to Logs Container Output, connecting core concepts with practical Docker and container operations.

Container output refers to everything a containerized process writes to its standard output (stdout) and standard error (stderr) streams. Docker automatically captures this output and stores it through the container's configured logging driver. The docker logs command reads and displays this captured output. Understanding how container output is captured, stored, and retrieved is essential for monitoring application behavior, debugging failures, and analyzing runtime activity.

How Docker Captures Output

When a container runs, Docker intercepts the stdout and stderr file descriptors of PID 1 (the container's primary process). Any data written to these descriptors is captured by the Docker daemon and passed to the configured logging driver. This capture happens at the kernel level — the container process writes normally to its stdout/stderr, and Docker handles the rest transparently.

The captured data is stored in log files (for the default json-file driver) or forwarded to external systems. Each log entry is stored with the stream identifier (stdout or stderr), a timestamp, and the content of the line.

What Appears as Container Output

Everything the containerized application writes to stdout or stderr becomes container output. This includes:

  • Application startup messages and initialization logs.
  • HTTP request/response logs.
  • Error messages, warnings, and stack traces.
  • Health check output (if health checks write to stdout).
  • Informational status messages at regular intervals.

What does NOT appear in docker logs:

  • Files written inside the container's filesystem (application log files written to a path like /app/logs/app.log).
  • Data written only to an in-memory buffer that is never flushed to the file descriptor.
  • Output from processes that are not PID 1 and write to a file path instead of stdout/stderr.

For docker logs to capture application output, the application must write to stdout/stderr directly rather than only to files.

Redirecting Application Logs to Docker's Log System

A common pattern is to make an application's logs available through docker logs by having the container write to stdout/stderr, which in many frameworks means:

  • Configuring the application logger to output to stdout instead of a file.
  • Or using a symbolic link: ln -sf /dev/stdout /app/logs/app.log

The official nginx image uses exactly this approach in its Dockerfile:

RUN ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log

This causes nginx's log output to flow through Docker's logging system.

Viewing Container Output

docker logs my-container

Outputs all captured stdout and stderr since container start. Both streams appear interleaved by default.

Follow in real time
docker logs -f my-container
Show the last 100 lines
docker logs --tail 100 my-container
With timestamps
docker logs -t my-container
2024-06-15T10:32:45.123456789Z Server started on port 8080
2024-06-15T10:32:46.456789012Z Connected to database
2024-06-15T10:33:01.001234567Z GET /health 200 2ms

stdout vs. stderr in Output

Docker stores stdout and stderr separately but presents them merged by default in docker logs. Each log entry in the underlying storage includes a stream label (stdout or stderr).

When redirecting with shell redirection, they can be separated:

# Capture stdout only (stderr goes to terminal)
docker logs my-container 2>/dev/null

# Capture stderr only (stdout discarded)
docker logs my-container 2>&1 >/dev/null

# Save both to separate files
docker logs my-container >stdout.log 2>stderr.log

Buffering and Real-Time Output

Some applications buffer their stdout output for performance. When running in a non-TTY context (as all detached containers do), many language runtimes and C standard library programs default to block-buffering rather than line-buffering. This means output is held in memory and only written in large chunks — causing it to appear in docker logs with a delay, or not at all before the container exits.

Common solutions:

Python: Disable buffering with the -u flag or PYTHONUNBUFFERED environment variable:

ENV PYTHONUNBUFFERED=1

Or:

docker run -e PYTHONUNBUFFERED=1 python:3.12 python3 script.py

Node.js: Node.js stdout is already unbuffered when not connected to a TTY; stderr is always unbuffered.

General (C programs): Set the --init flag so that stdout flushing is handled correctly, or call fflush(stdout) in the application code.

Log Volume and Storage

By default, Docker stores container output in JSON-formatted files on disk with no size limit. On a high-traffic container writing thousands of lines per second, log files can grow rapidly and fill the disk.

Configure limits when starting the container:

docker run -d \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  my-container

This caps each log file at 10 MB and keeps at most 3 rotated files per container (30 MB maximum).

Check current log file location on the host:

docker inspect --format '{{.LogPath}}' my-container

The path is typically:

/var/lib/docker/containers/<container-id>/<container-id>-json.log

Container Output and the json-file Driver

The default json-file driver stores each log line as a JSON object:

{"log":"Server started on port 8080\n","stream":"stdout","time":"2024-06-15T10:32:45.123456789Z"}
{"log":"Error: connection refused\n","stream":"stderr","time":"2024-06-15T10:32:46.456789012Z"}

docker logs reads these files, parses the JSON, and presents the log content with optional timestamps. The --details flag on docker logs exposes additional fields the logging driver may include.