Provision Grafana Dashboards and Datasources from Files Instead of Clicking Through the UI
Configure Grafana datasources and dashboards as version-controlled files that load automatically at startup, with secrets kept in environment variables and verification steps for each provider.
22 Jun 2026, 13:37 UTC

If you rebuild a Grafana instance — a fresh container, a new VM, a disaster recovery restore — everything configured by hand in the UI is gone unless you exported it first. Provisioning fixes this: Grafana reads YAML and JSON files from disk at startup and creates datasources and dashboards from them automatically. The files live in version control, so every environment gets the same setup and changes are reviewable like any other code.
This guide covers the file layout, a working datasource and dashboard provider configuration, how to keep secrets out of committed YAML, and how to verify the providers actually loaded.
What provisioning does and does not do
Grafana scans a provisioning directory at startup. Each YAML file in a subdirectory (datasources, dashboards, alerting, plugins) defines a provider. A dashboard provider points at a folder of dashboard JSON files; a datasource file lists datasources inline.
Two behaviors surprise people:
- Provisioned dashboards are read-only in the UI by default. Users cannot save edits or delete them from the browser; the file on disk is the source of truth.
- If you delete a dashboard JSON file from disk, Grafana removes the dashboard on the next reload — unless you set
disableDeletion: true. Cleanup happens through Git, not the UI.
Option names and paths vary slightly across Grafana versions. The examples below match Grafana 9/10/11 conventions; check the docs for your installed version before copying blindly.
Prerequisites
- A Grafana installation you can restart (package install, Docker, or Kubernetes — the same files work in all three).
- Write access to the provisioning directory:
/etc/grafana/provisioningon package installs, or a mounted volume in containers. - A dashboard exported as JSON if you want to provision an existing one: open the dashboard, go to Dashboard settings → JSON model, copy the JSON.
Step 1: Provision a datasource
Create /etc/grafana/provisioning/datasources/prometheus.yaml (run as root or a user with write access to that path):
apiVersion: 1\n\ndatasources:\n - name: Prometheus\n type: prometheus\n access: proxy\n url: http://prometheus:9090\n isDefault: true\n editable: falseaccess: proxy means Grafana's backend makes the requests, so the Prometheus URL only needs to be reachable from the Grafana server, not from users' browsers. editable: false locks the datasource against UI changes.
Keep credentials out of Git
For authenticated datasources, interpolate environment variables instead of committing plaintext:
datasources:\n - name: Postgres\n type: postgres\n url: db.internal:5432\n user: grafana_reader\n jsonData:\n database: metrics\n secureJsonData:\n password: ${GRAFANA_DB_PASSWORD}Grafana substitutes ${GRAFANA_DB_PASSWORD} from its process environment at load time. In Docker, pass it with -e GRAFANA_DB_PASSWORD=...; in Kubernetes, inject it from a Secret. Committing a real password in this file is one of the most common provisioning mistakes.
Step 2: Provision dashboards from a folder
Create /etc/grafana/provisioning/dashboards/default.yaml:
apiVersion: 1\n\nproviders:\n - name: default\n orgId: 1\n folder: ''\n type: file\n updateIntervalSeconds: 30\n allowUiUpdates: false\n options:\n path: /var/lib/grafana/dashboards\n foldersFromFilesStructure: trueKey options:
options.path— directory containing dashboard JSON files.foldersFromFilesStructure: truemaps subdirectories to Grafana folders, sodashboards/infra/nodes.jsonlands in an \"infra\" folder.updateIntervalSeconds: 30— Grafana rescans the path every 30 seconds, so edits to JSON files appear without a restart. Omit it to load only at startup.allowUiUpdates: false— the default-safe choice. If set totrue, users can edit provisioned dashboards in the UI, but those edits are not written back to disk and are lost on reload. Leave it false unless you deliberately want throwaway edits.
Now place your exported dashboard JSON into /var/lib/grafana/dashboards/. One tip: remove or null out the top-level \"id\" field in the JSON so Grafana assigns a fresh ID and avoids collisions with manually created dashboards.
Step 3: Restart and verify
Restart Grafana (systemctl restart grafana-server on a package install, or restart the container/pod). Then check:
- Logs first. Look for provisioning errors at startup:
journalctl -u grafana-server | grep -i provisionordocker logs <grafana-container>. YAML indentation errors are the usual culprit — a misindented provider file can fail to load without an obvious UI symptom. - Datasource. Under Connections → Data sources, the datasource should appear with a \"Provisioned\" label and (if
editable: false) no edit form. - Dashboard. It should appear in the Dashboards list. Open it and try to save a change — you should be blocked, confirming
allowUiUpdates: falsetook effect. - Reload behavior. Edit the JSON file on disk (change a panel title), wait for one
updateIntervalSecondscycle, and refresh the dashboard. The change should appear. Then delete the file and confirm the dashboard disappears on the next cycle.
Containers and Kubernetes
The workflow is identical; only the file delivery changes. In Docker, mount the provisioning tree and dashboard folder as volumes:
docker run -d \\\n -v ./provisioning:/etc/grafana/provisioning:ro \\\n -v ./dashboards:/var/lib/grafana/dashboards:ro \\\n -e GRAFANA_DB_PASSWORD=\"$GRAFANA_DB_PASSWORD\" \\\n grafana/grafanaIn Kubernetes, package the YAML and dashboard JSON into ConfigMaps and mount them at the same paths. Dashboards larger than the ConfigMap size limit (1 MiB) need a sidecar or an init container that pulls from Git instead.
Recovery options
Provisioning changes state only at load time, so recovery is straightforward: remove or fix the offending file and restart (or wait for the rescan). If a bad dashboard JSON causes a provider error, Grafana logs it and skips that file — other providers still load. If a provisioned datasource points at the wrong host, fix the YAML and restart; do not try to correct it in the UI, since the file will overwrite your change on the next load.
Limitations
- Provisioning configures datasources and dashboards, not users, teams, or orgs (those need the API or Terraform).
- UI edits with
allowUiUpdates: trueare ephemeral and silently diverge from the files — treat that option as a debugging convenience, not a workflow. - Verify option names against the docs for your exact Grafana version before rolling this out broadly.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.