This guide walks through deploying the observability stack for the application: Prometheus (metrics collection), Grafana (dashboards), and Alertmanager (alert routing). It also covers wiring the backend metrics endpoint so Prometheus can scrape it.
All configuration referenced here lives in the monitoring/ directory.
- Overview
- Prerequisites
- 1. Wire the backend metrics endpoint
- 2. Deploy Prometheus and Grafana
- 3. Import the Grafana dashboard
- 4. Load the alert rules
- 5. Configure Alertmanager notification channels
- 6. Verify the setup
- Troubleshooting
The monitoring stack consists of:
| Component | Purpose | Config file |
|---|---|---|
| Prometheus | Scrapes and stores metrics | monitoring/prometheus.yml |
| Alert rules | Defines alerting conditions | monitoring/alert-rules.yml |
| Alertmanager | Routes alerts to notification channels | monitoring/alertmanager.yml |
| Grafana | Visualizes metrics via dashboards | monitoring/grafana/ |
| Docker Compose | Local/all-in-one deployment | monitoring/docker-compose.yml |
Adjust the paths above if your repository layout differs. The steps below assume the
monitoring/directory at the repository root.
- Docker and Docker Compose (for the self-hosted option), or accounts with a managed Prometheus/Grafana provider.
- Network access from Prometheus to the backend metrics endpoint.
- SMTP credentials (for email alerts) and/or a Slack incoming webhook URL (for Slack alerts).
Prometheus scrapes metrics over HTTP. The backend must expose a metrics endpoint (commonly /metrics) in the Prometheus exposition format.
-
Ensure the backend exposes the metrics endpoint. Confirm it responds:
curl -s http://localhost:3000/metrics | headYou should see lines such as
# HELP ...and# TYPE ...followed by metric samples. -
Point Prometheus at the endpoint. In
monitoring/prometheus.yml, the scrape target is defined underscrape_configs. Update the target to match your backend host and port:scrape_configs: - job_name: "backend" metrics_path: /metrics static_configs: - targets: ["backend:3000"] # host:port of the backend metrics endpoint
- If Prometheus runs in Docker Compose on the same network, use the service name (e.g.
backend:3000). - If Prometheus runs outside the backend's network, use a reachable host/IP (e.g.
host.docker.internal:3000on Docker Desktop, or the backend's public address).
- If Prometheus runs in Docker Compose on the same network, use the service name (e.g.
-
If the metrics endpoint requires authentication, add credentials to the scrape config:
scrape_configs: - job_name: "backend" metrics_path: /metrics scheme: https basic_auth: username: metrics password_file: /etc/prometheus/metrics_password static_configs: - targets: ["backend.example.com:443"]
Mount the password file into the Prometheus container and keep it out of version control.
The repository ships a Compose file at monitoring/docker-compose.yml that starts Prometheus, Alertmanager, and Grafana together.
-
Review and adjust the Compose file and the referenced config files:
monitoring/prometheus.yml— scrape targets and rule file references.monitoring/alert-rules.yml— alerting rules.monitoring/alertmanager.yml— notification routing.
-
Start the stack:
cd monitoring docker compose up -d -
Confirm the containers are running:
docker compose ps
-
Access the UIs:
- Prometheus: http://localhost:9090
- Alertmanager: http://localhost:9093
- Grafana: http://localhost:3000 (default credentials
admin/admin— change on first login)
-
To stop the stack:
docker compose down
If you prefer managed infrastructure, use a hosted Prometheus (e.g. Grafana Cloud, Amazon Managed Service for Prometheus, Google Managed Prometheus) and a hosted Grafana.
-
Prometheus / metrics backend
-
Create the managed Prometheus instance and note its remote-write endpoint and credentials.
-
Configure your backend (or a local Prometheus agent) to remote-write metrics to the managed endpoint. If you run a local Prometheus agent, reuse
monitoring/prometheus.ymlfor scrape configs and add aremote_writeblock:remote_write: - url: https://<managed-prometheus-endpoint>/api/v1/write basic_auth: username: <user-id> password: <api-key>
-
-
Grafana
- Create the hosted Grafana instance.
- Add a data source pointing at the managed Prometheus instance (see step 3).
-
Alerting
- Managed Prometheus services typically support alert rules and Alertmanager-compatible routing. Load the rules from
monitoring/alert-rules.ymlinto the managed rule store, and configure notification channels in the provider's UI (see step 5).
- Managed Prometheus services typically support alert rules and Alertmanager-compatible routing. Load the rules from
- Log in to Grafana.
- Add the Prometheus data source:
- Go to Connections → Data sources → Add data source → Prometheus.
- Set the URL to your Prometheus instance (e.g.
http://prometheus:9090when running in Compose, or the managed endpoint URL). - Click Save & test and confirm the connection succeeds.
- Import the dashboard:
- Go to Dashboards → New → Import.
- Upload the dashboard JSON from
monitoring/grafana/(or paste its contents). - Select the Prometheus data source created above.
- Click Import.
- The dashboard should now render panels populated by the backend metrics.
If the dashboard JSON references a data source by name, ensure the name matches the one you created, or update the variable when importing.
Alert rules are defined in monitoring/alert-rules.yml.
-
Ensure Prometheus references the rule file. In
monitoring/prometheus.yml:rule_files: - /etc/prometheus/alert-rules.yml
Confirm the Compose file mounts
alert-rules.ymlto that path inside the Prometheus container. -
Reload Prometheus to pick up rule changes:
-
Via the UI: Status → Configuration → Reload (requires
--web.enable-lifecycle), or -
Via the API:
curl -X POST http://localhost:9090/-/reload
-
Or restart the container:
docker compose restart prometheus
-
-
Verify the rules loaded: go to Prometheus → Status → Rules and confirm the rules from
alert-rules.ymlappear.
Alertmanager routing and receivers are configured in monitoring/alertmanager.yml. The examples below show how to add email and Slack receivers. Replace placeholders with your real values and do not commit secrets — use environment variable substitution or a secrets manager.
Add an email receiver and route alerts to it:
global:
smtp_smarthost: "smtp.example.com:587"
smtp_from: "alerts@example.com"
smtp_auth_username: "alerts@example.com"
smtp_auth_password: "<smtp-password>" # use a secret, not a literal
smtp_require_tls: true
route:
receiver: "email"
group_by: ["alertname"]
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receivers:
- name: "email"
email_configs:
- to: "oncall@example.com"
send_resolved: trueAdd a Slack receiver using an incoming webhook URL:
receivers:
- name: "slack"
slack_configs:
- api_url: "https://hooks.slack.com/services/<T...>/<B...>/<secret>"
channel: "#alerts"
send_resolved: true
title: "{{ .CommonLabels.alertname }}"
text: "{{ range .Alerts }}{{ .Annotations.summary }}\n{{ end }}"To route different severities to different channels, use multiple routes:
route:
receiver: "slack"
group_by: ["alertname"]
routes:
- matchers:
- severity = "critical"
receiver: "slack"
continue: false
- matchers:
- severity = "warning"
receiver: "email"After editing, reload Alertmanager:
curl -X POST http://localhost:9093/-/reloador restart the container:
docker compose restart alertmanager- Metrics scraping — In Prometheus, go to Status → Targets and confirm the
backendtarget isUP. - Rules — In Prometheus, go to Status → Rules and confirm the alert rules are loaded.
- Alerts — In Alertmanager (http://localhost:9093), confirm it is reachable and shows any firing alerts.
- Notifications — Trigger a test alert (or use Alertmanager's built-in test) and confirm it arrives in the configured email/Slack channel.
- Dashboard — Open the imported Grafana dashboard and confirm panels show live data.
- Target is
DOWN: verify the metrics endpoint is reachable from the Prometheus container (docker compose exec prometheus wget -qO- http://backend:3000/metrics). Check host/port and network configuration. - No data in Grafana: confirm the data source URL is correct and that Prometheus has scraped data (Prometheus → Graph, query
up). - Rules not loading: check the
rule_filespath and that the file is mounted into the container. Inspect Prometheus logs:docker compose logs prometheus. - Alerts not delivered: check Alertmanager logs (
docker compose logs alertmanager) and confirm the receiver config, webhook URL, and SMTP credentials are correct. - Config errors: validate configs before reloading:
- Prometheus:
promtool check config monitoring/prometheus.yml - Alertmanager:
amtool check-config monitoring/alertmanager.yml
- Prometheus: