Skip to Content

Self-Host Grafana & Prometheus: Complete Monitoring Stack in 15 Minutes

Self-Host Grafana & Prometheus: Complete Monitoring Stack in 15 Minutes

Self-Host Grafana & Prometheus: Complete Monitoring Stack in 15 Minutes

Every self-hosted service is a black box until it breaks. Then you're SSHing into servers at 2 AM, guessing if it's a memory leak, disk full, or a runaway container. Sound familiar?

Here's the fix: Prometheus scrapes metrics from everything you run. Grafana turns those metrics into beautiful, actionable dashboards. Together, they're the industry-standard monitoring stack — and you can self-host both in about 15 minutes.

Why self-host monitoring? Because your infrastructure data stays yours. No per-host SaaS pricing. No data leaving your network. And you get unlimited dashboards, alerts, and retention.

What You'll Build

  • Prometheus server scraping metrics from itself, Docker, and your node
  • Grafana with pre-built dashboards for system and container monitoring
  • Node Exporter for hardware/OS metrics
  • cAdvisor for container metrics
  • Everything wired together with Docker Compose

Prerequisites

  • A Linux VPS or server (2 vCPU, 4GB RAM minimum)
  • Docker and Docker Compose installed
  • A domain or subdomain pointed at your server (optional but recommended)
  • Traefik or Nginx reverse proxy (optional, for HTTPS)

Step 1: Create the Project Directory

mkdir -p ~/monitoring/{prometheus,grafana}
cd ~/monitoring

Step 2: Prometheus Configuration

Create prometheus/prometheus.yml:

global:
  scrape_interval: 15s
  evaluation_interval: 15s

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

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

  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor:8080']

  - job_name: 'docker'
    static_configs:
      - targets: ['host.docker.internal:9323']

Step 3: Docker Compose Stack

Create docker-compose.yml in the project root:

version: '3.8'

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

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    restart: unless-stopped
    volumes:
      - grafana-data:/var/lib/grafana
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=admin
      - GF_USERS_ALLOW_SIGN_UP=false
    ports:
      - "3000:3000"
    networks:
      - monitoring

  node-exporter:
    image: prom/node-exporter:latest
    container_name: node-exporter
    restart: unless-stopped
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.rootfs=/rootfs'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
    ports:
      - "9100:9100"
    networks:
      - monitoring

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

volumes:
  prometheus-data:
  grafana-data:

networks:
  monitoring:
    driver: bridge

Step 4: Launch the Stack

docker compose up -d

Wait 30 seconds for all containers to start, then verify:

docker compose ps

You should see all four services running.

Step 5: Verify Prometheus Is Scraping

  1. Open http://your-server-ip:9090
  2. Go to Status → Targets
  3. All endpoints should show UP (green)

If any target is down, check the container logs:

docker logs cadvisor
docker logs node-exporter

Step 6: Configure Grafana

  1. Open http://your-server-ip:3000
  2. Login with admin / admin
  3. Change the password when prompted
  4. Go to Connections → Data Sources → Add data source
  5. Select Prometheus
  6. URL: http://prometheus:9090
  7. Click Save & Test

Step 7: Import Dashboards

Don't build dashboards from scratch. Import these community dashboards:

Node Exporter Full (ID: 1860)

  1. Go to Dashboards → Import
  2. Enter 1860 and click Load
  3. Select your Prometheus data source
  4. Click Import

cAdvisor (ID: 14282)

  1. Import dashboard ID 14282
  2. Select Prometheus data source

You'll now have full system metrics (CPU, memory, disk, network) and container-level metrics (per-container CPU, memory, I/O).

Step 8: Add Docker Daemon Metrics (Optional)

Edit /etc/docker/daemon.json:

{
  "metrics-addr": "0.0.0.0:9323",
  "experimental": true
}

Restart Docker:

sudo systemctl restart docker

The docker job in Prometheus will now collect Docker daemon metrics.

Verification Checklist

  • [ ] Prometheus targets page shows all endpoints UP
  • [ ] Grafana data source test succeeds
  • [ ] Node Exporter dashboard shows CPU, memory, disk graphs
  • [ ] cAdvisor dashboard shows running containers
  • [ ] Metrics are updating in real-time (15s interval)

Next Steps

  • Add Alertmanager: Get notified via email, Slack, or Telegram when metrics cross thresholds
  • Scrape remote nodes: Deploy node-exporter on other servers and add them to prometheus.yml
  • Monitor applications: Add client libraries to your apps for custom metrics
  • Secure with HTTPS: Put Grafana behind Traefik or Nginx with Let's Encrypt
  • Long-term storage: Consider Thanos or VictoriaMetrics for multi-year retention

Related Guides


Questions? Drop a comment below or reach out on our contact page.

Self-Host Traefik: The Ultimate Reverse Proxy in 15 Minutes