44b2eb2cc3
The README was 1,240 lines of reference material and still described a SQLite quick start. It is now a short tour (highlights, screenshots of the web UI, an accurate quick start against Postgres), and each topic has its own page under docs/ with an index: deployment, configuration, Alertmanager, incidents, notifications, escalation, dead man's switches, single sign-on, web UI, API and development. SERVICE-ACCOUNTS.md is rewritten from a proposal into a reference, and TEAM-LOOKUP.md is gone with the endpoint it described. The "Upgrading to ..." sections for an unreleased product are dropped. Claude-Session: https://claude.ai/code/session_016mBLURvJoMuUEr9cB2RpUN
75 lines
3.8 KiB
Markdown
75 lines
3.8 KiB
Markdown
# Deployment
|
|
|
|
_Running the server in a container and on Kubernetes with the Helm chart._ Back to the [README](../README.md) and the [documentation index](./README.md).
|
|
|
|
## Docker
|
|
|
|
```bash
|
|
docker build -t terdut-server .
|
|
docker run -p 8080:8080 \
|
|
-e TERDUT_DB_DSN='postgres://terdut:secret@host.docker.internal:5432/terdut?sslmode=disable' \
|
|
terdut-server
|
|
```
|
|
|
|
The server creates its own schema on startup and needs a reachable Postgres; it stores nothing on
|
|
disk, so there is no volume to mount.
|
|
|
|
## Kubernetes
|
|
|
|
A Helm chart is published from this repository as an OCI artifact, versioned in lockstep
|
|
with the app — chart `x.y.z` is always app `vx.y.z`:
|
|
|
|
```bash
|
|
helm upgrade --install terdut-server oci://git.ryuvia.com/niklas/terdut-server \
|
|
--version 0.9.2 \
|
|
--namespace terdut-server --create-namespace \
|
|
--set networking.hostname=terdut.example.com
|
|
```
|
|
|
|
The chart expects a [Gateway API](https://gateway-api.sigs.k8s.io/) Gateway named `envoy-main` in
|
|
the `envoy-gateway-system` namespace to already exist — it renders an `HTTPRoute` against it rather
|
|
than an `Ingress`. TLS is terminated at the gateway, so the server itself never sees a certificate.
|
|
|
|
| Value | Default | Description |
|
|
|---|---|---|
|
|
| `networking.hostname` | `terdut.example.com` | Hostname the `HTTPRoute` serves |
|
|
| `networking.listener` | `""` | Gateway listener (`sectionName`) to bind to. Empty attaches to every matching listener, **including plaintext HTTP** — set it to the HTTPS listener's name to serve TLS only |
|
|
| `networking.servicePort` | `8080` | Port the route forwards to; keep in sync with `service.port` |
|
|
| `bootstrap.enabled` | `true` | Runs a post-install hook that creates the first user and stores its API key in the `<release>-admin-key` Secret. Already-bootstrapped servers are left alone |
|
|
| `database.dsn` | `""` | **Required.** Postgres DSN, with no password in it. The chart provisions no database |
|
|
| `database.passwordSecret.name` | `""` | Secret supplying `PGPASSWORD`. With the Zalando postgres operator, the Secret it generates for the role |
|
|
| `database.passwordSecret.key` | `password` | Key within that Secret |
|
|
|
|
The API key travels in an `Authorization: Bearer` header, so set `networking.listener` whenever the
|
|
hostname is reachable outside a trusted network.
|
|
|
|
### The database
|
|
|
|
The chart provisions no database: it takes a DSN and expects a Postgres that already exists. In this
|
|
cluster the wrapper chart declares an `acid.zalan.do/v1 postgresql` CR; anywhere else, any reachable
|
|
Postgres 14+ will do.
|
|
|
|
The DSN carries no password. pgx falls back to libpq's environment variables for whatever the DSN
|
|
leaves out, so the password arrives as `PGPASSWORD` from a Secret and never appears in values, in
|
|
the rendered manifest or in `kubectl describe pod`. With the postgres operator that Secret is the
|
|
one it generates for the role, so a rebuild mints a new password with nothing to keep in sync —
|
|
the same wiring miniflux uses.
|
|
|
|
The server migrates its own schema on startup, so a new database only has to exist and be writable.
|
|
|
|
### Backups
|
|
|
|
Postgres is backed up where it runs, not from here. The database pod carries a
|
|
[k8up](https://k8up.io/) `k8up.io/backupcommand` annotation that streams a `pg_dump`, the same way
|
|
gitea and immich do in this cluster.
|
|
|
|
## On Kubernetes with the operator
|
|
|
|
[terdut-operator](https://git.ryuvia.com/niklas/terdut-operator) runs a server for you from a
|
|
`TerdutServer` object and manages its teams, escalation ladders, dead man's switches and alert
|
|
sources as Kubernetes objects. It hands the server a generated key through `TERDUT_OPERATOR_KEY`
|
|
(see [Configuration](./configuration.md) and [`SERVICE-ACCOUNTS.md`](../SERVICE-ACCOUNTS.md)), and
|
|
the server then treats configuration as operator-managed (`TERDUT_OPERATOR_MODE`), refusing edits
|
|
made by hand in the web UI. Use the Helm chart above for a plain install, the operator when you
|
|
want that configuration in gitops.
|