Skip to content

Commands & Recipes

Installation

Docker (Quick Start)

# Latest OSS version
docker run -d --name grafana \
  -p 3000:3000 \
  -v grafana-data:/var/lib/grafana \
  grafana/grafana-oss:latest

# With environment variable overrides
docker run -d --name grafana \
  -p 3000:3000 \
  -e GF_SECURITY_ADMIN_PASSWORD=mysecretpassword \
  -e GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-piechart-panel \
  -e GF_DATABASE_TYPE=postgres \
  -e GF_DATABASE_HOST=postgres:5432 \
  -e GF_DATABASE_NAME=grafana \
  -e GF_DATABASE_USER=grafana \
  -e GF_DATABASE_PASSWORD=dbpassword \
  grafana/grafana-oss:latest

Docker Compose (LGTM Stack)

# docker-compose.yml — Minimal LGTM stack for development
version: '3.8'
services:
  grafana:
    image: grafana/grafana-oss:latest
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana-data:/var/lib/grafana
      - ./provisioning:/etc/grafana/provisioning

  mimir:
    image: grafana/mimir:latest
    command: ["-config.file=/etc/mimir/config.yaml", "-target=all"]
    ports:
      - "9009:9009"
    volumes:
      - ./mimir-config.yaml:/etc/mimir/config.yaml

  loki:
    image: grafana/loki:latest
    ports:
      - "3100:3100"
    command: ["-config.file=/etc/loki/local-config.yaml"]

  tempo:
    image: grafana/tempo:latest
    ports:
      - "3200:3200"   # Tempo API
      - "4317:4317"   # OTLP gRPC
      - "4318:4318"   # OTLP HTTP
    command: ["-config.file=/etc/tempo/config.yaml"]
    volumes:
      - ./tempo-config.yaml:/etc/tempo/config.yaml

  alloy:
    image: grafana/alloy:latest
    ports:
      - "12345:12345" # Debug UI
    volumes:
      - ./alloy-config.alloy:/etc/alloy/config.alloy
    command: ["run", "/etc/alloy/config.alloy"]

volumes:
  grafana-data:

Helm (Kubernetes Production)

# Add Grafana Helm repo
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update

# Install Grafana
helm install grafana grafana/grafana \
  --namespace monitoring \
  --create-namespace \
  --set replicas=3 \
  --set persistence.enabled=false \
  --set database.type=postgres \
  --set "grafana.ini.database.host=postgres.internal:5432" \
  --set "grafana.ini.database.name=grafana" \
  --set "grafana.ini.database.user=grafana" \
  --set "grafana.ini.database.password=${DB_PASSWORD}" \
  --set "grafana.ini.server.root_url=https://grafana.example.com"

# Install Mimir (distributed mode)
helm install mimir grafana/mimir-distributed \
  --namespace monitoring \
  -f mimir-values.yaml

# Install Loki
helm install loki grafana/loki \
  --namespace monitoring \
  -f loki-values.yaml

# Install Tempo
helm install tempo grafana/tempo-distributed \
  --namespace monitoring \
  -f tempo-values.yaml

# Install Alloy (DaemonSet)
helm install alloy grafana/alloy \
  --namespace monitoring \
  -f alloy-values.yaml

Homebrew (macOS)

brew install grafana
brew services start grafana
# Access at http://localhost:3000 (admin/admin)

grafana-cli Commands

Plugin Management

# Install a plugin
grafana-cli plugins install grafana-piechart-panel

# Install specific version
grafana-cli plugins install grafana-piechart-panel 1.6.4

# List installed plugins
grafana-cli plugins ls

# List all available plugins
grafana-cli plugins list-remote

# Update all plugins
grafana-cli plugins update-all

# Remove a plugin
grafana-cli plugins remove grafana-piechart-panel

# Install from a custom URL (private/unsigned)
grafana-cli --pluginUrl https://example.com/plugin.zip plugins install custom-plugin

Admin Commands

# Reset admin password
grafana-cli admin reset-admin-password MyNewPassword

# Reset admin password (custom install path)
grafana-cli --homepath /usr/share/grafana \
  --config /etc/grafana/grafana.ini \
  admin reset-admin-password MyNewPassword

# Encrypt data source passwords (migrate to secure_json_data)
grafana-cli admin data-migration encrypt-datasource-passwords

# Show Grafana version
grafana-cli --version

# Generate secret key
grafana-cli admin secret-scan

API Recipes

Authentication

# API key auth
curl -H "Authorization: Bearer YOUR_API_KEY" \
  https://grafana.example.com/api/dashboards/home

# Basic auth
curl -u admin:password \
  https://grafana.example.com/api/org

# Service account token (recommended for automation)
curl -H "Authorization: Bearer sa-token-xxx" \
  https://grafana.example.com/api/dashboards/home

Dashboard Operations

# List all dashboards
curl -s -H "Authorization: Bearer $TOKEN" \
  "$GRAFANA_URL/api/search?type=dash-db" | jq '.[] | {title, uid, url}'

# Get dashboard by UID
curl -s -H "Authorization: Bearer $TOKEN" \
  "$GRAFANA_URL/api/dashboards/uid/YOUR_DASHBOARD_UID" | jq .

# Export dashboard JSON (for backup/provisioning)
curl -s -H "Authorization: Bearer $TOKEN" \
  "$GRAFANA_URL/api/dashboards/uid/YOUR_DASHBOARD_UID" \
  | jq '.dashboard' > dashboard-export.json

# Import/create dashboard
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "dashboard": '"$(cat dashboard-export.json)"',
    "overwrite": true,
    "folderId": 0
  }' "$GRAFANA_URL/api/dashboards/db"

# Delete dashboard
curl -X DELETE -H "Authorization: Bearer $TOKEN" \
  "$GRAFANA_URL/api/dashboards/uid/YOUR_DASHBOARD_UID"

Data Source Operations

# List all data sources
curl -s -H "Authorization: Bearer $TOKEN" \
  "$GRAFANA_URL/api/datasources" | jq '.[] | {name, type, url}'

# Create a Prometheus data source
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Prometheus",
    "type": "prometheus",
    "url": "http://prometheus:9090",
    "access": "proxy",
    "isDefault": true
  }' "$GRAFANA_URL/api/datasources"

# Test data source connectivity
curl -s -H "Authorization: Bearer $TOKEN" \
  "$GRAFANA_URL/api/datasources/proxy/1/api/v1/query?query=up"

Alert Operations

# List all alert rules
curl -s -H "Authorization: Bearer $TOKEN" \
  "$GRAFANA_URL/api/v1/provisioning/alert-rules" | jq .

# Get alert rule by UID
curl -s -H "Authorization: Bearer $TOKEN" \
  "$GRAFANA_URL/api/v1/provisioning/alert-rules/RULE_UID"

# List contact points
curl -s -H "Authorization: Bearer $TOKEN" \
  "$GRAFANA_URL/api/v1/provisioning/contact-points" | jq .

# List notification policies
curl -s -H "Authorization: Bearer $TOKEN" \
  "$GRAFANA_URL/api/v1/provisioning/policies" | jq .

User & Org Management

# List all users
curl -s -H "Authorization: Bearer $TOKEN" \
  "$GRAFANA_URL/api/org/users" | jq '.[] | {login, role}'

# Create a service account
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "automation-sa", "role": "Editor"}' \
  "$GRAFANA_URL/api/serviceaccounts"

# Create service account token
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "ci-token"}' \
  "$GRAFANA_URL/api/serviceaccounts/1/tokens"

Provisioning Recipes

Data Source Provisioning (YAML)

# /etc/grafana/provisioning/datasources/datasources.yaml
apiVersion: 1
datasources:
  - name: Mimir
    type: prometheus
    access: proxy
    url: http://mimir-query-frontend:8080/prometheus
    isDefault: true
    jsonData:
      httpMethod: POST
      exemplarTraceIdDestinations:
        - name: traceID
          datasourceUid: tempo

  - name: Loki
    type: loki
    access: proxy
    url: http://loki-gateway:3100
    jsonData:
      derivedFields:
        - datasourceUid: tempo
          matcherRegex: '"traceID":"(\w+)"'
          name: TraceID
          url: '$${__value.raw}'

  - name: Tempo
    type: tempo
    access: proxy
    url: http://tempo-query-frontend:3200
    jsonData:
      tracesToMetrics:
        datasourceUid: mimir
      tracesToLogs:
        datasourceUid: loki
        tags: ['job', 'namespace', 'pod']

Dashboard Provider (YAML)

# /etc/grafana/provisioning/dashboards/provider.yaml
apiVersion: 1
providers:
  - name: 'Infrastructure'
    orgId: 1
    folder: 'Infrastructure'
    type: file
    editable: false
    options:
      path: /etc/grafana/provisioning/dashboards/infra
      foldersFromFilesStructure: true

Alert Rule Provisioning (YAML)

# /etc/grafana/provisioning/alerting/rules.yaml
apiVersion: 1
groups:
  - orgId: 1
    name: infrastructure-alerts
    folder: Infrastructure Alerts
    interval: 1m
    rules:
      - uid: high-cpu-alert
        title: High CPU Usage
        condition: A
        data:
          - refId: A
            relativeTimeRange:
              from: 600
              to: 0
            datasourceUid: mimir
            model:
              expr: '100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80'
              refId: A
        for: 5m
        labels:
          severity: warning
          team: infra
        annotations:
          summary: "CPU usage above 80% on {{ $labels.instance }}"

Contact Point Provisioning (YAML)

# /etc/grafana/provisioning/alerting/contactpoints.yaml
apiVersion: 1
contactPoints:
  - orgId: 1
    name: slack-oncall
    receivers:
      - uid: slack-receiver
        type: slack
        settings:
          recipient: '#alerts-oncall'
          token: '${SLACK_BOT_TOKEN}'
          title: '{{ template "slack.default.title" . }}'
          text: '{{ template "slack.default.text" . }}'

Grafana Alloy Configuration

Basic OTel Pipeline (Alloy Syntax)

// config.alloy — Basic OTLP receiver → Grafana Cloud

// OTLP Receiver (gRPC + HTTP)
otelcol.receiver.otlp "default" {
  grpc { endpoint = "0.0.0.0:4317" }
  http { endpoint = "0.0.0.0:4318" }

  output {
    metrics = [otelcol.processor.batch.default.input]
    logs    = [otelcol.processor.batch.default.input]
    traces  = [otelcol.processor.batch.default.input]
  }
}

// Batch Processor
otelcol.processor.batch "default" {
  output {
    metrics = [otelcol.exporter.otlphttp.grafana_cloud.input]
    logs    = [otelcol.exporter.otlphttp.grafana_cloud.input]
    traces  = [otelcol.exporter.otlphttp.grafana_cloud.input]
  }
}

// Export to Grafana Cloud
otelcol.exporter.otlphttp "grafana_cloud" {
  client {
    endpoint = env("GRAFANA_CLOUD_OTLP_ENDPOINT")
    auth     = otelcol.auth.basic.grafana_cloud.handler
  }
}

otelcol.auth.basic "grafana_cloud" {
  username = env("GRAFANA_CLOUD_INSTANCE_ID")
  password = env("GRAFANA_CLOUD_API_KEY")
}

Prometheus Scraping (Alloy Syntax)

// Scrape Prometheus endpoints
prometheus.scrape "kubernetes_pods" {
  targets    = discovery.kubernetes.pods.targets
  forward_to = [prometheus.remote_write.mimir.receiver]
}

// Kubernetes pod discovery
discovery.kubernetes "pods" {
  role = "pod"
}

// Remote write to Mimir
prometheus.remote_write "mimir" {
  endpoint {
    url = "http://mimir-distributor:8080/api/v1/push"
  }
}

Terraform Recipes

Grafana Provider Setup

terraform {
  required_providers {
    grafana = {
      source  = "grafana/grafana"
      version = ">= 3.0.0"
    }
  }
}

provider "grafana" {
  url  = "https://grafana.example.com"
  auth = var.grafana_api_key
}

Manage Dashboard via Terraform

resource "grafana_dashboard" "node_overview" {
  config_json = file("${path.module}/dashboards/node-overview.json")
  folder      = grafana_folder.infrastructure.id
  overwrite   = true
}

resource "grafana_folder" "infrastructure" {
  title = "Infrastructure"
}

resource "grafana_data_source" "prometheus" {
  type       = "prometheus"
  name       = "Mimir"
  url        = "http://mimir-query-frontend:8080/prometheus"
  is_default = true

  json_data_encoded = jsonencode({
    httpMethod = "POST"
  })
}

Useful One-Liners

# Backup all dashboards via API
for uid in $(curl -s -H "Authorization: Bearer $TOKEN" "$GRAFANA_URL/api/search?type=dash-db" | jq -r '.[].uid'); do
  curl -s -H "Authorization: Bearer $TOKEN" "$GRAFANA_URL/api/dashboards/uid/$uid" \
    | jq '.dashboard' > "backup-$uid.json"
done

# Check Grafana health
curl -s "$GRAFANA_URL/api/health" | jq .

# Get Grafana build info
curl -s "$GRAFANA_URL/api/frontend/settings" | jq '.buildInfo'

# Count dashboards per folder
curl -s -H "Authorization: Bearer $TOKEN" "$GRAFANA_URL/api/search?type=dash-db" \
  | jq 'group_by(.folderTitle) | map({folder: .[0].folderTitle, count: length})'

# Find dashboards not viewed in 90 days (requires admin)
curl -s -H "Authorization: Bearer $TOKEN" "$GRAFANA_URL/api/search?type=dash-db" \
  | jq '[.[] | select(.sortMeta < (now - 7776000))] | length'