Grafana is the visualization layer for modern observability — Prometheus metrics, Loki logs, ClickHouse analytics, and dozens of other sources, all rendered as dashboards. Running it yourself is straightforward, but a *durable* Grafana deployment needs an external database and a provisioning strategy so your dashboards and data sources survive container restarts. Here's how to do it right.
The persistence problem
Out of the box, Grafana stores its configuration — users, dashboards, data sources, API keys — in an embedded SQLite database. In a stateless container, that file is wiped on every redeploy. You have two complementary fixes:
- 1External database for Grafana's own state.
- 2Provisioning (config-as-code) so data sources and core dashboards are recreated declaratively on every boot.
Use both. The external DB persists user-created content; provisioning version-controls the foundational setup.
Step 1: Configure the external database
Grafana supports PostgreSQL and MySQL for its backend store. Configure via environment variables:
GF_DATABASE_TYPE=postgres
GF_DATABASE_HOST=<host>:5432
GF_DATABASE_NAME=grafana
GF_DATABASE_USER=<user>
GF_DATABASE_PASSWORD=<password>
GF_DATABASE_SSL_MODE=requireGrafana creates its schema automatically on first boot.
Step 2: Core settings
GF_SERVER_ROOT_URL=https://grafana.yourdomain.com
GF_SECURITY_ADMIN_USER=admin
GF_SECURITY_ADMIN_PASSWORD=<strong-password>
# pin this so signed content stays valid across restarts
GF_SECURITY_SECRET_KEY=<random 32+ chars>The GF_SECURITY_SECRET_KEY encrypts data-source credentials stored in the database — keep it stable.
Step 3: Provision data sources as code
Drop a YAML file at /etc/grafana/provisioning/datasources/datasources.yaml:
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
- name: Loki
type: loki
access: proxy
url: http://loki:3100Because this is declarative, redeploying never loses your data sources.
Step 4: Provision dashboards as code
Reference a folder of dashboard JSON files:
# /etc/grafana/provisioning/dashboards/dashboards.yaml
apiVersion: 1
providers:
- name: default
folder: 'Provisioned'
type: file
options:
path: /var/lib/grafana/dashboardsExport dashboards from the UI as JSON, commit them to your repo, and they're recreated on every deploy. This is the cleanest way to keep dashboards in version control.
Step 5: Deploy on PandaStack
Grafana publishes an official Docker image, so this fits the container app workflow perfectly. The provisioning files live in your repo, baked into the image via a small Dockerfile:
FROM grafana/grafana:latest
COPY provisioning/ /etc/grafana/provisioning/
COPY dashboards/ /var/lib/grafana/dashboards/Then:
- 1Provision a managed PostgreSQL database for Grafana's state.
- 2Create a container app from your repo (PandaStack builds the Dockerfile with rootless BuildKit).
- 3Link the database and set the
GF_DATABASE_*and security variables. - 4Attach a custom domain — automatic SSL gives you HTTPS.
Step 6: Authentication and sharing
For a team deployment, don't rely on the single admin login:
- Configure OAuth/SSO (
GF_AUTH_*variables) so people sign in with your identity provider. - Disable anonymous access unless you specifically want public dashboards.
- Use Grafana's folder permissions and teams for access control.
GF_AUTH_ANONYMOUS_ENABLED=false
GF_USERS_ALLOW_SIGN_UP=falseComparison: persistence strategies
| Approach | Persists user dashboards | Version controlled | Survives redeploy |
|---|---|---|---|
| SQLite only | No | No | No |
| External DB only | Yes | No | Yes |
| Provisioning only | No (read-only) | Yes | Yes |
| External DB + provisioning | Yes | Partly | Yes |
The last row is the production answer.
Honest caveats
Grafana visualizes data; it doesn't store time-series itself. You still need Prometheus, Loki, ClickHouse, or another backend behind it. Also, provisioned dashboards are read-only in the UI by default — if your team likes editing dashboards live, lean on the external database for those and reserve provisioning for the canonical set.
Wrapping up
A bulletproof Grafana deployment combines an external PostgreSQL backend with config-as-code provisioning. The database keeps live edits; provisioning keeps your foundation reproducible and in Git. Together they make redeploys boring — which is exactly what you want from your dashboards.
PandaStack's managed PostgreSQL and Dockerfile builds make this an easy stack to stand up, and the free tier covers a small team's Grafana. Get started at https://dashboard.pandastack.io.
References
- Grafana documentation: https://grafana.com/docs/grafana/latest/
- Grafana database configuration: https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#database
- Grafana provisioning: https://grafana.com/docs/grafana/latest/administration/provisioning/
- Run Grafana with Docker: https://grafana.com/docs/grafana/latest/setup-grafana/installation/docker/
- Grafana configuration reference: https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/