Files
jarvis-selfhost/kubernetes/06b-ingress-uploads.yaml
2026-10-21 12:12:12 +02:00

62 lines
3.4 KiB
YAML

# File uploads: a second Ingress on the same host, for one path.
#
# A SEPARATE OBJECT, NOT A FOURTH PATH IN 06-ingress.yaml. ingress-nginx applies an Ingress's
# annotations to every path that object declares, so the only way to give /api/uploads/ a larger
# body ceiling without handing the same ceiling to every API route is an object of its own. The
# controller merges both Ingresses into one server for the host and picks the longest matching
# prefix per request, so /api/uploads/ lands here and everything else under /api/ keeps the 4m of
# the main Ingress.
#
# PATCH ITS HOST LIKE THE MAIN ONE. An overlay that rewrites `jarvis.example.com` in 06-ingress.yaml
# must rewrite it here too, TLS block included. Left unpatched, this rule serves a host nobody
# visits and uploads quietly fall back to the /api/ rule — where every upload between 4 and 13 MB
# meets the controller's HTML 413 instead of Jarvis's own answer, and small files keep working, so
# nothing looks broken until somebody attaches a large one.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: jarvis-uploads
namespace: jarvis
annotations:
# With the main Ingress, for the same reason: last, once the Services have endpoints.
argocd.argoproj.io/sync-wave: "2"
# 13376k is 13 MiB and 64 KiB: exactly the most the API itself reads on this path — a workspace
# image of up to 10 MiB, the copy of it prepared for the model (at most 3 MiB), and 64 KiB for
# the multipart framing and the name (`UploadGate.BodyMaxBytes(UploadRoute.Documents)` +
# `UploadGate.MultipartOverhead`, in src/Jarvis.Api/Files/UploadEndpoint.cs; both refuse
# strictly above). Not less: a body the API would take would meet this ceiling first, and the
# controller's answer is an HTML page the client cannot translate. Not more: the API refuses
# anything larger unread. A composer attachment over 10 MiB still gets through here, and meets
# the API's own JSON 413.
nginx.ingress.kubernetes.io/proxy-body-size: "13376k"
# Everything the API does with a file happens before it answers: a wait for an upload slot, a
# PDF's text extraction (capped at 20s), a 13 MB row write. Two minutes is headroom over that,
# and far below the hour the main Ingress keeps open for idle websockets, which have no business
# on this path.
nginx.ingress.kubernetes.io/proxy-read-timeout: "120"
nginx.ingress.kubernetes.io/proxy-send-timeout: "120"
# Request buffering is left at the controller's default, ON: the controller reads the whole body
# before it opens a connection to the api, so a slow client costs the controller's disk rather
# than one of the api pod's few upload slots and the memory behind it.
#
# And no `custom-http-errors` override. A controller that sets it replaces the body of the
# statuses it lists, and every refusal the api makes on this path is a 4xx — busy included,
# which is a 429, never a 503. The one 4xx such lists usually name, 404, is a status whose body
# the client never reads.
spec:
ingressClassName: nginx
tls:
- hosts: ["jarvis.example.com"]
secretName: jarvis-tls
rules:
- host: jarvis.example.com
http:
paths:
- path: /api/uploads/
pathType: Prefix
backend:
service:
name: jarvis-api
port:
name: http