Docker Compose: Prometheus + Grafana locally
Prometheus and Grafana with Docker Compose locally: prometheus.yml, volumes for the data, connecting Grafana to Prometheus by service name, and what this stack monitors and what it does not.
Prometheus collects metrics and Grafana draws them in dashboards. Together they are the most common way to get started with monitoring. This guide sets them up locally so you learn the flow, and it is honest about something many tutorials skip: what this stack actually monitors and what it does not.
compose.yaml for Prometheus + Grafana
services:
prometheus:
image: prom/prometheus:v2.54.1
restart: unless-stopped
ports:
- "127.0.0.1:9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
grafana:
image: grafana/grafana:11.2.0
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD}
depends_on:
- prometheus
volumes:
- grafana_data:/var/lib/grafana
volumes:
prometheus_data:
grafana_data:Prometheus needs its configuration. Create a prometheus.yml next to the compose:
global:
scrape_interval: 15s
scrape_configs:
- job_name: prometheus
static_configs:
- targets: ["localhost:9090"]And the .env.examplewith Grafana's admin password:
GRAFANA_ADMIN_PASSWORD=CHANGE_ME
Check that they start
docker compose up -d # Prometheus: status of the targets it scrapes # http://localhost:9090/targets # Prometheus health endpoint # http://localhost:9090/-/healthy # Grafana: login admin / GRAFANA_ADMIN_PASSWORD # http://localhost:3000
If Grafana does not load, check docker compose logs grafana: the most common issues are a permissions problem on the grafana_data volume or port 3000 already being taken by something else on your machine.
What it monitors... and what it does not
Here is the part that prevents false expectations. Prometheus only collects metrics from the targets you list in scrape_configs. The prometheus.yml above only scrapes itself, so at first you will only see metrics about Prometheus itself.
- To monitor your app: your app must expose a
/metricsendpoint and you add it as a newjobinscrape_configspointing atyour-service:port. - For host metrics (CPU, memory, disk) you usually add
node_exporteras a separate service. - What this does NOT cover: logs and traces. Full observability is more than metrics; this stack is only the metrics leg.
Connect Grafana to Prometheus
In Grafana, when adding the Prometheus data source, the URL is http://prometheus:9090: the service name, not localhost. To make the stack reproducible and not dependent on clicks, you can keep the data source and dashboards configured with provisioning (files mounted at /etc/grafana/provisioning).
In short: this is an excellent sandbox to learn metrics and dashboards, not a production-ready observability system.
Next steps
Generate this stack in the Docker Compose generator and head back to the examples index.
Frequently asked questions
- What does this stack monitor as-is?
- Almost nothing yet, and that is important to understand. Prometheus only collects metrics from the targets you list in scrape_configs. The example prometheus.yml only scrapes itself (localhost:9090), so you will see metrics about Prometheus itself, not your app or your machine. To monitor your app, it must expose a /metrics endpoint and you add it as a job; for the host, node_exporter is usually added.
- Is this production observability?
- No. It is a local environment to learn Prometheus and Grafana and prototype dashboards. Real observability also includes logs and traces, sized retention and storage, high availability, alerts that actually reach someone and often long-term remote storage. Here you cover only the metrics part, and on a single instance.
- In Grafana, what URL do I use for the Prometheus data source?
- http://prometheus:9090, using the service name, not localhost. Grafana and Prometheus are different containers on the same Compose internal network, so they find each other by name. Inside the Grafana container, localhost would be Grafana.
- What are the two volumes for?
- prometheus_data stores the time-series database (the metrics it collects) in /prometheus. grafana_data stores Grafana's configuration in /var/lib/grafana: users, data sources and dashboards you create through the interface. Without those volumes, you would lose metrics and dashboards on every docker compose down.
- Can I have dashboards and the data source preconfigured?
- Yes, with provisioning. Instead of configuring it by hand each time, Grafana can read provisioning files mounted as a volume at startup (data sources and dashboards under /etc/grafana/provisioning). It is optional for this example, but it is what you would do to make the stack reproducible and not dependent on clicks.