Intro to observability as code: managing dashboards with GitOps

Observability as code treats monitoring, dashboards, and alerting the same way you treat application code: stored in Git, reviewed, tested, and deployed via automated pipelines. For teams adopting GitOps, dashboards are a natural next step to bring under source control. This article explains the why and how of managing dashboards with GitOps, compares common approaches, and gives practical examples you can adapt to your environment.

Why treat dashboards as code?

Grafana and many tools now provide first-class support for “observability as code”—new APIs, SDKs, and UI-driven Git sync features make it easier to integrate dashboards into GitOps workflows.(grafana.com)

Two common GitOps patterns for dashboards

  1. Operator / Kubernetes CRD approach
    • Use a Kubernetes operator (e.g., Grafana Operator) that watches custom resources (CRs) in a Git-backed GitOps tool (Argo CD, Flux) and reconciles them into Grafana. Dashboards are stored as CR YAML in your repo; Argo CD applies them and the operator ensures Grafana reflects the desired state. This fits teams already using GitOps for cluster config and prefer Kubernetes-native management.(github.com)
  2. API / CLI / SDK + CI approach
    • Generate dashboard JSON or code with a language SDK (Foundation SDK), validate in CI, and push to Grafana via a CLI (grafanactl), Terraform provider, or direct API calls from CI/CD pipelines. This pattern works well when you don’t want cluster-level operators or when dashboards live alongside application repos and are deployed via existing CI pipelines. Grafana’s Foundation SDK, new CLI tooling, and Terraform resources are aimed at making this workflow smoother.(grafana.com)

What’s changed recently (short overview)

Example: dashboards-as-CRD (operator + Argo CD) This is the simplest operator-managed dashboard flow: your repo contains a Kubernetes manifest for a GrafanaDashboard CR that the Grafana Operator reconciles.

Example GrafanaDashboard CR (shortened):

apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
  name: example-dashboard
  namespace: monitoring
spec:
  resyncPeriod: 30s
  instanceSelector:
    matchLabels:
      dashboards: "grafana"
  json: >
    {
      "id": null,
      "title": "Example Dashboard",
      "panels": [
        {
          "type": "graph",
          "title": "CPU Usage",
          "targets": [
            { "expr": "sum(rate(container_cpu_usage_seconds_total[5m]))", "format": "time_series" }
          ]
        }
      ]
    }

Pros and cons of the operator approach

Example: CI-driven API/CLI approach When you prefer pipelines to handle the push, generate dashboards using an SDK or template and deploy them with a CI job.

Sample GitHub Actions job (conceptual):

name: Deploy Grafana dashboards
on: [push]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Validate JSON
        run: jq empty dashboards/*.json
      - name: Push dashboards to Grafana
        env:
          GRAFANA_API_KEY: $
        run: |
          grafanactl push --instance https://grafana.example.com dashboards/

Pros and cons of the CI/API approach

Choosing between operator and CI approaches

Best practices and patterns

Notable tooling and features to know about

A short checklist before you go live

Conclusion Bringing dashboards under GitOps brings the same benefits you expect from modern infrastructure workflows: reproducibility, traceability, and safer change management. You can choose a Kubernetes-native operator path (perfect when you already use Argo CD/Flux) or a CI-driven SDK/CLI path (handy when you prefer to keep dashboards with application code). Recent additions from Grafana—versioned APIs, SDKs, grafanactl, and Git Sync—make both patterns more practical and production-ready. Start small, add CI validations, and standardize how dashboards are generated—over time you’ll get more reliable, auditable observability that scales with your team.(grafana.com)