Grafana Grafana

Deploy Grafana on Kubernetes with Helm and Provisioning Values

Run Grafana from a Helm chart where data sources, dashboards, and settings all come from values files.

15 min Intermediate

On Kubernetes, the Grafana Helm chart turns the provisioning folders into configuration you write in values.yaml. The chart renders ConfigMaps and Secrets, mounts them at the paths Grafana expects, and restarts or reloads when they change. That makes Grafana a good fit for GitOps, since the values file is the only thing you edit. This guide builds a values file that provisions a data source, dashboards, alerting, and server settings.

Chart hosting is changing: some Grafana Labs charts are moving to a community-maintained repository. Before installing, check the chart’s README for the repository URL and chart version to use, and pin that version. The values keys below follow the chart’s documented structure, but confirm them in the values.yaml of the version you install.

Install

helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
helm install grafana grafana/grafana \
  --namespace monitoring --create-namespace \
  --version <chart-version> \
  -f values.yaml

If the chart has moved, replace the repository URL with the one in the README. Keep the command, the --version, and the values file in Git so the installation is repeatable.

Server settings: grafana.ini

Anything you would put in grafana.ini goes under the grafana.ini key, organized by section:

grafana.ini:
  server:
    root_url: https://grafana.example.com/
  users:
    allow_sign_up: false
  analytics:
    reporting_enabled: false
    check_for_updates: false
  log:
    mode: console

Values that must not be committed can reference environment variables or files, as in the configuration guide (for example password: $__file{/etc/secrets/db/password}).

Admin credentials and other secrets

Use an existing Kubernetes Secret instead of a password in values:

admin:
  existingSecret: grafana-admin
  userKey: admin-user
  passwordKey: admin-password

envFromSecret: grafana-secrets   # every key becomes an environment variable
apiVersion: v1
kind: Secret
metadata:
  name: grafana-secrets
  namespace: monitoring
stringData:
  POSTGRES_PASSWORD: change-me
  SLACK_WEBHOOK_URL: https://hooks.slack.com/services/XXX/YYY/ZZZ

Provisioning files can then use $POSTGRES_PASSWORD and ${SLACK_WEBHOOK_URL}. Encrypt the Secret with SOPS or sync it with External Secrets rather than committing it as plain YAML (see the secrets guide).

Data sources

The datasources key is rendered into a provisioning file:

datasources:
  datasources.yaml:
    apiVersion: 1
    datasources:
      - name: Prometheus
        uid: prometheus
        type: prometheus
        access: proxy
        url: http://prometheus-server.monitoring.svc.cluster.local
        isDefault: true
        editable: false

Dashboards: three ways

1. Inline from files in the chart folder. Declare a provider and list dashboards by source:

dashboardProviders:
  dashboardproviders.yaml:
    apiVersion: 1
    providers:
      - name: default
        orgId: 1
        folder: ''
        type: file
        disableDeletion: false
        allowUiUpdates: false
        options:
          path: /var/lib/grafana/dashboards/default

dashboards:
  default:
    node-exporter:
      gnetId: 1860
      revision: 37
      datasource: Prometheus
    kubernetes-overview:
      json: |
        { "uid": "k8s-overview", "title": "Kubernetes Overview", "panels": [] }

Entries can come from grafana.com (gnetId and revision), a URL, or inline JSON. The key under dashboards (here default) must match the provider’s options.path folder name.

2. From ConfigMaps you manage yourself. Keep dashboard JSON in its own ConfigMaps (for example generated by Kustomize from the repository) and mount them:

dashboardsConfigMaps:
  default: grafana-dashboards

3. With the sidecar. A sidecar container watches the cluster for ConfigMaps (or Secrets) with a label and loads them as dashboards, so each application team can ship its own dashboard next to its manifests:

sidecar:
  dashboards:
    enabled: true
    label: grafana_dashboard
    labelValue: "1"
    searchNamespace: ALL
    folderAnnotation: grafana_folder
    provider:
      foldersFromFilesStructure: true
  datasources:
    enabled: true
    label: grafana_datasource
  alerts:
    enabled: true
    label: grafana_alert
apiVersion: v1
kind: ConfigMap
metadata:
  name: webapp-dashboard
  namespace: webapp
  labels:
    grafana_dashboard: "1"
  annotations:
    grafana_folder: Applications
data:
  webapp.json: |
    { "uid": "webapp-overview", "title": "Webapp Overview", "panels": [] }

The sidecar approach scales best across teams: no one edits Grafana’s values to add a dashboard.

Alerting

Alerting provisioning files can come from the alerting key, using the YAML formats from the alerting guide:

alerting:
  contactpoints.yaml:
    secret:
      apiVersion: 1
      contactPoints:
        - orgId: 1
          name: team-slack
          receivers:
            - uid: team-slack-1
              type: slack
              settings:
                url: ${SLACK_WEBHOOK_URL}
                recipient: '#alerts'
  policies.yaml:
    apiVersion: 1
    policies:
      - orgId: 1
        receiver: team-slack

The secret: wrapper places that file in a Secret instead of a ConfigMap. Check the chart’s values.yaml for the exact shape in your version. Alert rules can come from the same key or from the sidecar’s alerts setting.

Persistence and replicas

persistence:
  enabled: true
  size: 5Gi
replicas: 1

SQLite on a persistent volume supports one replica. For high availability, point Grafana at PostgreSQL through grafana.ini and raise replicas.

Deploy with Flux

If you use Flux, the chart becomes a HelmRelease, and the values above go in spec.values:

apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: grafana
  namespace: monitoring
spec:
  interval: 10m
  chart:
    spec:
      chart: grafana
      version: "<chart-version>"
      sourceRef:
        kind: HelmRepository
        name: grafana
        namespace: flux-system
  valuesFrom:
    - kind: ConfigMap
      name: grafana-values

A change to the values in Git rolls out the change automatically. See the Helm guide in the Flux series for HelmRepository, remediation, and drift detection.

Verify

kubectl get pods -n monitoring
kubectl port-forward svc/grafana 3000:80 -n monitoring &
curl -s http://localhost:3000/api/health
kubectl logs deploy/grafana -n monitoring -c grafana | grep -i provisioning

If a dashboard or data source is missing, check the provisioning log lines first, then the sidecar’s logs (-c grafana-sc-dashboard) for label or namespace problems.

Popular Guides