Licensed to be used in conjunction with basebox, only.
// installation
Networking
Applies to
Product: Server · Audience: Platform Operator
DNS, TLS modes (cert-manager, existing secret, local), required outbound access and the network boundary between AISRV and inference. The network decisions flow directly into the Helm values – make them before the installation.
DNS
basebox runs under one hostname (global.domain) through which frontend, GraphQL (/graphql, /subscriptions), REST/OpenAI API (/rest, /v1), media (/media) and Keycloak (/auth) are served. The hostname must resolve to the cluster's ingress IP – publicly, in internal DNS or, for evaluation, via /etc/hosts (basebox.local).
The hostname is also the basis for OIDC: the frontend expects VITE_BB_OIDC_DOMAIN as https://<domain>/auth/realms/ – with a trailing slash. The chart derives this from global.domain; if you change the hostname later, frontend and AISRV configuration must follow (failure patterns "Organisation not found", redirect loop – see FAQ).
TLS mode
global.tls.mode controls the chart's behaviour:
| Mode | Who provides the certificate | AISRV JWKS mode | When |
|---|---|---|---|
cert-manager |
cert-manager creates and renews the secret global.tls.secretName via a ClusterIssuer |
JWKS URL | Public domain with ACME (Let's Encrypt) |
existing-secret |
You create a secret of type kubernetes.io/tls (tls.crt, tls.key) in the namespace; name = global.tls.secretName |
JWKS URL | Internal CA, own certificate, private installation without internet |
local |
The chart creates a self-signed secret (basebox-local-tls) if none exists |
JWKS file | Evaluation (basebox.local); browser warns until a locally trusted certificate (e.g. mkcert) is provided |
The hostname is not the distinguishing feature; basebox.internal can use local or existing-secret. What matters is the trust model. Let's Encrypt is not needed for private installations. Commands per mode: Using Helm charts.
Create an existing secret:
kubectl create namespace basebox --dry-run=client -o yaml | kubectl apply -f -
kubectl -n basebox create secret tls customer-tls \
--cert=<PATH_TO_TLS_CERTIFICATE> \
--key=<PATH_TO_TLS_PRIVATE_KEY>
Ingress
The chart ships nginx annotations you should know because they determine behaviour: proxy-body-size: 20m (uploads), large header buffers (JWT), proxy-read/send-timeout: 86400 (WebSocket, streaming), proxy-buffering: off (streaming), CORS (Authorization, Content-Type, X-Realm), cache control no-store. Details: Helm chart overview → Ingress configuration. A reverse proxy in front of the ingress must have the same properties – above all no buffering and long timeouts, otherwise streaming breaks.
Firewall
Inbound to the server:
| Port | Purpose |
|---|---|
| 22/TCP | SSH (restrict to the admin network) |
| 443/TCP (80/TCP) | Ingress for users and API clients |
| 6443/TCP | Kubernetes API – only if needed externally |
| 10250/TCP | Kubelet – only within the cluster |
Outbound (installation and updates): HTTPS 443 to the package sources, NVIDIA, Kubernetes, Helm, registries and gitea.basebox.health – the full list with test commands under Required network access. Outbound in operation: only what integrations need – SMTP, LDAP/OIDC, connector target systems, web search provider (api.staan.ai or DuckDuckGo hosts). Overview of all paths: VPN / networking.
UFW example from the Server Preparation Guide:
sudo ufw allow 22/tcp && sudo ufw allow 443/tcp && sudo ufw allow 80/tcp
sudo ufw allow 6443/tcp && sudo ufw allow 10250/tcp
sudo ufw enable
The boundary AISRV ↔ inference
Users never talk to inference directly; only AISRV does. If inference runs on the same cluster, traffic stays internal (http://inference:8000). If it runs on a separate GPU host:
- Only AISRV may reach the inference endpoint (firewall rule from AISRV nodes to the GPU host); end users may not.
- The endpoint requires an API key, stored as a Kubernetes secret, never in values.
- TLS when hosts or security zones are crossed. If the endpoint uses a certificate from a private CA, import the CA into the basebox workload – ingress certificate and inference certificate are separate trust decisions.
- Prompts, retrieved context and document content travel over this path; both hosts belong in the same data processing boundary.
Worked example with values and verification steps: Deployment topologies.
Cluster DNS
On k3s with systemd-resolved, name resolution in inference and ragsrv-support fails ("Temporary error in name resolution"). Solution: point coredns at a reachable DNS server – FAQ.
Verify
nslookup <domain> # points to the ingress IP
kubectl -n basebox get ingress # after installation: host = global.domain
kubectl -n basebox get secret <tls-secret> -o jsonpath='{.type}{"\n"}' # kubernetes.io/tls
curl -fsSL https://gitea.basebox.health -o /dev/null && echo registry ok
Checklist
- Hostname decided and set in DNS to the ingress IP
- TLS mode chosen; certificate/secret or ClusterIssuer ready
- Inbound ports 443 (and 22 for admins) open; 6443/10250 internal only
- Outbound domains for installation allowed – or offline transfer
- With external inference: firewall AISRV → GPU host, API key as secret, TLS/CA settled
- Outbound paths for integrations (SMTP, LDAP/OIDC, connectors, web search) planned
Next step: Install basebox