Infrastructure guide
Grafana Docker Compose: Persist Dashboards and Provision What Matters
Deploy a version-pinned Grafana container with persistent dashboards, reproducible provisioning, backups, and an upgrade path that protects state.
Published and reviewed by OpenAlt · September 25, 2026

TL;DR
A disposable Grafana container loses dashboards and configuration when it is removed. Mount /var/lib/grafana to a named Docker volume or bind mount because Grafana stores its embedded SQLite database, users, dashboards, plugins, and other data there.
Use file provisioning for configuration that should be reproducible, such as data sources and dashboard definitions. Use the volume for durable runtime state. Pin the Grafana image version, bootstrap the admin password from a secret file, back up the volume consistently, and never use default credentials as the operational state.
TOC
- Persistence and provisioning
- Version-pinned Docker Compose
- Provision data sources and dashboards
- Verify the installation
- Back up, upgrade, and roll back
- Troubleshooting
- FAQ
Persistence and provisioning
Persistence and provisioning solve different problems.
Persistence keeps Grafana’s runtime state between container replacements. Grafana listens on port 3000 and stores its embedded SQLite configuration, users, dashboards, and other data under /var/lib/grafana. Changes made only inside the container filesystem disappear when that container is deleted, so a volume or bind mount is required.
Provisioning makes selected configuration reproducible from files. It is ideal for data sources, dashboard providers, alerting configuration, and dashboard JSON that should be recreated consistently across environments.
A useful operating model is:
| Concern | Recommended location | Purpose |
|---|---|---|
| Grafana database and runtime state | Named volume at /var/lib/grafana | Preserve dashboards, users, settings, and plugins |
| Data sources | /etc/grafana/provisioning/datasources | Recreate connection definitions |
| Dashboard providers | /etc/grafana/provisioning/dashboards | Tell Grafana where dashboard files live |
| Dashboard JSON | A provisioned dashboard directory | Reproduce dashboards from version-controlled files |
| Admin password | Docker secret file | Keep bootstrap credentials out of Compose YAML |
Provisioning is not a replacement for backups. A provisioned dashboard may be reproducible, but the Grafana database still contains important operational state.
Version-pinned Docker Compose
Pin the image instead of using latest. The current stable Docker image listed by Grafana is 13.2.1, dated September 1, 2026.
services:
grafana:
image: grafana/grafana:13.2.1
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_USER: operator
GF_SECURITY_ADMIN_PASSWORD__FILE: /run/secrets/grafana_admin_password
GF_PATHS_PROVISIONING: /etc/grafana/provisioning
secrets:
- grafana_admin_password
volumes:
- grafana_data:/var/lib/grafana
- ./provisioning:/etc/grafana/provisioning:ro
restart: unless-stopped
secrets:
grafana_admin_password:
file: ./secrets/grafana_admin_password
volumes:
grafana_data:
The file ./secrets/grafana_admin_password should contain the chosen administrator password and should not be committed to source control. The __FILE setting lets the container read that value from the mounted secret rather than exposing it in Compose.
This is an initial bootstrap mechanism. If Grafana has already initialized its database, changing the environment variable does not automatically rotate the existing administrator password. Change the password through Grafana’s supported user-management flow and update the secret for future rebuilds.
The named volume is managed by Docker and survives normal container replacement. Do not add -v to a teardown command unless you intentionally want to remove the stored Grafana database.


Provision data sources and dashboards
Create a small provisioning tree beside the Compose file:
provisioning/
├── dashboards/
│ ├── providers.yaml
│ └── json/
└── datasources/
└── prometheus.yaml
A data source definition can be concise:
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: false
The URL assumes that another Compose service is named prometheus and listens on its normal internal port. Use the actual service name and endpoint in your stack. Credentials are intentionally not included; supply them through your environment’s secret-management approach when the data source requires authentication.
Define where dashboard JSON files are loaded:
apiVersion: 1
providers:
- name: file-dashboards
orgId: 1
folder: ""
type: file
disableDeletion: true
editable: false
options:
path: /etc/grafana/provisioning/dashboards/json
Place exported dashboard JSON files in provisioning/dashboards/json. Keep those files under version control if they represent the desired state of your monitoring stack.
editable: false protects the provisioned definition from casual UI changes. If operators must edit dashboards in Grafana, enable UI editing deliberately and decide which system is authoritative. Otherwise, a later provisioning update can make file-based configuration win over an uncommitted UI change.
The official Grafana provisioning documentation covers file-based data sources and dashboards in detail.
Verify the installation
Open http://localhost:3000 or the host address mapped to port 3000. Sign in with the administrator username and the password stored in the secret file.
Then verify three separate behaviors:
- The data source appears under Grafana’s data-source settings and passes its connection test.
- Provisioned dashboards load from the expected folder.
- Removing and recreating the container does not remove the dashboard database or users.
Check container logs if provisioning fails. A valid YAML file can still point to a path that is not mounted, a service name that is unreachable, or a dashboard directory containing invalid JSON.
Back up, upgrade, and roll back
Back up /var/lib/grafana consistently, not just the Compose file. For SQLite-backed Grafana, the safest simple approach is to stop Grafana, copy or snapshot the named volume, and start Grafana again. This prevents a live SQLite write from producing an inconsistent filesystem copy.
Also back up the provisioning directory and the administrator secret through your normal protected process. The provisioning files recreate selected configuration, while the volume backup preserves the database and runtime state.
An external MySQL or PostgreSQL database can separate Grafana’s database from the container and may be useful for larger or highly available deployments. It does not automatically back up plugins, provisioning files, or every file stored under /var/lib/grafana; those still need an explicit storage plan.
For upgrades, keep the image tag pinned, back up first, then change the tag in one controlled commit. Start the new container and verify login, data sources, dashboards, and plugins. For rollback, return to the previous tag only when its database schema is compatible. If the newer version performed a migration that cannot be reversed safely, restore the pre-upgrade database backup instead of relying only on an image downgrade.
Grafana’s official Docker installation documentation explains the container port and storage paths. The official download page lists available image versions.
Troubleshooting
If Grafana cannot write to the named volume, check the container’s filesystem permissions and whether the data mount is accidentally read-only. Named volumes avoid many host UID problems. Bind mounts require the host directory to be writable by the Grafana process.
If dashboards are missing, check that the provider file is under /etc/grafana/provisioning/dashboards, that its path matches the mounted directory, and that the JSON files are present inside the container. Also check orgId, folder settings, and startup logs.
If a data source is unavailable, test the service name from the Compose network. localhost inside the Grafana container means Grafana itself, not another container.
FAQ
Where does Grafana store dashboards?
By default, Grafana stores dashboard records in its embedded SQLite database under /var/lib/grafana. Provisioned dashboard JSON files live wherever your provider configuration points, commonly under /etc/grafana/provisioning/dashboards.
Is a volume enough for backups?
No. A volume preserves data between container replacements, but it is not an independent backup. Create consistent copies or snapshots of the volume, and back up provisioning files and any external database separately.
What is provisioning?
Provisioning is Grafana’s file-based configuration mechanism. YAML and JSON files define items such as data sources, dashboard providers, and dashboards so they can be recreated consistently.
Which port does Grafana use?
Grafana listens on port 3000 by default. A Compose mapping such as "3000:3000" exposes the container port on port 3000 of the Docker host.
Should you run the Grafana Enterprise image for free?
No, not for this basic persistence and provisioning use case. The Grafana OSS image is sufficient. Choose Enterprise when you specifically need its features and have the corresponding licensing or operational reason; persistence does not depend on the Enterprise image.
For related tooling, explore OpenAlt’s Grafana profile, BI & dashboards, and monitoring tools.