Logs, Logging Drivers, Rotation, stdout/stderr Contracts, Events, stats, and Container Observability: Diagnostics, Failure Modes, Security, and Performance
Diagnose Docker log growth, missing remote delivery, writable-layer logs, secret leakage, stale driver configuration, and backpressure from preserved evidence.
Learning objectives
- Diagnose disk growth, missing logs, dropped remote delivery, secret leakage, and stale logging configuration from first-failure evidence.
- Distinguish application silence from driver/sink failure, daemon failure, and container/process lifecycle events.
- Explain why changing the daemon default does not retrofit existing containers.
- Apply the smallest safe correction without destructive cleanup, blanket daemon changes, or exposure of secrets.
1. Evidence-first diagnostic sequence
- Record context, Engine/CLI versions, host/Desktop boundary, and time.
- Record container ID/name, exact image identity, state, exit code/OOM flag, and logging driver/options.
- Capture a bounded application-log window and Engine events before history rolls over.
- Capture a stats sample if the container is still running; preserve resource configuration if it has exited.
- Inspect daemon logs for driver, filesystem, transport, or Engine errors.
- For remote logging, prove external sink receipt independently without exposing credentials.
- Apply one least-destructive correction and rerun the smallest reproduction.
docker context show
docker version
docker inspect dkr23-app --format '{{json .State}} {{json .HostConfig.LogConfig}}'
docker logs --timestamps --since 10m --tail 500 dkr23-app 2>&1
docker events --since 10m --filter container=dkr23-app --format '{{json .}}'
docker stats --no-stream --format '{{json .}}' dkr23-app 2>/dev/null || true
2. Failure: unbounded json-file growth
Symptom: daemon-host disk pressure rises while
application behavior is otherwise normal.
Cause: high-volume
json-file containers with no rotation can accumulate
data indefinitely until external capacity limits intervene.
Preserve: exact container driver/options, host disk
usage trend, daemon warnings, container output rate, and retention
requirements. Correction: for future/recreated
containers, choose local or configure explicit
json-file rotation. Do not delete Docker-managed log
files by hand.
3. Failure: critical logs exist only inside the writable layer
If the application writes only /app/logs/app.log and
the container is replaced, operational evidence can disappear with
its writable layer. The correction is not “docker cp the file every
incident.” Establish stdout/stderr or an explicit
persistent/collector contract, then rebuild/redeploy.
4. Failure: a fake credential leaks into logs
Use a synthetic value to understand the response without exposing a real secret. Once a real secret reaches logs, rotation does not guarantee immediate removal from every retained local/remote copy. Treat it as a credential incident according to the relevant secret-rotation and retention procedures.
docker rm -f dkr23-badlog 2>/dev/null || true
docker run --name dkr23-badlog --label academy=docker-ch23 alpine:3.22 sh -c 'echo "ERROR demo_auth=FAKE-ONLY-NOT-A-CREDENTIAL" >&2; exit 23' || true
docker logs --timestamps dkr23-badlog 2>&1 | tee dkr23-evidence/badlog-original.txt
docker inspect dkr23-badlog --format 'exit={{.State.ExitCode}} image={{.Image}} log={{json .HostConfig.LogConfig}}'
5. Failure: docker logs and the remote sink disagree
With a remote driver, docker logs can be served by dual
logging rather than by reading back from the remote system.
Therefore these states are possible: local recent logs exist but
remote delivery failed; remote logs exist but local cache was
disabled/rotated; both are missing because delivery/cache failed; or
the application emitted nothing.
Inspect the per-container driver/options, daemon logs,
network/TLS/provider errors, dual-cache configuration, and remote
receipt. Do not equate a successful docker logs command
with successful external transport.
6. Failure: changing the daemon default does not fix existing containers
Docker documentation explicitly states that daemon logging changes
apply to newly created containers. Existing containers retain the
logging configuration they were created with. A restart of the
existing container does not replace its
HostConfig.LogConfig.
# Read-only proof for two containers created at different times.
docker inspect OLD NEW --format '{{.Name}} {{json .HostConfig.LogConfig}}'
7. Failure: tuning without disk or latency evidence
Increasing buffers, retention, or remote retries can move pressure elsewhere. Larger local retention consumes disk; non-blocking buffers consume memory and can still drop; blocking delivery can stall the application; aggressive remote retry can amplify incidents. Measure output rate, disk headroom, sink latency/error rate, and application tolerance first.
8. Causal map: do not troubleshoot the wrong layer
| Symptom | Likely evidence plane | Do not jump to |
|---|---|---|
| No app logs, container never started | Engine events / inspect / daemon logs | Remote sink tuning. |
| App emits, local logs exist, remote missing | Driver transport / daemon / external sink | Application restart first. |
| Disk fills on daemon host | File driver retention + host storage | Deleting arbitrary files in Docker data root. |
| Container latency rises during log outage | Blocking driver/backpressure path | CPU limits without evidence. |
| docker logs unavailable with remote driver | Dual-log/read capability/config | Assuming application emitted nothing. |
| Stats show rising PIDs but logs normal | Runtime/resource plane | Changing logging driver. |
Knowledge check
Why should you not delete json-file/local driver files manually during disk pressure?
They are Docker-daemon-owned implementation files. Preserve evidence and correct retention/configuration; manual deletion can interfere with the logging system.
You changed daemon.json log-driver and restarted Docker, but an old container still uses json-file. Bug?
Not necessarily. Existing containers retain the logging configuration used when they were created; recreate the intended disposable workload after validating the change.
docker logs shows a line but the remote SIEM has none. What does that prove?
Only that the Engine can supply the line locally (possibly from dual logging). It does not prove remote delivery; inspect driver/daemon transport and the remote sink.
Why is a real secret in logs more serious than one file on disk?
Logs can be rotated, copied, cached, aggregated, backed up, and retained in multiple systems. Rotate/revoke the secret and handle each sink according to incident policy.
A high-volume app stalls when the remote logger is slow. Which design axis matters?
Blocking versus non-blocking log delivery, plus sink latency/capacity and the accepted tradeoff between backpressure and possible message loss.
Official references and version notes
- Docker Docs — Configure logging drivers — daemon/container driver selection, delivery modes, labels/tags, and current default-driver behavior.
- Docker Docs — Local file logging driver — default rotation/compression behavior and supported options.
- Docker Docs — JSON file logging driver — JSON framing and explicit rotation options.
-
Docker Docs — Dual logging
— how
docker logscan remain available with remote drivers and when it does not. -
Docker CLI — docker container logs
— timestamps,
--since,--until, follow, and tail behavior. - Docker CLI — docker system events — event scope, filters, JSON Lines formatting, and bounded event history.
- Docker CLI — docker container stats — CPU, memory, network, block I/O, PIDs, and Linux cache-reporting notes.
- Docker Docs — Read daemon logs — platform-specific locations and systemd/desktop guidance.
- Docker Engine 29 release notes — current Engine baseline and logging-related fixes/features.
Docker
Engine 29.8.1 is the current Engine baseline used for compatibility
notes. The daemon default logging driver remains
json-file; Docker recommends local for
general use because it rotates by default. The mandatory labs
configure logging per container so they do not require editing
daemon.json or restarting Docker. Always record
docker version, docker info, context, the
actual per-container HostConfig.LogConfig, and
platform-specific daemon-log location instead of assuming defaults.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.