---
# === IDENTITY ===
id: software/devops/docker-prometheus-grafana/2026
canonical_question: "Docker Compose reference: Prometheus + Grafana"
aliases:
  - "Prometheus Grafana Docker Compose monitoring stack"
  - "docker compose prometheus grafana node exporter setup"
  - "Docker monitoring stack with Prometheus and Grafana"
  - "prometheus grafana cAdvisor alertmanager docker compose"
  - "container monitoring Docker Compose Prometheus"
  - "self-hosted monitoring stack docker"
  - "docker compose observability stack"
  - "prometheus grafana docker production setup"
entity_type: software_reference
domain: software > devops > Docker Prometheus Grafana
region: global
jurisdiction: global
temporal_scope: 2025-2026

# === VERIFICATION ===
last_verified: 2026-02-27
confidence: 0.94
version: 1.0
first_published: 2026-02-27

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: stable
  last_breaking_change: "Prometheus 3.0 (Nov 2024): removed deprecated flags, new OTLP native support, UTF-8 metric names"
  next_review: 2026-08-26
  change_sensitivity: medium

# === CONSTRAINTS ===
constraints:
  - "Pin image versions in production -- never use :latest for Prometheus, Grafana, or exporters"
  - "Never expose Prometheus (9090) or Alertmanager (9093) publicly without authentication -- no auth by default"
  - "cAdvisor requires privileged volume mounts (/var/run, /sys, /var/lib/docker) -- review security implications"
  - "Node Exporter must run with --path.rootfs=/host and host PID namespace for accurate host metrics"
  - "Grafana provisioned datasources/dashboards are read-only in UI unless editable: true is set"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "Need Kubernetes-native monitoring (not Docker Compose)"
    use_instead: "software/devops/kube-prometheus-stack/2026"
  - condition: "Need cloud-managed monitoring (AWS CloudWatch, GCP Cloud Monitoring)"
    use_instead: "software/devops/cloud-monitoring-comparison/2026"
  - condition: "Need log aggregation, not metrics"
    use_instead: "software/devops/docker-loki-grafana/2026"

# === AGENT HINTS ===
inputs_needed:
  - key: "monitoring_scope"
    question: "What do you need to monitor?"
    type: choice
    options: ["Host metrics only", "Docker containers only", "Both host and containers", "Application custom metrics"]
  - key: "alerting"
    question: "Do you need alerting (email, Slack, PagerDuty notifications)?"
    type: choice
    options: ["Yes", "No"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/devops/docker-prometheus-grafana/2026"
suggested_citation: "Source: knowledgelib.io -- AI Knowledge Library (verified 2026-02-27)"

# === RELATED UNITS ===
related_kos:
  depends_on: []
  related_to:
    - id: "software/devops/docker-loki-grafana/2026"
      label: "Docker Loki + Grafana Log Aggregation"
    - id: "software/devops/prometheus-alerting-rules/2026"
      label: "Prometheus Alerting Rules Reference"
  solves: []
  alternative_to:
    - id: "software/devops/kube-prometheus-stack/2026"
      label: "Kubernetes Prometheus Stack (Helm)"
  often_confused_with:
    - id: "software/devops/docker-elk-stack/2026"
      label: "Docker ELK Stack (Elasticsearch + Logstash + Kibana)"

# === SOURCES (7 authoritative sources) ===
# Types: official_docs, technical_blog, rfc_spec, academic_paper, community_resource, industry_report
# Reliability: high, moderate_high, moderate, moderate_low, low, authoritative
sources:
  - id: src1
    title: "Installation | Prometheus"
    author: Prometheus Authors
    url: https://prometheus.io/docs/prometheus/latest/installation/
    type: official_docs
    published: 2026-02-24
    reliability: authoritative
  - id: src2
    title: "Monitoring Docker container metrics using cAdvisor"
    author: Prometheus Authors
    url: https://prometheus.io/docs/guides/cadvisor/
    type: official_docs
    published: 2025-06-01
    reliability: authoritative
  - id: src3
    title: "Provision Grafana"
    author: Grafana Labs
    url: https://grafana.com/docs/grafana/latest/administration/provisioning/
    type: official_docs
    published: 2026-02-25
    reliability: authoritative
  - id: src4
    title: "Run Grafana Docker image"
    author: Grafana Labs
    url: https://grafana.com/docs/grafana/latest/setup-grafana/installation/docker/
    type: official_docs
    published: 2026-02-25
    reliability: authoritative
  - id: src5
    title: "Prometheus with Docker Compose: Guide & Examples"
    author: Spacelift
    url: https://spacelift.io/blog/prometheus-docker-compose
    type: technical_blog
    published: 2025-12-15
    reliability: high
  - id: src6
    title: "Monitoring a Linux host with Prometheus, Node Exporter, and Docker Compose"
    author: Grafana Labs
    url: https://grafana.com/docs/grafana-cloud/send-data/metrics/metrics-prometheus/prometheus-config-examples/docker-compose-linux/
    type: official_docs
    published: 2025-10-01
    reliability: high
  - id: src7
    title: "dockprom: Docker hosts and containers monitoring with Prometheus, Grafana, cAdvisor, NodeExporter and AlertManager"
    author: Stefan Prodan
    url: https://github.com/stefanprodan/dockprom
    type: community_resource
    published: 2025-08-01
    reliability: moderate_high
---

# Docker Compose: Prometheus + Grafana Monitoring Stack

## TL;DR

- **Bottom line**: A production-ready monitoring stack using Docker Compose combines Prometheus (metrics collection), Grafana (visualization), Node Exporter (host metrics), cAdvisor (container metrics), and Alertmanager (notifications) -- all configured declaratively with persistent storage and auto-provisioned dashboards.
- **Key tool/command**: `docker compose up -d` with pinned image versions and named volumes for all stateful services.
- **Watch out for**: Running Prometheus or Alertmanager without authentication -- both expose unauthenticated web UIs by default on ports 9090 and 9093.
- **Works with**: Docker Compose v2+, Prometheus 3.x, Grafana 12.x, Node Exporter 1.9.x, cAdvisor 0.49.x, Alertmanager 0.28.x. Linux, macOS, Windows (WSL2).

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- Pin image versions in production -- never use :latest for Prometheus, Grafana, or exporters
- Never expose Prometheus (9090) or Alertmanager (9093) publicly without authentication -- no auth by default
- cAdvisor requires privileged volume mounts (/var/run, /sys, /var/lib/docker) -- review security implications
- Node Exporter must run with --path.rootfs=/host and host PID namespace for accurate host metrics
- Grafana provisioned datasources/dashboards are read-only in UI unless editable: true is set

## Quick Reference

| Service | Image | Ports | Volumes | Key Config |
|---|---|---|---|---|
| Prometheus | `prom/prometheus:v3.10.0` | `9090:9090` | `prometheus_data:/prometheus`, `./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml` | `--storage.tsdb.retention.time=30d` |
| Grafana | `grafana/grafana:12.4.0` | `3000:3000` | `grafana_data:/var/lib/grafana`, `./grafana/provisioning:/etc/grafana/provisioning` | `GF_SECURITY_ADMIN_PASSWORD` |
| Node Exporter | `prom/node-exporter:v1.9.0` | `9100:9100` | `/:/host:ro,rslave` | `--path.rootfs=/host`, PID host |
| cAdvisor | `gcr.io/cadvisor/cadvisor:v0.49.1` | `8080:8080` | `/var/run:/var/run:ro`, `/sys:/sys:ro`, `/var/lib/docker:/var/lib/docker:ro` | Privileged mounts required |
| Alertmanager | `prom/alertmanager:v0.28.1` | `9093:9093` | `alertmanager_data:/alertmanager`, `./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml` | Route + receiver config |

**Default Endpoints:**

| Endpoint | URL | Purpose |
|---|---|---|
| Prometheus UI | `http://localhost:9090` | Query, targets, rules, TSDB status |
| Prometheus Targets | `http://localhost:9090/targets` | Scrape target health check |
| Grafana UI | `http://localhost:3000` | Dashboards (default: admin/admin) |
| Alertmanager UI | `http://localhost:9093` | Alert status, silences |
| Node Exporter Metrics | `http://localhost:9100/metrics` | Raw host metrics |
| cAdvisor UI | `http://localhost:8080` | Container metrics explorer |

## Decision Tree

```
START: What monitoring do you need?
├── Host metrics only (CPU, RAM, disk, network)?
│   ├── YES → Prometheus + Node Exporter + Grafana (skip cAdvisor)
│   └── NO ↓
├── Docker container metrics only?
│   ├── YES → Prometheus + cAdvisor + Grafana (skip Node Exporter)
│   └── NO ↓
├── Both host + container metrics?
│   ├── YES → Full stack: Prometheus + Node Exporter + cAdvisor + Grafana
│   └── NO ↓
├── Need alerting (email, Slack, PagerDuty)?
│   ├── YES → Add Alertmanager service + alert rules
│   └── NO → Skip Alertmanager
├── Running on Kubernetes?
│   ├── YES → Use kube-prometheus-stack Helm chart instead
│   └── NO ↓
└── DEFAULT → Full stack with all 5 services
```

## Step-by-Step Guide

### 1. Create the project directory structure

Organize configuration files into service-specific directories for clarity. [src5]

```bash
mkdir -p monitoring/{prometheus,grafana/provisioning/datasources,grafana/provisioning/dashboards,alertmanager}
cd monitoring
```

**Verify**: `find . -type d` → should list all subdirectories.

### 2. Create the Docker Compose file

Define all services with pinned versions, named volumes, health checks, and a shared network. [src1]

```yaml
# docker-compose.yml
services:
  prometheus:
    image: prom/prometheus:v3.10.0
    container_name: prometheus
    command:
      - "--config.file=/etc/prometheus/prometheus.yml"
      - "--storage.tsdb.path=/prometheus"
      - "--storage.tsdb.retention.time=30d"
      - "--web.enable-lifecycle"
      - "--storage.tsdb.wal-compression"
    volumes:
      - prometheus_data:/prometheus
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - ./prometheus/rules.yml:/etc/prometheus/rules.yml:ro
    ports:
      - "9090:9090"
    restart: unless-stopped
    networks:
      - monitoring
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy"]
      interval: 30s
      timeout: 5s
      retries: 3

  grafana:
    image: grafana/grafana:12.4.0
    container_name: grafana
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-changeme}
      - GF_USERS_ALLOW_SIGN_UP=false
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning:ro
    ports:
      - "3000:3000"
    restart: unless-stopped
    networks:
      - monitoring
    depends_on:
      prometheus:
        condition: service_healthy

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

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

  alertmanager:
    image: prom/alertmanager:v0.28.1
    container_name: alertmanager
    command:
      - "--config.file=/etc/alertmanager/alertmanager.yml"
      - "--storage.path=/alertmanager"
    volumes:
      - alertmanager_data:/alertmanager
      - ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
    ports:
      - "9093:9093"
    restart: unless-stopped
    networks:
      - monitoring

volumes:
  prometheus_data:
  grafana_data:
  alertmanager_data:

networks:
  monitoring:
    driver: bridge
```

**Verify**: `docker compose config` → should print the resolved YAML with no errors.

### 3. Configure Prometheus scrape targets

Define scrape jobs for all exporters. Use Docker DNS names (service names resolve automatically within the compose network). [src1]

```yaml
# prometheus/prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s
  scrape_timeout: 10s

alerting:
  alertmanagers:
    - static_configs:
        - targets:
            - alertmanager:9093

rule_files:
  - "rules.yml"

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

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

  - job_name: "cadvisor"
    scrape_interval: 10s
    static_configs:
      - targets: ["cadvisor:8080"]
```

**Verify**: After starting, visit `http://localhost:9090/targets` → all targets should show `UP`.

### 4. Create Prometheus alert rules

Define alert conditions for common failure scenarios. [src5]

```yaml
# prometheus/rules.yml
groups:
  - name: node_alerts
    rules:
      - alert: HighCPUUsage
        expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High CPU usage on {{ $labels.instance }}"
          description: "CPU usage is above 80% for more than 5 minutes."

      - alert: HighMemoryUsage
        expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High memory usage on {{ $labels.instance }}"
          description: "Memory usage is above 85% for more than 5 minutes."

      - alert: DiskSpaceLow
        expr: (1 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"})) * 100 > 90
        for: 10m
        labels:
          severity: critical
        annotations:
          summary: "Disk space low on {{ $labels.instance }}"
          description: "Root filesystem is more than 90% full."

  - name: container_alerts
    rules:
      - alert: ContainerDown
        expr: absent(container_last_seen{name=~".+"})
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Container {{ $labels.name }} is down"

      - alert: ContainerHighCPU
        expr: rate(container_cpu_usage_seconds_total{name=~".+"}[5m]) * 100 > 80
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Container {{ $labels.name }} high CPU usage"
```

**Verify**: `http://localhost:9090/rules` → rules should appear as loaded.

### 5. Configure Alertmanager

Set up routing and receivers for notifications. [src5]

```yaml
# alertmanager/alertmanager.yml
global:
  resolve_timeout: 5m

route:
  group_by: ["alertname", "severity"]
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  receiver: "default"
  routes:
    - match:
        severity: critical
      receiver: "critical-alerts"

receivers:
  - name: "default"
    webhook_configs:
      - url: "http://example.com/webhook"
        send_resolved: true

  # Slack example (uncomment and configure):
  # - name: "critical-alerts"
  #   slack_configs:
  #     - api_url: "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"
  #       channel: "#alerts"
  #       title: '{{ .GroupLabels.alertname }}'
  #       text: '{{ range .Alerts }}{{ .Annotations.summary }}{{ end }}'

  - name: "critical-alerts"
    webhook_configs:
      - url: "http://example.com/critical-webhook"
        send_resolved: true
```

**Verify**: `http://localhost:9093/#/status` → config should be loaded.

### 6. Provision Grafana datasource and dashboard

Use Grafana provisioning to auto-configure Prometheus as a datasource and load dashboards on startup. [src3]

```yaml
# grafana/provisioning/datasources/datasource.yml
apiVersion: 1

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

```yaml
# grafana/provisioning/dashboards/dashboard.yml
apiVersion: 1

providers:
  - name: "default"
    orgId: 1
    folder: ""
    type: file
    disableDeletion: false
    updateIntervalSeconds: 30
    options:
      path: /etc/grafana/provisioning/dashboards
      foldersFromFilesStructure: false
```

**Verify**: Log into Grafana at `http://localhost:3000` (admin/changeme) → Configuration > Data Sources → Prometheus should appear.

### 7. Start the stack and verify

Launch all services and confirm everything is healthy. [src5]

```bash
# Start all services
docker compose up -d

# Check all containers are running
docker compose ps

# View logs for any errors
docker compose logs --tail=50
```

**Verify**: `docker compose ps` → all 5 services should show `running` (or `healthy` for Prometheus).

## Code Examples

### Grafana Dashboard JSON: Node Exporter Host Overview

> Full script: [grafana-dashboard-json-node-exporter-host-overview.json](scripts/grafana-dashboard-json-node-exporter-host-overview.json) (46 lines)

```json
{
  "dashboard": {
    "title": "Node Exporter - Host Overview",
    "uid": "node-exporter-host",
    "panels": [
# ... (see full script)
```

### Prometheus Recording Rules: Pre-compute expensive queries

```yaml
# prometheus/recording-rules.yml
# Input:  Raw metrics from Node Exporter and cAdvisor
# Output: Pre-aggregated metrics for faster Grafana queries

groups:
  - name: node_recording_rules
    interval: 15s
    rules:
      - record: instance:node_cpu_utilization:ratio
        expr: 1 - avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m]))

      - record: instance:node_memory_utilization:ratio
        expr: 1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)

      - record: instance:node_disk_utilization:ratio
        expr: 1 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"})

  - name: container_recording_rules
    interval: 15s
    rules:
      - record: container:cpu_usage:rate5m
        expr: rate(container_cpu_usage_seconds_total{name=~".+"}[5m])

      - record: container:memory_usage:bytes
        expr: container_memory_usage_bytes{name=~".+"}
```

## Anti-Patterns

### Wrong: Using :latest tags for all images

```yaml
# BAD -- unpinned versions cause silent breaking changes
services:
  prometheus:
    image: prom/prometheus:latest
  grafana:
    image: grafana/grafana:latest
  node-exporter:
    image: prom/node-exporter:latest
```

### Correct: Pin specific versions

```yaml
# GOOD -- predictable, reproducible deployments
services:
  prometheus:
    image: prom/prometheus:v3.10.0
  grafana:
    image: grafana/grafana:12.4.0
  node-exporter:
    image: prom/node-exporter:v1.9.0
```

### Wrong: No persistent volumes

```yaml
# BAD -- all metrics data lost on container restart
services:
  prometheus:
    image: prom/prometheus:v3.10.0
    # No volumes defined -- data stored in ephemeral container layer
```

### Correct: Named volumes for all stateful services

```yaml
# GOOD -- data survives container restarts and upgrades
services:
  prometheus:
    image: prom/prometheus:v3.10.0
    volumes:
      - prometheus_data:/prometheus
volumes:
  prometheus_data:
```

### Wrong: Exposing Prometheus publicly without auth

```yaml
# BAD -- anyone can query/delete your metrics and see infrastructure details
services:
  prometheus:
    image: prom/prometheus:v3.10.0
    ports:
      - "0.0.0.0:9090:9090"  # Exposed to the internet
```

### Correct: Bind to localhost or use reverse proxy with auth

```yaml
# GOOD -- only accessible locally or through authenticated reverse proxy
services:
  prometheus:
    image: prom/prometheus:v3.10.0
    ports:
      - "127.0.0.1:9090:9090"  # Bind to localhost only
```

### Wrong: Node Exporter without host PID and rootfs mount

```yaml
# BAD -- incomplete host metrics (missing process info, wrong filesystem paths)
services:
  node-exporter:
    image: prom/node-exporter:v1.9.0
    # Missing pid: host and /:/host volume
```

### Correct: Proper Node Exporter configuration

```yaml
# GOOD -- accurate host metrics with rootfs remapping
services:
  node-exporter:
    image: prom/node-exporter:v1.9.0
    command:
      - "--path.rootfs=/host"
    volumes:
      - "/:/host:ro,rslave"
    pid: host
```

### Wrong: Hardcoded Grafana admin password in docker-compose.yml

```yaml
# BAD -- password committed to version control
services:
  grafana:
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=mysecretpassword
```

### Correct: Use environment variable or .env file

```yaml
# GOOD -- password loaded from .env file (excluded from git)
services:
  grafana:
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD}
```

## Common Pitfalls

- **Prometheus targets show DOWN**: Service names in scrape_configs must match docker-compose service names exactly. `node-exporter:9100` not `node_exporter:9100` or `localhost:9100`. Fix: Check `docker compose ps` for exact service names. [src5]
- **Grafana shows "No data"**: Prometheus datasource URL must use the Docker internal DNS name `http://prometheus:9090`, not `http://localhost:9090` (localhost inside the Grafana container is Grafana itself). Fix: Use service name in datasource URL. [src3]
- **cAdvisor crash on newer kernels**: cAdvisor may fail with `Failed to start container manager` on Linux 6.x+ kernels. Fix: Add `--docker_only=true` flag or update to cAdvisor v0.49+. [src2]
- **Prometheus OOM on high cardinality**: Too many unique label combinations (especially container IDs) cause memory exhaustion. Fix: Add `metric_relabel_configs` to drop high-cardinality labels, or set `--storage.tsdb.retention.size=5GB`. [src1]
- **Alert rules not loading**: `rule_files` path in prometheus.yml must match the mounted path inside the container. If prometheus.yml says `rules.yml`, the file must be at `/etc/prometheus/rules.yml`. Fix: Verify mount paths match rule_files paths. [src5]
- **Grafana provisioned dashboards can't be edited**: Provisioned dashboards are read-only by default. Fix: Set `editable: true` in datasource config, or export dashboard JSON, modify, and re-provision. [src3]
- **Node Exporter shows container filesystem instead of host**: Missing `--path.rootfs=/host` flag or missing volume mount causes Node Exporter to report container metrics instead of host. Fix: Ensure both the volume mount and the flag are set. [src6]
- **Alertmanager not receiving alerts**: The `alerting` section in prometheus.yml must reference the alertmanager service by Docker DNS name `alertmanager:9093`, not `localhost:9093`. Fix: Use Docker service names for all inter-service communication. [src5]

## Diagnostic Commands

```bash
# Check all service health
docker compose ps

# View Prometheus targets status
curl -s http://localhost:9090/api/v1/targets | jq '.data.activeTargets[] | {scrapeUrl, health, lastError}'

# Test Prometheus config validity
docker compose exec prometheus promtool check config /etc/prometheus/prometheus.yml

# Validate alert rules
docker compose exec prometheus promtool check rules /etc/prometheus/rules.yml

# Check Grafana datasource connectivity
curl -s -u admin:changeme http://localhost:3000/api/datasources | jq '.[].name'

# Test Alertmanager config
docker compose exec alertmanager amtool check-config /etc/alertmanager/alertmanager.yml

# Check Prometheus TSDB status (storage, retention, WAL size)
curl -s http://localhost:9090/api/v1/status/tsdb | jq

# Query a specific metric directly
curl -s 'http://localhost:9090/api/v1/query?query=up' | jq '.data.result'

# Reload Prometheus config without restart (requires --web.enable-lifecycle)
curl -X POST http://localhost:9090/-/reload

# View cAdvisor container metrics
curl -s http://localhost:8080/api/v2.0/summary/ | jq 'keys'

# Check container resource usage
docker stats --no-stream
```

## Version History & Compatibility

| Component | Version | Status | Breaking Changes | Notes |
|---|---|---|---|---|
| Prometheus | 3.10.0 | Current | 3.0 removed deprecated flags, UTF-8 metric names | LTS: 3.5.1 |
| Prometheus | 2.54.x | LTS until 2025-07 | -- | Last 2.x LTS |
| Grafana | 12.4.0 | Current | 12.0 changed auth defaults | Unified alerting is default |
| Grafana | 11.x | Previous | -- | -- |
| Node Exporter | 1.9.0 | Current | None | -- |
| cAdvisor | 0.49.1 | Current | 0.47+ requires Linux 5.4+ | Google-maintained |
| Alertmanager | 0.28.1 | Current | 0.27 removed v1 API | v2 API only |
| Docker Compose | v2.x | Current | v1 syntax deprecated | Built into Docker CLI |

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Self-hosted monitoring on VMs or bare metal with Docker | Running on Kubernetes | kube-prometheus-stack Helm chart |
| Need full control over retention, scraping, alerting | Want managed monitoring with zero ops | Grafana Cloud, Datadog, or AWS CloudWatch |
| Dev/staging environment monitoring | Monitoring 1000+ nodes | Thanos or Cortex for horizontal scaling |
| Docker Compose is already your deployment tool | Need log aggregation (not metrics) | Loki + Grafana or ELK stack |
| Budget-conscious -- all components are free and open-source | Need APM/tracing | OpenTelemetry + Jaeger or commercial APM |

## Important Caveats

- Prometheus stores data locally on a single node -- it is NOT horizontally scalable; for multi-node or long-term storage, add Thanos, Cortex, or Mimir as a remote write target
- cAdvisor's `privileged: true` and volume mounts give it broad host access -- in multi-tenant environments, evaluate the security risk
- Grafana dashboard JSON exported from the UI may differ from provisioned JSON format -- always test provisioned dashboards after export
- Docker Desktop on macOS and Windows runs containers in a Linux VM, so Node Exporter reports the VM's metrics, not the host OS; use Prometheus Windows Exporter or macOS-specific collectors for native host metrics
- Prometheus 3.0 introduced breaking changes (removed deprecated flags, OTLP endpoint enabled by default) -- verify configuration when upgrading from 2.x
- Default scrape interval of 15s generates ~5,760 samples/day per metric per target -- adjust based on storage capacity and metric cardinality

## Related Units
<!-- Generated from related_kos frontmatter -->

- [Docker Loki + Grafana Log Aggregation](/software/devops/docker-loki-grafana/2026)
- [Prometheus Alerting Rules Reference](/software/devops/prometheus-alerting-rules/2026)
- [Kubernetes Prometheus Stack (Helm)](/software/devops/kube-prometheus-stack/2026)
- [Docker ELK Stack](/software/devops/docker-elk-stack/2026)
