Skip to content

// 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