Docker Monitoring and Logging: Stop Flying Blind in Production
Docker monitoring and logging guide for production.
20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.
- ✓Docker fundamentals (containers, images, docker-compose)
- ✓Basic Linux command line
- ✓Familiarity with Prometheus and Grafana concepts
Use Prometheus with cAdvisor for container metrics, Loki for log aggregation, and Grafana for dashboards. For quick debugging, start with docker stats and docker logs --tail 100 -f .
Imagine you're a landlord with 50 apartments (containers). You need to know which apartment is flooding (high memory), which has a party (CPU spike), and who's complaining (error logs). Docker monitoring is your smart meter panel; logging is your intercom system. Without them, you're knocking on doors randomly.
| Chrome | Firefox | Safari | Edge |
|---|---|---|---|
| ✓ | ✓ | ✓ | ✓ |
You've deployed your microservices on Docker. Everything's running. Then at 3 AM, the pager goes off: 'Payment service latency > 10s.' You SSH in, run docker stats, and see one container eating 4GB of RAM. But which container? What was it doing? You have no logs, no metrics, no history. You're blind. That's the problem Docker monitoring and logging solves. By the end of this article, you'll have a production-ready stack that tells you exactly what's happening inside every container, past and present, so you can fix issues before they become incidents.
Why Default Docker Logging Will Bite You
Docker's default logging driver is json-file. It writes logs to the container's writable layer as JSON files. Sounds fine until you realize those logs live inside the container's storage, not on the host. If the container dies, logs die with it. Worse, no rotation means a chatty container can fill your disk. I've seen a single container write 20GB of logs in an hour, taking down the entire host. The fix: use a logging driver that ships logs off the container. journald for systemd integration, syslog for centralized logging, or gelf for Graylog. But the gold standard for modern stacks is the local driver: it's fast, uses binary format, and supports compression. Or use fluentd/loki for cloud-native setups. Never rely on docker logs in production — it's for debugging, not monitoring.
Container Metrics: What to Collect and Why
Docker exposes raw container metrics via the Docker API and docker stats. But docker stats is a snapshot, not a history. For real monitoring, you need time-series data. The key metrics: CPU usage (as percentage of host), memory usage (RSS + cache), network I/O (bytes in/out), block I/O (read/write), and PIDs. Why PIDs? A fork bomb in a container can exhaust the host's PID limit. I've seen a misconfigured Node.js event loop spawn thousands of child processes, freezing the host. Collect these metrics every 15s using cAdvisor, which runs as a container and exposes Prometheus endpoints. Then ship to Prometheus. Don't forget to set resource limits — without them, one container can starve others.
image: gcr.io/cadvisor/cadvisor:latest and the same volume mounts. Pin the version to avoid breaking changes.Prometheus: The Backbone of Container Monitoring
Prometheus scrapes metrics from cAdvisor and other exporters at configurable intervals. It's pull-based, which means you don't need to install agents — just expose an endpoint. For Docker monitoring, you'll scrape cAdvisor (container metrics), node_exporter (host metrics), and your application's custom metrics. The critical configuration: scrape interval (15s default), retention (15d default), and storage (local TSDB). In production, set retention to 30 days and use persistent volumes. Don't forget alerting rules — Prometheus can evaluate rules and fire alerts to Alertmanager. I once saw a team miss a memory leak because they only alerted on CPU. Alert on memory usage > 80% and container restarts.
Grafana: Turning Metrics into Answers
Raw metrics are useless without visualization. Grafana connects to Prometheus and lets you build dashboards. For Docker monitoring, create a dashboard with: CPU usage per container (line chart), memory usage (bar chart), network I/O (area chart), and container restarts (stat panel). Use variables to filter by container name or host. The key: set up alerts in Grafana too — it can send notifications to Slack, PagerDuty, or email. Don't build one massive dashboard. Create separate dashboards for 'Container Overview', 'Host Health', and 'Application Performance'. I've seen teams spend weeks perfecting a dashboard that nobody uses. Start with the top 5 metrics that would have caught your last outage.
GF_SECURITY_ADMIN_USER and GF_SECURITY_ADMIN_PASSWORD. Also enable HTTPS in production.Log Aggregation with Loki: Logs as Cheap as Metrics
Loki is Grafana's log aggregation system designed for Kubernetes and Docker. Unlike Elasticsearch, Loki doesn't index the log content — it indexes only metadata (labels like container name, host). This makes it cheaper and faster for high-volume logs. Use Promtail (or Docker plugin) to ship logs to Loki. The Docker plugin is easiest: docker plugin install grafana/loki-docker-driver:latest --alias loki. Then set --log-driver=loki on containers. Query logs in Grafana with LogQL. Example: {container_name="payment-service"} |= "error". Loki is great for 'needle in a haystack' searches. But if you need full-text search on historical logs, Elasticsearch might be better. Trade-off: cost vs capability.
table_manager.retention_period: 720h (30 days) in config. Monitor disk usage — Loki compresses logs but can still grow fast.Structured Logging: The Missing Piece
Unstructured logs are a nightmare to query. 'Payment failed at 3:02' tells you nothing. Structured logging outputs JSON with fields like timestamp, level, service, trace_id, user_id, error. This lets you filter and aggregate in Loki. In Node.js, use pino or winston. In Python, structlog. In Go, logrus or zap. The key: include a correlation ID (trace ID) that spans services. Without it, you can't trace a request across microservices. I've spent hours correlating logs manually because a team didn't include trace IDs. Use OpenTelemetry for distributed tracing — it's the industry standard now.
info as default. debug only for development. warn for recoverable issues. error for failures. Never log sensitive data (passwords, PII) — even at debug level.Alerting: Don't Wake Up for Everything
Alert fatigue is real. If every container restart triggers a page, you'll ignore the real ones. Define alerting rules in Prometheus or Grafana. Good alerts: 'Container memory > 85% for 5 minutes', 'Container restart count > 3 in 10 minutes', 'No logs from service for 5 minutes'. Bad alerts: 'CPU > 50%' (too noisy). Use severity levels: critical (page), warning (email), info (dashboard). I once worked at a company where every CPU spike paged the on-call. After a week, nobody responded. We fixed it by adding 'for' duration and only paging on sustained high usage. Also, set up a 'dead man's switch' — alert if Prometheus stops scraping.
When Not to Use This Stack
Prometheus + Grafana + Loki is overkill for a single container on a dev machine. For local development, docker stats and docker logs are fine. For a small app with 2-3 containers, consider a lightweight alternative like Netdata (all-in-one monitoring) or Dozzle (log viewer). Also, if you're on Kubernetes, use the native monitoring stack (kube-prometheus-stack). Don't reinvent the wheel. And if you need full-text search on logs (e.g., for compliance audits), Elasticsearch + Kibana (ELK) might be better than Loki, despite the cost. Choose based on scale and requirements, not hype.
The 4GB Container That Kept Dying
--memory=512m --memory-reservation=256m on the container. Configured --log-opt max-size=10m --log-opt max-file=3 and switched to --log-driver=local for better performance. Added cAdvisor + Prometheus to track memory usage over time.- Always set memory limits and log rotation on containers.
- Without them, you're one memory spike away from a silent OOM kill.
docker ps | grep cadvisor. 2. Check cAdvisor logs: docker logs cadvisor --tail 20. 3. Verify network connectivity: docker exec prometheus curl http://cadvisor:8080/metrics. 4. Restart cAdvisor if needed.docker logs <promtail-container> --tail 20. 2. Verify Loki URL in plugin config. 3. Test Loki API: curl http://loki:3100/loki/api/v1/labels. 4. Check Grafana datasource is configured correctly.curl http://prometheus:9090/api/v1/rules. 2. Verify Alertmanager is running: docker ps | grep alertmanager. 3. Check Alertmanager config for correct receivers. 4. Test alert: manually fire a test alert via API.dmesg | grep -i killdocker inspect <container> --format '{{.State.ExitCode}}'docker update --memory 512m <container> or fix memory leak.| File | Command / Code | Purpose |
|---|---|---|
| logging-driver-config.devops | docker run --log-driver local --log-opt max-size=10m --log-opt max-file=3 --name... | Why Default Docker Logging Will Bite You |
| cadvisor-prometheus.devops | docker run --volume=/:/rootfs:ro --volume=/var/run:/var/run:ro --volume=/sys:/sy... | Container Metrics |
| prometheus-config.devops | scrape_configs: | Prometheus |
| grafana-datasource.devops | docker run --publish=3000:3000 --volume=grafana-storage:/var/lib/grafana grafana... | Grafana |
| loki-docker-plugin.devops | docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all... | Log Aggregation with Loki |
| structured-logging.js | const pino = require('pino'); | Structured Logging |
| prometheus-alert-rules.devops | groups: | Alerting |
| lightweight-alternative.devops | docker run --pid=host --privileged --volume=/:/host:ro,rslave --publish=19999:19... | When Not to Use This Stack |
Key takeaways
Interview Questions on This Topic
How does Docker's logging driver affect container performance, and which driver would you choose for high-throughput production services?
json-file driver writes JSON to the container's writable layer, causing I/O contention and storage bloat. For high throughput, use local driver (binary, compressed) or loki (streams directly to Loki). Avoid syslog as it can block if the syslog server is slow.Frequently Asked Questions
20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.
That's Docker. Mark it forged?
3 min read · try the examples if you haven't