Skip to main content

Grafana Docker Compose Example

This example is a small, working docker-compose.yml for Grafana, the open-source dashboarding platform. Copy it and Grafana runs on port 3000, with its database, users and dashboards on a persistent volume at /var/lib/grafana.

The stack includes:

  • A Grafana container with a persistent volume
  • An admin password set from the environment
  • An optional Prometheus service, wired in as a data source from a file
  • Local-only port binding for security
  • Simple reverse-proxy instructions (Caddy example)

1. docker-compose.yml​

Create a folder and add:

services:
grafana:
image: grafana/grafana:latest
restart: always
ports:
- "127.0.0.1:3000:3000"
environment:
- GF_SERVER_DOMAIN=grafana.example.com
- GF_SERVER_ROOT_URL=https://grafana.example.com
volumes:
- grafana_data:/var/lib/grafana

volumes:
grafana_data:

Start the stack:

docker compose up -d

Grafana will be available locally at:

http://localhost:3000

Default login:

Username: admin
Password: admin

You will be prompted to change the password on first login.

To skip the default password, set it in the environment before the first start:

environment:
- GF_SECURITY_ADMIN_PASSWORD=change-me

Grafana reads this value only when it creates its database. If the volume already exists, changing it does nothing. Reset the password from the CLI instead:

docker compose exec grafana grafana cli admin reset-admin-password new-password

Add Prometheus as a data source​

Grafana shows data; it does not collect it. The most common pair is Grafana with Prometheus. Add a Prometheus service to the same file, and let Grafana load the data source from a provisioning file, so you never click through the UI:

services:
grafana:
image: grafana/grafana:latest
restart: always
ports:
- "127.0.0.1:3000:3000"
volumes:
- grafana_data:/var/lib/grafana
- ./provisioning:/etc/grafana/provisioning
depends_on:
- prometheus

prometheus:
image: prom/prometheus:latest
restart: always
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus

volumes:
grafana_data:
prometheus_data:

provisioning/datasources/prometheus.yml:

apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true

prometheus.yml (Prometheus scrapes itself, so you have data at once):

scrape_configs:
- job_name: prometheus
static_configs:
- targets: ["localhost:9090"]

The data source URL uses the service name prometheus, not localhost. Inside the Grafana container, localhost is Grafana itself. See Docker Compose networks for how service names resolve.

Install plugins​

Set GF_PLUGINS_PREINSTALL to a comma-separated list of plugin IDs. Grafana installs them in the background on each start, into /var/lib/grafana/plugins:

environment:
- GF_PLUGINS_PREINSTALL=grafana-clock-panel,grafana-polystat-panel

Bind mount instead of a named volume​

The Grafana image runs as user ID 472, not root. If you swap the named volume for a host folder such as ./grafana-data:/var/lib/grafana, Grafana fails to start with a permission error on that folder. Give the folder to that user first:

mkdir -p grafana-data && sudo chown -R 472:0 grafana-data

2. Add a Reverse Proxy (Caddy example)​

To expose Grafana with HTTPS, use a simple Caddyfile:

grafana.example.com {
reverse_proxy localhost:3000
}

Reload Caddy:

systemctl reload caddy

Caddy will automatically request and renew the TLS certificate.


3. Optional: Auto-Start with systemd​

# /etc/systemd/system/grafana.service
[Unit]
Description=Grafana (Docker Compose)
After=network.target

[Service]
Type=oneshot
WorkingDirectory=/root/grafana
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

Enable the service:

systemctl enable grafana
systemctl start grafana

4. Deploy on Hostim.dev Instead​

If you don’t want to manage servers, proxies, or systemd:

Hostim.dev automatically configures HTTPS, domains, restarts, logs, and persistent storage.


FAQ​

Frequently asked questions

What is the default Grafana port in Docker?

Grafana listens on port 3000 inside the container. The example publishes it as 127.0.0.1:3000:3000, so it is reachable only from the host, and a reverse proxy such as Caddy serves it on HTTPS.

What is the default Grafana login?

The username is admin and the password is admin. Grafana asks you to change it on first login. To set it in advance, add GF_SECURITY_ADMIN_PASSWORD to the environment before the first start.

Where does Grafana store its data in Docker?

In /var/lib/grafana inside the container. That folder holds the SQLite database with users, dashboards and data sources, plus installed plugins. Mount a named volume there, or your dashboards are lost when the container is removed.

Why does changing GF_SECURITY_ADMIN_PASSWORD not change my password?

Grafana reads it only when it creates its database on the first start. After that the password lives in the database on the volume. Reset it with: docker compose exec grafana grafana cli admin reset-admin-password new-password.

How do I connect Grafana to Prometheus in Docker Compose?

Run both services in the same Compose file and use http://prometheus:9090 as the data source URL. The service name resolves on the Compose network. Do not use localhost, because inside the Grafana container localhost is Grafana itself.

Why does Grafana fail with a permission error on a bind mount?

The Grafana image runs as user ID 472, so it cannot write to a host folder owned by root. Run sudo chown -R 472:0 on the folder, or use a named Docker volume, which Docker creates with the right owner.