For AI agents: the documentation index is at /docs/interbox/llms.txt. A Markdown version of this page is available at /docs/interbox/ops/local-testing.md or by requesting it with the Accept: text/markdown header.
Interbox Docs

Testing Locally with k3d

You can rehearse the full secure deployment — internal-style ingress, TLS, and an SSO login gate — on a laptop with k3d (k3s in Docker) before you touch a real cloud. It exercises the same chart and the same secure-access wiring; only the identity provider and the cert issuer differ from production.

The credentials and self-signed certs here are throwaway, local-only. Never reuse them anywhere real.

Prerequisites: k3d, kubectl, helm, docker, openssl.

1. Cluster + ingress controller

k3d ships Traefik; swap it for ingress-nginx (so the oauth2-proxy auth annotations work as they do on AKS), and map host ports to the ingress:

k3d cluster create interbox \
  --k3s-arg "--disable=traefik@server:0" \
  -p "8081:80@loadbalancer" -p "8443:443@loadbalancer" --wait

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx \
  -n ingress-nginx --create-namespace \
  --set controller.service.type=LoadBalancer

*.localhost resolves to loopback, so interbox.localhost:8443 reaches the ingress with no /etc/hosts edits.

2. Postgres

A throwaway in-cluster Postgres is enough (its postgres superuser has CREATEDB, so the engine self-creates the interbox db):

kubectl create deploy interbox-pg --image=postgres:18-alpine
kubectl set env deploy/interbox-pg POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres POSTGRES_DB=postgres
kubectl expose deploy/interbox-pg --port=5432

3. cert-manager + a local CA

Install cert-manager, then create a self-signed CA issuer — the local stand-in for the internal CA you'd use for a private host in production:

helm repo add jetstack https://charts.jetstack.io
helm install cert-manager jetstack/cert-manager \
  -n cert-manager --create-namespace --set crds.enabled=true
# ca-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata: { name: selfsigned }
spec: { selfSigned: {} }
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata: { name: local-ca, namespace: cert-manager }
spec:
  isCA: true
  commonName: interbox-local-ca
  secretName: local-ca
  issuerRef: { name: selfsigned, kind: ClusterIssuer, group: cert-manager.io }
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata: { name: local-ca }
spec: { ca: { secretName: local-ca } }

kubectl apply -f ca-issuer.yaml. Now an ingress annotated cert-manager.io/cluster-issuer: local-ca gets a cert automatically.

4. An identity provider (Dex)

In production this is Entra ID / Okta. Locally, run Dex with one static test user and an OIDC client for the dashboard. Generate the password hash, then serve Dex behind the ingress:

docker run --rm httpd:2-alpine htpasswd -bnBC 10 "" 'password' | tr -d ':\n' | sed 's/\$2y/\$2a/'

Dex config (issuer https://dex.localhost:8443/dex, client interbox-dashboard, redirect https://interbox.localhost:8443/oauth2/callback, static user user@interbox.local with the hash above), exposed via an nginx Ingress on dex.localhost with cert-manager.io/cluster-issuer: local-ca.

5. The login gate (oauth2-proxy)

Deploy oauth2-proxy pointed at Dex. The key flags (split browser-facing vs in-cluster endpoints so it works without cluster DNS for dex.localhost):

--provider=oidc  --skip-oidc-discovery=true
--oidc-issuer-url=https://dex.localhost:8443/dex
--login-url=https://dex.localhost:8443/dex/auth               # browser
--redeem-url=http://dex.dex.svc.cluster.local:5556/dex/token  # in-cluster
--oidc-jwks-url=http://dex.dex.svc.cluster.local:5556/dex/keys # in-cluster
--client-id=interbox-dashboard  --redirect-url=https://interbox.localhost:8443/oauth2/callback
--cookie-secret=<32 chars>  --cookie-secure=true  --email-domain=*

Expose it at interbox.localhost/oauth2 (same host as the dashboard, for cookies

  • callback).

6. Interbox

Install the chart with the secure values (see secure-access):

config:
  AIDBOX_URL: "http://aidbox.placeholder.svc:8080"     # no Aidbox locally
  INTERBOX_WORKSPACE_GIT_URL: "https://github.com/HealthSamurai/interbox-workspace"
secrets:
  data:
    DATABASE_URL: "postgres://postgres:postgres@interbox-pg:5432/interbox?sslmode=disable"
    AIDBOX_CLIENT_SECRET: "placeholder"
ingress:
  enabled: true
  className: nginx
  host: interbox.localhost
  tls:
    - { hosts: [interbox.localhost], secretName: interbox-tls }
  annotations:
    cert-manager.io/cluster-issuer: local-ca
    nginx.ingress.kubernetes.io/auth-url: "http://oauth2-proxy.default.svc.cluster.local:4180/oauth2/auth"
    nginx.ingress.kubernetes.io/auth-signin: "https://interbox.localhost:8443/oauth2/start?rd=$escaped_request_uri"
helm repo add healthsamurai https://healthsamurai.github.io/helm-charts
helm install interbox healthsamurai/interbox -f values.yaml

7. Verify

# unauthenticated -> redirected to the login gate (SSO enforced)
curl -sk -o /dev/null -D - https://interbox.localhost:8443/api/messages | grep -iE '^HTTP|^location'
#   HTTP/1.1 302 ... Location: .../oauth2/start?rd=/api/messages  -> then on to Dex

# TLS served by the cert-manager CA
echo | openssl s_client -connect interbox.localhost:8443 -servername interbox.localhost 2>/dev/null \
  | openssl x509 -noout -issuer
#   issuer=CN=interbox-local-ca

Then open https://interbox.localhost:8443/ (accept the self-signed warning), log in as user@interbox.local / password, and you land on the dashboard.

What changes on real cloud

Local (here)Production (e.g. AKS)
ingress-nginx (mapped host ports)ingress-nginx behind an internal LB, or AGIC
self-signed CA issuerDNS-01 (public zone) or your internal CA / Key Vault
Dex + static userEntra ID / Okta (real OIDC app)
in-cluster Postgres podmanaged Postgres (private endpoint, sslmode=require)

Everything else — the chart, the ingress annotations, cert-manager, oauth2-proxy — is identical. Tear down with k3d cluster delete interbox.

Last updated: