Metrics
docker stats shows CPU, memory usage/limit, network I/O, block I/O and PID count per container in real time. The numbers come from Linux cgroups (control groups), the kernel mechanism that limits and measures resources for groups of processes.
docker stats # streams continuously, Ctrl+C to quit
docker stats --no-stream # one snapshot then exit (good for scripts)
Logging
docker logs shows what the main process writes to stdout/stderr. If the app writes to a file inside the container, docker logs does not see it; official images usually link log files to /dev/stdout.
flowchart LR APP["Main process<br/>stdout / stderr"] --> DRV["Logging driver"] DRV --> J["json-file (default)"] DRV --> L["local"] DRV --> F["fluentd / syslog / ..."]
The default driver is json-file, with no size limit: logs can fill the disk. Set rotation in /etc/docker/daemon.json:
{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }
daemon.json settings only apply to newly created containers; existing ones must be recreated. Change the driver for one container with --log-driver and --log-opt. With --log-driver none, docker logs has nothing to read.
| Command | Meaning |
|---|---|
docker stats --no-stream |
One snapshot of metrics for all running containers |
docker logs -f --tail 50 web |
Last 50 lines, then keep following |
docker inspect -f '{{.HostConfig.LogConfig.Type}}' web |
The container's logging driver |
Beyond docker stats
docker stats is live only; nothing is stored. For history and alerts, a common open-source stack is:
| Component | Role |
|---|---|
| cAdvisor | Exposes per-container CPU, memory, network, and disk metrics |
| node-exporter | Exposes host metrics |
| Prometheus | Scrapes and stores metrics, evaluates alert rules |
| Grafana | Dashboards on top of Prometheus |
The Docker daemon can also expose its own metrics in Prometheus format with "metrics-addr": "127.0.0.1:9323" in daemon.json.
Limits make metrics meaningful
A container without limits can use the whole host. Set them with --memory and --cpus, or in Compose:
services:
api:
deploy:
resources:
limits: { cpus: "1.0", memory: 512M }
docker stats then shows memory against the limit, and a container that exceeds its memory limit is killed with exit code 137.
Choosing a logging driver
| Driver | Where logs go | docker logs works? |
|---|---|---|
json-file, local |
Files on the host | Yes |
journald |
systemd journal (journalctl CONTAINER_NAME=web) |
Yes |
syslog, fluentd, gelf |
Central log servers | Through dual logging cache |
awslogs, gcplogs |
Cloud logging services | Through dual logging cache |
Recent Docker versions keep a local cache (dual logging) so docker logs still works with remote drivers.
Making logs useful
Write structured logs, for example one JSON object per line with level, timestamp, and request ID, so central tools such as the ELK stack, Grafana Loki, or a cloud service can filter them. docker logs --since 10m --timestamps web narrows the window while debugging.
Events
docker events streams what the daemon does: containers starting, dying, being killed for memory, health status changes. Filtering with --filter event=die is a quick way to spot crash loops.