Skip to main content

Self-Hosting Guide: Deploy Grafana for Observability

·OSSAlt Team
grafanamonitoringself-hostingdockerguide

Self-Hosting Guide: Deploy Grafana for Observability

Grafana is the industry-standard open source observability platform — dashboards, alerting, and data exploration across metrics, logs, and traces. Self-hosting gives you unlimited dashboards, data sources, and users.

Requirements

  • VPS with 2 GB RAM minimum (4 GB with Prometheus + Loki)
  • Docker and Docker Compose
  • Domain name (e.g., grafana.yourdomain.com)
  • 20+ GB disk

Step 1: Create Docker Compose

# docker-compose.yml
services:
  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    restart: unless-stopped
    ports:
      - "3000:3000"
    volumes:
      - grafana_data:/var/lib/grafana
    environment:
      - GF_SERVER_ROOT_URL=https://grafana.yourdomain.com
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=your-admin-password
      - GF_USERS_ALLOW_SIGN_UP=false
      - GF_SMTP_ENABLED=true
      - GF_SMTP_HOST=smtp.resend.com:587
      - GF_SMTP_USER=resend
      - GF_SMTP_PASSWORD=re_your_api_key
      - GF_SMTP_FROM_ADDRESS=grafana@yourdomain.com

  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    restart: unless-stopped
    ports:
      - "9090:9090"
    volumes:
      - prometheus_data:/prometheus
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.retention.time=30d'

  node-exporter:
    image: prom/node-exporter:latest
    container_name: node-exporter
    restart: unless-stopped
    ports:
      - "9100:9100"
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.sysfs=/host/sys'
      - '--path.rootfs=/rootfs'

volumes:
  grafana_data:
  prometheus_data:

Step 2: Configure Prometheus

Create prometheus.yml:

global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  # Prometheus self-monitoring
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  # Server metrics
  - job_name: 'node'
    static_configs:
      - targets: ['node-exporter:9100']

  # Add your application metrics
  # - job_name: 'my-app'
  #   static_configs:
  #     - targets: ['my-app:8080']
  #   metrics_path: /metrics

Step 3: Start the Stack

docker compose up -d

Step 4: Reverse Proxy (Caddy)

# /etc/caddy/Caddyfile
grafana.yourdomain.com {
    reverse_proxy localhost:3000
}
sudo systemctl restart caddy

Step 5: Add Data Sources

  1. Open https://grafana.yourdomain.com
  2. Login with admin credentials
  3. Go to ConnectionsData sourcesAdd data source
Data SourceURLUse Case
Prometheushttp://prometheus:9090Metrics (CPU, RAM, custom)
Lokihttp://loki:3100Log aggregation
PostgreSQLhost:5432Database metrics
InfluxDBhttp://influxdb:8086Time series data
Elasticsearchhttp://elasticsearch:9200Logs and search
CloudWatchAWS credentialsAWS metrics

Step 6: Import Pre-Built Dashboards

Grafana has 1000+ community dashboards at grafana.com/grafana/dashboards.

Essential dashboards to import:

Dashboard IDNameFor
1860Node Exporter FullServer metrics
3662Prometheus OverviewPrometheus health
14055Docker ContainersContainer metrics
12708PostgreSQLDatabase metrics
763RedisRedis metrics

To import:

  1. DashboardsNewImport
  2. Enter the dashboard ID
  3. Select your data source
  4. Click Import

Step 7: Create Custom Dashboards

Example: Application metrics dashboard

  1. DashboardsNew DashboardAdd visualization
  2. Select Prometheus data source
  3. Use PromQL queries:
# CPU usage per container
rate(container_cpu_usage_seconds_total[5m]) * 100

# Memory usage
container_memory_usage_bytes / 1024 / 1024

# HTTP request rate
rate(http_requests_total[5m])

# HTTP error rate (5xx)
rate(http_requests_total{status=~"5.."}[5m])

# Request latency (p95)
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))

Step 8: Set Up Alerting

  1. Go to AlertingAlert rulesNew alert rule
  2. Define your condition:

Example alerts:

AlertConditionSeverity
High CPUCPU > 80% for 5 minWarning
Disk fullDisk usage > 90%Critical
Service downUp metric = 0 for 1 minCritical
High error rate5xx > 1% of requestsWarning
Memory pressureRAM > 90% for 10 minWarning
  1. Configure Contact points (where alerts go):
    • Email (via SMTP)
    • Slack webhook
    • Discord webhook
    • PagerDuty
    • Telegram

Step 9: Add Log Aggregation with Loki (Optional)

Add to docker-compose.yml:

  loki:
    image: grafana/loki:latest
    container_name: loki
    restart: unless-stopped
    ports:
      - "3100:3100"
    volumes:
      - loki_data:/loki
    command: -config.file=/etc/loki/local-config.yaml

  promtail:
    image: grafana/promtail:latest
    container_name: promtail
    restart: unless-stopped
    volumes:
      - /var/log:/var/log:ro
      - ./promtail-config.yml:/etc/promtail/config.yml
    command: -config.file=/etc/promtail/config.yml

Create promtail-config.yml:

server:
  http_listen_port: 9080
positions:
  filename: /tmp/positions.yaml
clients:
  - url: http://loki:3100/loki/api/v1/push
scrape_configs:
  - job_name: system
    static_configs:
      - targets: [localhost]
        labels:
          job: varlogs
          __path__: /var/log/*.log

Production Hardening

Backups:

# Grafana (dashboards, users, settings)
docker cp grafana:/var/lib/grafana/grafana.db /backups/grafana-$(date +%Y%m%d).db

# Prometheus data (if needed — usually regenerated from exporters)
docker run --rm -v prometheus_data:/data -v /backups:/backup alpine \
  tar czf /backup/prometheus-$(date +%Y%m%d).tar.gz /data

Data retention:

# Prometheus: keep 30 days
command: '--storage.tsdb.retention.time=30d'

# Loki: configure retention in loki config
# limits_config:
#   retention_period: 744h  # 31 days

Updates:

docker compose pull
docker compose up -d

Security:

  • Disable sign-up: GF_USERS_ALLOW_SIGN_UP=false
  • Set up OIDC auth for team access
  • Restrict Prometheus/Loki to internal network
  • Use read-only data source connections

Resource Usage

StackRAMCPUDisk
Grafana only512 MB1 core5 GB
Grafana + Prometheus2 GB2 cores20 GB
Full stack (+ Loki)4 GB4 cores50 GB

VPS Recommendations

ProviderSpec (full stack)Price
Hetzner4 vCPU, 8 GB RAM€8/month
DigitalOcean2 vCPU, 4 GB RAM$24/month
Linode2 vCPU, 4 GB RAM$24/month

Compare monitoring and observability tools on OSSAlt — features, data sources, and pricing side by side.