Sync the self-hosting stack

This commit is contained in:
2026-09-26 15:49:04 +02:00
parent 1aa6e9d05f
commit 248d6c2d52
8 changed files with 274 additions and 18 deletions
+25 -3
View File
@@ -76,6 +76,7 @@ kubectl wait --for=condition=complete job/jarvis-migrate -n jarvis --timeout=15m
kubectl apply -f 04-api.yaml
kubectl apply -f 05-web.yaml
kubectl apply -f 06-ingress.yaml
kubectl apply -f 06b-ingress-uploads.yaml # uploads alone get a ~13 MiB body ceiling
kubectl apply -f 07-disruption.yaml
```
@@ -175,12 +176,30 @@ images:
- name: git.luxit.be/luxit/jarvis-api
digest: sha256:...
patches:
- path: replicas.yaml # your node count, your ingress host, your storage class
- path: replicas.yaml # your node count, the host of BOTH Ingresses, your storage class
```
Then the digests you run are in a commit you control, upgrading is a diff you can review and revert,
and `?ref=` pins the base to a tag rather than a branch that moves under you. Pin it.
**There are two Ingresses on the one host, and the host patch must reach both**: `jarvis`, and
`jarvis-uploads` in `06b-ingress-uploads.yaml`, TLS block included. Uploads have an Ingress of their
own because annotations apply to every path of the object that carries them, and they are the one
path that needs bodies over 13 MiB. An overlay that rewrites only the first leaves uploads on the
`/api/` rule, where small files still work and anything between 4 and 13 MB meets the controller's
own 413 — so it looks fine until somebody attaches a large PDF.
To check it once deployed, send an upload of more than 4 MB without signing in, on your own host:
```sh
head -c 5000000 /dev/zero > big.bin
curl -s -o /dev/null -w '%{http_code} %{content_type}\n' -F file=@big.bin \
https://jarvis.example.com/api/uploads/orgs/x/conversations/y/attachments
```
`401 application/json` is Jarvis turning away an anonymous request, so the body got through. A
`413` with an HTML page is the controller, and means the host patch did not reach `jarvis-uploads`.
## The three things most likely to bite
**Never mint secrets per pod.** On Compose a one-shot generates the vault key and the JWT secrets
@@ -263,8 +282,11 @@ outcome. Run it again.
`emptyDir` filled by an initContainer is right; a shared RWO volume is not — either every pod is
pinned to one node, or the second sits in `Multi-Attach error`. Leaving it unset is supported:
everything works except the agent installer, which answers 503 saying no build is published.
- **No application volume, and it should stay that way.** Avatars, documents and terminal recordings
are all in Postgres; exports are rendered in memory.
- **No application volume, and it should stay that way.** Avatars, documents, terminal recordings
and attached files are all in Postgres; exports are rendered in memory.
- **The Ingresses are written for ingress-nginx.** Under another controller their `nginx.ingress…`
annotations do nothing, so set that controller's own equivalents: an hour's read timeout on the
two websocket paths, a 4 MB body ceiling on `/api/`, and 13,376 KiB on `/api/uploads/`.
- **Autoscale `jarvis-web`, not `jarvis-api`.** Every api scale-in kills a pod holding agent
websockets, live runs and open shells, and CPU is a poor proxy for a load made of long-lived
connections. Raise api replicas deliberately.