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
- Open
http://your-server-ip:9090 - Go to Status → Targets
- 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
- Open
http://your-server-ip:3000 - Login with
admin / admin - Change the password when prompted
- Go to Connections → Data Sources → Add data source
- Select Prometheus
- URL:
http://prometheus:9090 - Click Save & Test
Step 7: Import Dashboards
Don't build dashboards from scratch. Import these community dashboards:
Node Exporter Full (ID: 1860)
- Go to Dashboards → Import
- Enter
1860and click Load - Select your Prometheus data source
- Click Import
cAdvisor (ID: 14282)
- Import dashboard ID
14282 - 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
- Self-Host Traefik: The Ultimate Reverse Proxy in 15 Minutes
- Self-Host Portainer: Manage All Your Docker Containers in 15 Minutes
- Self-Host Uptime Kuma: Monitor Everything in 15 Minutes
Questions? Drop a comment below or reach out on our contact page.