Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Grafana with git-friendly, two-way dashboard sync

Dashboards live as JSON files in dashboards/ and stay in sync with a running Grafana in both directions, using Grafana 12+'s new provisioning system (the engine behind Git Sync, pointed at a mounted local path instead of GitHub — no remote or webhook needed):

  • File → Grafana: the repository connection pulls the directory on a ~60 second cycle, so edits to the JSON (e.g. after a git pull) appear in Grafana.
  • Grafana → file: pressing Save in the UI writes the dashboard straight back to its JSON file — no sidecar, no export script. The save dialog even shows which file it's writing to.

Usage

docker compose up -d

Open http://localhost:3000 — no login needed (anonymous access with an Admin role is enabled for local development; remove the three GF_AUTH_* lines in docker-compose.yml to restore the login page, admin / admin). Edit the Example Dashboard, press Save. The file updates immediately:

git diff dashboards/
git add dashboards/ && git commit -m "Update dashboard"

The repository appears in Grafana as a Git dashboards folder. To create a new dashboard that's managed by the repository, save it into that folder: the save dialog switches to the provisioned flow (it asks for a filename) and writes the JSON file alongside the others. Dashboards saved anywhere else go to Grafana's database only — that's how you keep scratch dashboards out of git. To delete a managed dashboard, delete its JSON file; Grafana removes it on the next sync.

The example app and dashboard

The stack includes a .NET 10 minimal Web API (app/) instrumented with OpenTelemetry, displayed by the community dashboard OpenTelemetry dotnet webapi (20568) (dashboards/otel-dotnet-webapi.json):

  • Metrics are exposed on http://localhost:8080/metrics by the OTel Prometheus exporter and scraped by Prometheus (http://localhost:9090). ASP.NET Core, Kestrel, HTTP client, runtime, and process instrumentation cover every panel in the dashboard.
  • Logs are pushed over OTLP to Loki (http://localhost:3100), which feeds the dashboard's Logs row.
  • A traffic container hits the API (including a 404 and a failing endpoint) every couple of seconds so the panels have data.

Three deliberate compatibility choices, worth knowing before upgrading anything:

  • OpenTelemetry.Instrumentation.Runtime is pinned to 1.9.0: from 1.10 the package uses the runtime's built-in System.Runtime meter on .NET 9+, which renames every metric to the new dotnet_* semconv names — but this dashboard (including the service_name variable that all panels depend on) queries the old process_runtime_dotnet_* names, which 1.9.0 emits on any runtime version. If you upgrade the pin, switch to a dashboard built for the new names, e.g. the ASP.NET Core OTel dashboards for .NET 9+.
  • Prometheus runs with metric_name_validation_scheme: legacy (prometheus/prometheus.yml): Prometheus 3 otherwise negotiates UTF-8 names with the exporter and stores dotted names (http.server.request.duration) the dashboard can't find.
  • The dashboard filters everything by service_name / service_version / deployment_environment / host_name labels. In OTLP-collector setups those come from resource attributes; with a direct scrape they're attached as static labels in the Prometheus scrape config — keep them in step with the resource attributes in app/Program.cs.

How it fits together

  • docker-compose.yml — Grafana 13 with:
    • GF_FEATURE_TOGGLES_ENABLE=provisioning,kubernetesDashboards — the new provisioning system (default-on in v13+, needed explicitly on v12.x);
    • GF_PATHS_PERMITTED_PROVISIONING_PATHS=git-dashboards/ — allowlists the directory (relative to Grafana's working dir /usr/share/grafana, where ./dashboards is mounted);
    • a one-shot provisioning-setup container that registers the directory as a repository connection via the provisioning API.
  • setup/create-repository.sh — the bootstrap call. It does exactly what Administration → Provisioning → Configure file provisioning does in the UI; use the UI instead if you prefer clicking through it once.
  • dashboards/example.json — the dashboard, in Grafana's resource format (apiVersion: dashboard.grafana.app/v2, content under spec:). Classic dashboard JSON is accepted on first import and converted to this format on first save from the UI.

You can watch sync state under Administration → Provisioning, or force a pull instead of waiting for the interval:

curl -u admin:admin -H 'Content-Type: application/json' \
  -X POST http://localhost:3000/apis/provisioning.grafana.app/v0alpha1/namespaces/default/repositories/git-dashboards/jobs \
  -d '{"action": "pull", "pull": {}}'

Caveats

  • The provisioning system is experimental (Grafana 12/13): the provisioning.grafana.app/v0alpha1 API used by the bootstrap script may change between releases. The UI setup path is the stable-ish alternative.
  • File → Grafana sync runs on a ~60s cycle for local paths (UI saves hit the file instantly). If both sides change in the same window, the repository (file) side wins on the next pull.
  • The sync target is folder: everything in dashboards/ lives under the "Git dashboards" folder, and new dashboards saved into that folder become repository-managed. The alternative folderless target imports files at the instance root instead, but new UI dashboards then default to plain database saves — there's no folder to opt them into the repository.
  • Not available on Grafana Cloud (it has Git Sync to GitHub instead).
  • The example uses admin/admin basic auth for bootstrap — swap in a service-account token for anything beyond local use.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages