Monitor Docker containers with Prometheus and Grafana using cAdvisor for comprehensive metrics collection

Intermediate 45 min Apr 03, 2026 321 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up comprehensive Docker container monitoring with Prometheus, Grafana, and cAdvisor to track resource usage, performance metrics, and container health in production environments.

Prerequisites

  • Root or sudo access
  • 4GB RAM minimum
  • Docker and Docker Compose
  • Open firewall ports 3000, 8080, 9090, 9100

What this solves

Docker containers in production need continuous monitoring to track resource usage, performance bottlenecks, and system health. This tutorial sets up a complete monitoring stack with Prometheus for metrics collection, cAdvisor for detailed container statistics, and Grafana for visualization and alerting. You'll get real-time insights into CPU, memory, network, and disk usage across all your containers.

Step-by-step installation

Update system packages

Start by updating your system to ensure you have the latest security patches and packages.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install Docker Engine

Install Docker to run your containers and the monitoring stack. This adds the official Docker repository and installs the latest stable version.

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
newgrp docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
newgrp docker

Install Docker Compose

Docker Compose simplifies multi-container deployments by defining services in a single configuration file.

sudo apt install -y docker-compose-plugin
sudo dnf install -y docker-compose-plugin

Create monitoring directory structure

Organize your monitoring configuration files in a dedicated directory with proper permissions.

mkdir -p ~/docker-monitoring/{prometheus,grafana/dashboards,grafana/provisioning/{dashboards,datasources}}
cd ~/docker-monitoring

Configure Prometheus

Create the Prometheus configuration to scrape metrics from cAdvisor and itself.

global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - "alert_rules.yml"

alerting:
  alertmanagers:
    - static_configs:
        - targets: []

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor:8080']
    scrape_interval: 5s
    metrics_path: /metrics

  - job_name: 'node-exporter'
    static_configs:
      - targets: ['node-exporter:9100']

Create Prometheus alerting rules

Define alerting rules for container health, high resource usage, and system issues.

groups:
  - name: docker-containers
    rules:
      - alert: ContainerHighCPUUsage
        expr: rate(container_cpu_usage_seconds_total[5m]) * 100 > 80
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Container {{ $labels.name }} high CPU usage"
          description: "Container {{ $labels.name }} CPU usage is above 80% for more than 5 minutes."

      - alert: ContainerHighMemoryUsage
        expr: (container_memory_usage_bytes / container_spec_memory_limit_bytes) * 100 > 90
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Container {{ $labels.name }} high memory usage"
          description: "Container {{ $labels.name }} memory usage is above 90% for more than 5 minutes."

      - alert: ContainerDown
        expr: up == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Container {{ $labels.instance }} is down"
          description: "Container {{ $labels.instance }} has been down for more than 1 minute."

      - alert: HighDiskUsage
        expr: (container_fs_usage_bytes / container_fs_limit_bytes) * 100 > 85
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Container {{ $labels.name }} high disk usage"
          description: "Container {{ $labels.name }} disk usage is above 85% for more than 10 minutes."

Configure Grafana datasource

Automatically configure Prometheus as a datasource when Grafana starts.

apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
    editable: true

Configure Grafana dashboard provisioning

Set up automatic dashboard loading from the dashboards directory.

apiVersion: 1

providers:
  - name: 'Docker Container Monitoring'
    orgId: 1
    folder: ''
    type: file
    disableDeletion: false
    updateIntervalSeconds: 10
    allowUiUpdates: true
    options:
      path: /etc/grafana/provisioning/dashboards

Create Docker container dashboard

Create a comprehensive dashboard for monitoring Docker containers with key metrics and visualizations.

{
  "dashboard": {
    "id": null,
    "title": "Docker Container Monitoring",
    "tags": ["docker", "containers"],
    "timezone": "browser",
    "panels": [
      {
        "id": 1,
        "title": "Container CPU Usage",
        "type": "graph",
        "targets": [
          {
            "expr": "rate(container_cpu_usage_seconds_total{name!~\".POD.\",name!=\"\"}[5m]) * 100",
            "legendFormat": "{{ name }}",
            "refId": "A"
          }
        ],
        "yAxes": [
          {
            "label": "CPU Usage %",
            "max": 100,
            "min": 0
          }
        ],
        "gridPos": {
          "h": 8,
          "w": 12,
          "x": 0,
          "y": 0
        }
      },
      {
        "id": 2,
        "title": "Container Memory Usage",
        "type": "graph",
        "targets": [
          {
            "expr": "container_memory_usage_bytes{name!~\".POD.\",name!=\"\"}",
            "legendFormat": "{{ name }}",
            "refId": "A"
          }
        ],
        "yAxes": [
          {
            "label": "Memory Usage (Bytes)"
          }
        ],
        "gridPos": {
          "h": 8,
          "w": 12,
          "x": 12,
          "y": 0
        }
      },
      {
        "id": 3,
        "title": "Container Network I/O",
        "type": "graph",
        "targets": [
          {
            "expr": "rate(container_network_receive_bytes_total{name!~\".POD.\",name!=\"\"}[5m])",
            "legendFormat": "{{ name }} - RX",
            "refId": "A"
          },
          {
            "expr": "rate(container_network_transmit_bytes_total{name!~\".POD.\",name!=\"\"}[5m])",
            "legendFormat": "{{ name }} - TX",
            "refId": "B"
          }
        ],
        "gridPos": {
          "h": 8,
          "w": 24,
          "x": 0,
          "y": 8
        }
      }
    ],
    "time": {
      "from": "now-1h",
      "to": "now"
    },
    "refresh": "10s"
  }
}

Create Docker Compose configuration

Define all monitoring services in a single Docker Compose file for easy deployment and management.

version: '3.8'

services:
  prometheus:
    image: prom/prometheus:v2.45.0
    container_name: prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/etc/prometheus/console_libraries'
      - '--web.console.templates=/etc/prometheus/consoles'
      - '--web.enable-lifecycle'
      - '--storage.tsdb.retention.time=15d'
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus:/etc/prometheus
      - prometheus_data:/prometheus
    networks:
      - monitoring
    restart: unless-stopped

  grafana:
    image: grafana/grafana:10.0.3
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin123
      - GF_USERS_ALLOW_SIGN_UP=false
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
      - ./grafana/dashboards:/etc/grafana/provisioning/dashboards
    networks:
      - monitoring
    restart: unless-stopped

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:v0.47.0
    container_name: cadvisor
    ports:
      - "8080:8080"
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
      - /dev/disk/:/dev/disk:ro
    privileged: true
    devices:
      - /dev/kmsg
    networks:
      - monitoring
    restart: unless-stopped

  node-exporter:
    image: prom/node-exporter:v1.6.1
    container_name: node-exporter
    ports:
      - "9100:9100"
    command:
      - '--path.rootfs=/host'
    volumes:
      - '/:/host:ro,rslave'
    pid: host
    networks:
      - monitoring
    restart: unless-stopped

  # Example application container to monitor
  nginx-demo:
    image: nginx:alpine
    container_name: nginx-demo
    ports:
      - "8081:80"
    networks:
      - monitoring
    restart: unless-stopped

volumes:
  prometheus_data:
  grafana_data:

networks:
  monitoring:
    driver: bridge

Set proper file permissions

Ensure the monitoring stack can read configuration files and write data with correct ownership.

Never use chmod 777. It gives every user on the system full access to your files. Instead, fix ownership with chown and use minimal permissions.
sudo chown -R $USER:$USER ~/docker-monitoring
chmod -R 755 ~/docker-monitoring
chmod 644 ~/docker-monitoring/prometheus/prometheus.yml
chmod 644 ~/docker-monitoring/prometheus/alert_rules.yml
chmod 644 ~/docker-monitoring/grafana/provisioning/datasources/prometheus.yml

Configure firewall rules

Open necessary ports for the monitoring services while maintaining security.

sudo ufw allow 3000/tcp comment 'Grafana'
sudo ufw allow 9090/tcp comment 'Prometheus'
sudo ufw allow 8080/tcp comment 'cAdvisor'
sudo ufw allow 9100/tcp comment 'Node Exporter'
sudo firewall-cmd --permanent --add-port=3000/tcp
sudo firewall-cmd --permanent --add-port=9090/tcp
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --permanent --add-port=9100/tcp
sudo firewall-cmd --reload

Deploy the monitoring stack

Start all monitoring services using Docker Compose.

cd ~/docker-monitoring
docker compose up -d

Configure Grafana dashboard alerts

Set up notification channels and alert rules within Grafana for proactive monitoring.

Note: Access Grafana at http://your-server-ip:3000 with admin/admin123 credentials. Change the password immediately after first login.

Verify your setup

Check that all monitoring services are running and collecting data properly.

docker compose ps
docker compose logs prometheus
docker compose logs grafana
docker compose logs cadvisor

Verify service endpoints are responding:

curl -s http://localhost:9090/api/v1/targets | jq '.data.activeTargets[].health'
curl -s http://localhost:8080/metrics | head -10
curl -s http://localhost:3000/api/health

Check container metrics are being collected:

curl -s 'http://localhost:9090/api/v1/query?query=up' | jq '.data.result[].metric.job'

Common issues

SymptomCauseFix
cAdvisor container exits with permission error Insufficient privileges for system access Ensure privileged: true in docker-compose.yml and restart
Prometheus shows targets down Network connectivity or service discovery issues Check docker compose logs prometheus and verify service names in config
Grafana dashboards show no data Datasource misconfiguration Verify Prometheus URL is http://prometheus:9090 in datasource settings
High memory usage by cAdvisor Default retention settings Add --housekeeping_interval=30s --max_housekeeping_interval=35s to cAdvisor command
Alerts not firing Alert rules syntax error Validate YAML syntax and check Prometheus logs: docker compose logs prometheus

Next steps

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle managed devops services for businesses that depend on uptime. From initial setup to ongoing operations.