- Switch docker-compose.observability.yml from named volumes to host bind mounts under OBSERVABILITY_DATA_ROOT, defaulting to ./observability-data. - Make all service ports configurable via environment variables (PROMETHEUS_PORT, LOKI_PORT, ALLOY_PORT, GRAFANA_PORT, ALERTMANAGER_PORT, NODE_EXPORTER_PORT). - Add VITE_GRAFANA_URL handling to ObservabilityPage so Grafana links point to the configured standalone instance. - Update docs/observability-runbooks.md with the env variable table, reachable-web-UI table, and backup/restore instructions for the new host-directory layout.
8.7 KiB
Observability Runbooks
Operational playbooks for the Manage self-hosted observability stack (Prometheus, Grafana, Loki, Alertmanager).
Service Overview
| Service | Compose name | Internal URL | Health check |
|---|---|---|---|
| Prometheus | prometheus |
http://prometheus:9090 |
/-/healthy |
| Grafana | grafana |
http://grafana:3000 |
/api/health |
| Loki | loki |
http://loki:3100 |
/ready |
| Alloy | alloy |
http://alloy:12345 |
/-/healthy |
| Alertmanager | alertmanager |
http://alertmanager:9093 |
/-/healthy |
| Node Exporter | node-exporter |
http://node-exporter:9100 |
/ |
| Manage backend | backend |
http://backend:8000 |
/api/health |
Alert: BackupJobFailed
Severity: critical
Meaning: A backup job reported status=failed within the last hour.
Steps
- Open Manage → Backups and identify the failed job/run.
- Check the run output / logs for the failure reason.
- Search Loki for
{container="backend"} | json | message=~"(?i)backup"around the failure time. - If transient (network, lock file), retry the job.
- If persistent, open a task to fix the backup script or credentials.
Alert: BackupJobStuck
Severity: warning
Meaning: No successful backup run has been recorded for a job in the last 24 hours.
Steps
- Confirm the job is still scheduled and expected to run.
- Check whether the backup scheduler/host is running.
- Verify the job can still report success to
POST /api/backups/reports. - Inspect Prometheus graph for
manage_backup_runs_last_success_timestampbyjob_name. - If the job was intentionally retired, remove or disable its reporting.
Alert: PrometheusTargetMissing
Severity: warning
Meaning: A Prometheus scrape target is down (up == 0) for more than 2 minutes.
Steps
- Identify
jobandinstancefrom the alert labels. - Check the container/process status:
docker compose ps <service>docker compose logs --tail 100 <service>
- Verify network reachability from the Prometheus container:
docker compose exec prometheus wget -qO- http://<instance>/
- If the target is a remote Node Exporter:
- Check the machine is reachable over SSH.
- Verify Node Exporter is installed and running (
systemctl status node_exporter). - Confirm the scrape host/port in Manage → Settings for that machine.
- Restart if needed:
docker compose restart <service>.
Alert: AlertmanagerDown
Severity: critical
Meaning: Prometheus cannot scrape Alertmanager; new alerts may not be delivered.
Steps
- Check container status:
docker compose ps alertmanager - Review logs:
docker compose logs --tail 200 alertmanager - Validate config syntax:
docker compose exec alertmanager amtool check-config /etc/alertmanager/alertmanager.yml
- Verify SMTP environment variables are present if using email receivers.
- Restart:
docker compose restart alertmanager
Alert: GrafanaDown
Severity: warning
Meaning: Grafana is unreachable; dashboards and iframe panels in Manage are unavailable.
Steps
- Check container status and logs.
- Verify the OAuth client configuration is correct (
GF_AUTH_GENERIC_OAUTH_*). - If embedded panels are blank, confirm Grafana
allow_embedding = trueand cookie settings. - Restart:
docker compose restart grafana
Routine Maintenance
Check overall health
cd /path/to/manage
docker compose ps
docker compose exec prometheus wget -qO- http://127.0.0.1:9090/-/healthy
docker compose exec grafana wget -qO- http://127.0.0.1:3000/api/health
docker compose exec loki wget -qO- http://127.0.0.1:3100/ready
docker compose exec alertmanager wget -qO- http://127.0.0.1:9093/-/healthy
Reload Prometheus after rule/config changes
Prometheus is started with --web.enable-lifecycle, so a SIGHUP or HTTP call reloads config:
curl -X POST http://localhost:9090/-/reload
Inspect logs
# All backend logs in Loki via Grafana Explore, or locally:
docker compose logs --tail 500 backend
# Specific service:
docker compose logs -f prometheus
Storage usage
docker system df -v
docker compose exec prometheus du -sh /prometheus
docker compose exec loki du -sh /loki
docker compose exec grafana du -sh /var/lib/grafana
Backup and Disaster Recovery
The observability data lives on the host under OBSERVABILITY_DATA_ROOT (./observability-data by default). Subdirectories are created for each service:
prometheuslokigrafanaalertmanageralloy
Backup data
# Stop the stack to ensure consistency
docker compose -f docker-compose.observability.yml down
# Back up the whole data directory
rsync -aP --delete "$OBSERVABILITY_DATA_ROOT" /mnt/backups/observability-data/
# Start the stack again
docker compose -f docker-compose.observability.yml up -d
Restore data
docker compose -f docker-compose.observability.yml down
rm -rf "$OBSERVABILITY_DATA_ROOT"
rsync -aP /mnt/backups/observability-data/ "$OBSERVABILITY_DATA_ROOT"
docker compose -f docker-compose.observability.yml up -d
Standalone Observability Stack
Run the observability services without the Manage backend or frontend:
cd /path/to/manage
cp .env.example .env
# edit .env as needed
docker compose -f docker-compose.observability.yml up -d
Reachable web UIs
Only three services expose a human-facing web interface:
| Service | Has UI | Default URL | Notes |
|---|---|---|---|
| Grafana | yes | http://localhost:3000 |
Dashboards, log explore, alert management |
| Prometheus | yes | http://localhost:9090 |
Query, targets, alerts, config status |
| Alertmanager | yes | http://localhost:9093 |
Alerts, silences, routing status |
| Loki | no | http://localhost:3100 |
Log API only; browse logs through Grafana |
| Alloy | partial | http://localhost:12345 |
Agent debug UI for pipeline inspection |
| Node Exporter | no | http://localhost:9100 |
Metrics endpoint only (/metrics) |
Grafana defaults to admin / admin. Datasources and dashboards are provisioned automatically.
Environment variables
| Variable | Default | Purpose |
|---|---|---|
OBSERVABILITY_DATA_ROOT |
./observability-data |
Host directory where all service data is stored persistently. Each service gets a subdirectory inside it. |
PROMETHEUS_PORT |
9090 |
Host port for Prometheus web UI and API. |
LOKI_PORT |
3100 |
Host port for Loki API. |
ALLOY_PORT |
12345 |
Host port for Alloy debug UI. |
GRAFANA_PORT |
3000 |
Host port for Grafana web UI. |
ALERTMANAGER_PORT |
9093 |
Host port for Alertmanager web UI. |
NODE_EXPORTER_PORT |
9100 |
Host port for Node Exporter metrics endpoint. |
GRAFANA_ADMIN_USER |
admin |
Grafana admin username. |
GRAFANA_ADMIN_PASSWORD |
admin |
Grafana admin password. Change this in production. |
GF_AUTH_GENERIC_OAUTH_CLIENT_ID |
empty | Generic OAuth client ID for Authentik or another provider. |
GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET |
empty | Generic OAuth client secret. |
GF_AUTH_GENERIC_OAUTH_AUTH_URL |
empty | OAuth authorization endpoint. |
GF_AUTH_GENERIC_OAUTH_TOKEN_URL |
empty | OAuth token endpoint. |
GF_AUTH_GENERIC_OAUTH_API_URL |
empty | OAuth userinfo endpoint. |
LOG_LEVEL |
INFO |
Grafana log level. |
SMTP_HOST |
smtp.example.com |
SMTP host for Alertmanager email notifications. |
SMTP_PORT |
587 |
SMTP port for Alertmanager. |
SMTP_USERNAME |
empty | SMTP username. |
SMTP_PASSWORD |
empty | SMTP password. |
SMTP_FROM_ADDRESS |
no-reply@example.com |
From address for alert emails. |
ALERT_EMAIL_TO |
admin@example.com |
Default recipient for alert emails. |
To scrape a Manage backend from this standalone stack, edit monitoring/prometheus/prometheus.standalone.yml and add a static target for the backend's /metrics endpoint, or drop a file-SD JSON file into monitoring/prometheus/file-sd/.
Backing up standalone data
Because data is stored on the host under OBSERVABILITY_DATA_ROOT, you can back it up with normal filesystem tools:
rsync -aP --delete "$OBSERVABILITY_DATA_ROOT" /mnt/backups/observability-data/
Stop the stack first if you need a consistent snapshot.
Scaling Notes
- The current
deploy.resourcesblocks are tuned for a small homelab. Raise memory limits if you monitor many machines or retain logs longer than 30 days. - Loki is configured for single-node filesystem storage. For larger deployments, migrate to object storage (S3/GCS/MinIO) and a shared index.
- Prometheus remote-write or Thanos/Cortex can be added later for long-term metrics without changing application instrumentation.