# Observability Runbooks Operational playbooks for the **standalone example observability stack** (`docker-compose.observability.yml`) that can be deployed alongside Manage. Manage itself does **not** deploy these services; it connects to existing Grafana / Prometheus / Alertmanager instances. These runbooks cover operating the standalone stack shipped under `monitoring/`. ## 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 1. Open **Manage → Backups** and identify the failed job/run. 2. Check the run output / logs for the failure reason. 3. Search Loki for `{container="backend"} | json | message=~"(?i)backup"` around the failure time. 4. If transient (network, lock file), retry the job. 5. 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 1. Confirm the job is still scheduled and expected to run. 2. Check whether the backup scheduler/host is running. 3. Verify the job can still report success to `POST /api/backups/reports`. 4. Inspect Prometheus graph for `manage_backup_runs_last_success_timestamp` by `job_name`. 5. 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 1. Identify `job` and `instance` from the alert labels. 2. Check the container/process status: - `docker compose ps ` - `docker compose logs --tail 100 ` 3. Verify network reachability from the Prometheus container: - `docker compose exec prometheus wget -qO- http:///` 4. 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. 5. Restart if needed: `docker compose restart `. --- ## Alert: `AlertmanagerDown` **Severity**: critical **Meaning**: Prometheus cannot scrape Alertmanager; new alerts may not be delivered. ### Steps 1. Check container status: `docker compose ps alertmanager` 2. Review logs: `docker compose logs --tail 200 alertmanager` 3. Validate config syntax: - `docker compose exec alertmanager amtool check-config /etc/alertmanager/alertmanager.yml` 4. Verify SMTP environment variables are present if using email receivers. 5. Restart: `docker compose restart alertmanager` --- ## Alert: `GrafanaDown` **Severity**: warning **Meaning**: Grafana is unreachable; dashboards and iframe panels in Manage are unavailable. ### Steps 1. Check container status and logs. 2. Verify the OAuth client configuration is correct (`GF_AUTH_GENERIC_OAUTH_*`). 3. If embedded panels are blank, confirm Grafana `allow_embedding = true` and cookie settings. 4. Restart: `docker compose restart grafana` --- ## Routine Maintenance ### Check overall health ```bash 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: ```bash curl -X POST http://localhost:9090/-/reload ``` ### Inspect logs ```bash # 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 ```bash 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: - `prometheus` - `loki` - `grafana` - `alertmanager` - `alloy` ### Backup data ```bash # 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 ```bash 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: ```bash cd /path/to/manage # create an env file with at least the required variables cat > .env.observability <