docs(obs): document obs vs main stack env model, obs.env + obs-secrets.env approach
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -306,32 +306,54 @@ docker compose up -d # creates archiv-net
|
||||
docker compose -f docker-compose.observability.yml up -d
|
||||
```
|
||||
|
||||
#### Why the obs stack is managed differently from the main app stack
|
||||
|
||||
The main app stack (`docker-compose.prod.yml`) has no config-file bind mounts — its containers read config from env vars and image defaults. The workspace is wiped after each CI run but that does not affect running containers, because they hold no references to workspace paths.
|
||||
|
||||
The obs stack is different: `prometheus.yml`, `tempo.yml`, Loki config, Grafana provisioning files, and Promtail config are all bind-mounted from the host filesystem into their containers. If those source paths disappear (workspace wipe), the containers can restart fine until a `docker compose up` is run again — at that point Docker tries to re-resolve the bind-mount source and fails because the workspace path no longer exists.
|
||||
|
||||
The fix is to keep the obs compose file and config tree at a **permanent path** that CI copies to on every run but which survives between runs: `/opt/familienarchiv/` (see ADR-016).
|
||||
|
||||
#### Production — managed from `/opt/familienarchiv/`
|
||||
|
||||
The nightly CI job copies `docker-compose.observability.yml` and `infra/observability/` to `/opt/familienarchiv/` on every run, then starts the stack from there. Bind mounts in the compose file resolve to `/opt/familienarchiv/infra/observability/…` on the host, which survives workspace wipes between CI runs (see ADR-016).
|
||||
Every CI run (nightly + release) copies `docker-compose.observability.yml` and `infra/observability/` to `/opt/familienarchiv/` before starting the stack. Bind mounts then resolve to `/opt/familienarchiv/infra/observability/…` — a stable path that outlasts any workspace wipe.
|
||||
|
||||
The obs stack reads secrets from `/opt/familienarchiv/.env` (Docker Compose auto-reads this file when launched from that directory). This file is managed by the operator — CI does **not** write or delete it.
|
||||
**Environment variables** follow the same two-source model as the main stack:
|
||||
|
||||
**Required keys in `/opt/familienarchiv/.env`:**
|
||||
| Source | What it contains | Managed by |
|
||||
|---|---|---|
|
||||
| `infra/observability/obs.env` | All non-secret config (ports, URLs, hostnames) | Git — reviewed in PRs |
|
||||
| `/opt/familienarchiv/obs-secrets.env` | Passwords and secret keys only | CI — written fresh from Gitea secrets on every deploy |
|
||||
|
||||
| Key | Example / notes |
|
||||
Both files are passed explicitly via `--env-file` to the compose command, so there is no implicit auto-read `.env` and no operator-managed file to keep in sync.
|
||||
|
||||
**Non-secret config** (`infra/observability/obs.env`):
|
||||
|
||||
| Key | Value | Notes |
|
||||
|---|---|---|
|
||||
| `PORT_GRAFANA` | `3003` | Avoids collision with staging frontend on port 3001 |
|
||||
| `PORT_GLITCHTIP` | `3002` | |
|
||||
| `PORT_PROMETHEUS` | `9090` | |
|
||||
| `GF_SERVER_ROOT_URL` | `https://grafana.archiv.raddatz.cloud` | Required for alert email links and OAuth redirects |
|
||||
| `GLITCHTIP_DOMAIN` | `https://glitchtip.archiv.raddatz.cloud` | Must match the Caddy vhost |
|
||||
| `POSTGRES_HOST` | `archive-db` | Override if only the staging stack is running |
|
||||
|
||||
**Secret keys** (set in Gitea secrets, injected by CI into `obs-secrets.env`):
|
||||
|
||||
| Gitea secret | Notes |
|
||||
|---|---|
|
||||
| `GRAFANA_ADMIN_PASSWORD` | Strong unique password |
|
||||
| `GLITCHTIP_SECRET_KEY` | `python3 -c "import secrets; print(secrets.token_hex(32))"` |
|
||||
| `GLITCHTIP_DOMAIN` | `https://glitchtip.archiv.raddatz.cloud` — must match the Caddy vhost |
|
||||
| `POSTGRES_USER` | Must match the `archiv` user set in `.env.staging` / `.env.production` |
|
||||
| `POSTGRES_PASSWORD` | Must match the running PostgreSQL container's password |
|
||||
| `PORT_GRAFANA` | `3003` (staging default; 3001 was used by staging frontend) |
|
||||
| `PORT_GLITCHTIP` | `3002` |
|
||||
| `PORT_PROMETHEUS` | `9090` |
|
||||
| `SENTRY_DSN` | Set after GlitchTip first-run; leave empty to disable |
|
||||
| `GRAFANA_ADMIN_PASSWORD` | Strong unique password; shared by nightly and release |
|
||||
| `GLITCHTIP_SECRET_KEY` | `openssl rand -hex 32`; shared by nightly and release |
|
||||
| `STAGING_POSTGRES_PASSWORD` / `PROD_POSTGRES_PASSWORD` | Must match the running PostgreSQL container |
|
||||
|
||||
**`$$` escaping rule:** passwords that contain a literal `$` must use `$$` in this file so Docker Compose does not expand them as variable references. Example: a password `p@$word` must be written as `p@$$word`. Failure to escape produces a silently truncated password — Grafana or GlitchTip will start but reject logins.
|
||||
|
||||
To start or restart the obs stack manually on the server:
|
||||
To start or restart the obs stack manually on the server (after CI has run at least once):
|
||||
|
||||
```bash
|
||||
docker compose -f /opt/familienarchiv/docker-compose.observability.yml up -d --wait --remove-orphans
|
||||
docker compose \
|
||||
-f /opt/familienarchiv/docker-compose.observability.yml \
|
||||
--env-file /opt/familienarchiv/infra/observability/obs.env \
|
||||
--env-file /opt/familienarchiv/obs-secrets.env \
|
||||
up -d --wait --remove-orphans
|
||||
```
|
||||
|
||||
Current services:
|
||||
|
||||
Reference in New Issue
Block a user