The short answers

Namespace, config, secrets

The API

Why these probes. /v1/health never touches the database, so a database blip restarts nothing. /v1/ready runs SELECT 1, so a pod that cannot reach Postgres leaves the load-balancer rotation without being killed. Using /v1/ready for liveness would turn a database incident into a cluster-wide crash loop.readOnlyRootFilesystem: true works as shown — the API writes nothing to disk.

Migrations as a Job

Run before the new API rolls out. Migrations 04–10 are idempotent (CREATE ... IF NOT EXISTS, guarded ALTERs) and safe to re-run; schema.sql is not — it is a fresh-install file, so drop it from the list on every run after the first.
The migrations image is postgres:17-alpine with db/*.sql copied to /sql. Extension creation needs a role with CREATE EXTENSION — the API’s own appuser should not have it. See Database.

Postgres

Embedding model in-cluster

nomic-embed-text is 768-dimensional and runs on CPU.
replicas: 1 is a starting point, not a recommendation. This tier, not the API, is what runs out first — every search blocks on one embedding call, so the HPA below is pointless until the embedding tier can keep up. Measure your throughput and size replicas from the measurement: Capacity has the benchmark to run in-cluster and the arithmetic. Warm-up matters too — a cold model blows EMBEDDING_TIMEOUT_QUERY and serves 503s that look like an outage.

Ingress

Forward Host and X-Forwarded-Proto/install.sh builds the installer URL from them.

Autoscaling

Scaling the API multiplies two things. Database connections: replicas × 10. And the in-process rate-limit buckets: at 8 replicas, PUBLIC_RATE_PER_MINUTE: 60 is an effective 480/min across the fleet. Set your Postgres max_connections and your real quota (at the ingress) with both in mind.

NetworkPolicy

If the point of self-hosting is that note text never leaves, enforce it rather than assuming it:
With EMAIL_ENABLED=0 and an in-cluster model, the API has no other outbound destination. See Security.