โšก Onwuachi Control Plane

KB-OBS-001: Grafana Dashboards Vanish After Reset

Symptom

Grafana prompts for a mandatory password reset on login (a sign it has reset to factory defaults). Hitting the API confirms it:

curl -s -u admin:admin http://localhost:4000/api/search
# []

Dashboards that were previously working โ€” Node Exporter Full, HAProxy, Prometheus 2.0 Overview โ€” are gone. Datasources, however, may still be present.

Root Cause

Grafana persists two very different kinds of state, and they are not equally durable:

State typeWhere it livesSurvives DB reset?
UI-imported dashboardgrafana.db (SQLite) onlyโŒ No
File-provisioned dashboardJSON file on disk, referenced by dashboards.ymlโœ… Yes
File-provisioned datasourceYAML file on disk (datasources/*.yml)โœ… Yes

If a dashboard was added via Dashboards โ†’ New โ†’ Import โ†’ (enter grafana.com ID), it is written only to grafana.db. If that database is ever reset โ€” via a container recreate without a persistent volume, an accidental rm, a corrupted SQLite file, or any other reset event โ€” the dashboard is gone with no way to recover it short of re-importing.

Datasources set up via a provisioning YAML file (e.g. /etc/grafana/provisioning/datasources/datasource.yml) do not have this problem, because Grafana re-reads and re-creates them from the file on every startup, regardless of what’s in the database. You can confirm a datasource was provisioned this way if the API reports it as read-only:

curl -s -u admin:admin http://localhost:4000/api/datasources
# "readOnly": true  <- confirms provisioning, not manual entry

Diagnostic Steps

  1. Check Docker mounts to rule out a missing volume:
   sudo docker inspect grafana --format '{{json .Mounts}}'
  1. Check whether the dashboard-provisioning folder actually contains real dashboard JSON, or just an empty placeholder:
   ls -la /opt/grafana/dashboards/
  1. Check the provisioning configs to confirm what Grafana should be autoloading on startup:
   cat /etc/grafana/provisioning/dashboards/dashboards.yml
   cat /etc/grafana/provisioning/datasources/datasource.yml
  1. Hit the Grafana API directly rather than guessing from file timestamps โ€” this is the ground truth of what Grafana currently knows about:
   curl -s -u admin:admin http://localhost:4000/api/search
   curl -s -u admin:admin http://localhost:4000/api/datasources

Fix

Download the dashboard JSON directly from grafana.com and drop it into the folder your dashboards.yml provider already points at:

cd /opt/grafana/dashboards

sudo curl -s https://grafana.com/api/dashboards/1860/revisions/37/download \
  -o node-exporter-full.json

sudo curl -s https://grafana.com/api/dashboards/3662/revisions/2/download \
  -o prometheus-stats.json

sudo curl -s https://grafana.com/api/dashboards/789/revisions/1/download \
  -o haproxy-native.json

# remove any dead placeholder files
sudo rm -f /opt/grafana/dashboards/ops-overview.json

# fix ownership so the grafana container (uid 472) can read them
sudo chown -R 472:472 /opt/grafana/dashboards

Grafana’s file provider polls for changes automatically (default every 10s), but a restart guarantees a clean pickup:

sudo docker restart grafana

Verify:

curl -s -u admin:admin http://localhost:4000/api/search

Prevention

System Context

โ† Back to Kb