Skip to content

BaseBox - Quick Start Guide

Get your BaseBox up and running in minutes using OCI Helm registry.

Prerequisites

  • Kubernetes cluster already prepared
  • kubectl and helm installed on your workstation
  • CloudNativePG operator installed on the cluster
  • NGINX ingress controller installed on the cluster
  • cert-manager installed and ClusterIssuer available (example: letsencrypt-prod)
  • Basebox install bundle files available from basebox.ai/helm tag v0.3.12 under examples/:
    • examples/values-hetzner-gpu.example.yaml
    • examples/db-clusters-hetzner.yaml
    • examples/generate-hetzner-config.sh
    • examples/sql/aisrv/schema.sql
    • examples/sql/aisrv/initdb.sql
    • If you run from this monorepo layout, the same files are under helm/examples/

Pull Example Files

To get the example files, do:

helm pull oci://gitea.basebox.health/basebox-distribution/helm/basebox.ai --version 0.3.12
tar -xzf basebox.ai-0.3.12.tgz basebox.ai/examples
ls -R basebox.ai/examples
  • Registry credentials for gitea.basebox.health

Registry Credentials

Our container registry is public, but if you pull containers from a Kubernetes cluster (containerd), you still need to provide registry credentials. This seems to be a bug either in containerd or our registry server.

If you get permission denied errors, please use this token (username pacman):

ee4d3d1bad18cae07a1817701d2281f6fbf8aa2f

Step 1: Set kubeconfig

export KUBECONFIG="<PATH_TO_KUBECONFIG>"
kubectl cluster-info

See the commands under Server Preparation, Step 7 for instructions on how to create the KUBECONFIG file.

Step 2: Prepare install files

EXAMPLES_DIR="<PATH_TO_EXAMPLES_DIR>"   # examples or helm/examples
bash "${EXAMPLES_DIR}/generate-hetzner-config.sh" .

mv values-hetzner-gpu.yaml values.customer.yaml
mv db-clusters-hetzner.yaml db-clusters.customer.yaml

The helper script already generates the required passwords and keeps dependent values aligned.

Edit values.customer.yaml and set:

  • aisrv.env.ADMIN_EMAIL to the email address that should become the primary tenant admin login

Keep the generated passwords and the packaged image tags unchanged unless you intentionally want to deploy different credentials or application images.

Step 3: Create namespace and image pull secret

kubectl create ns basebox --dry-run=client -o yaml | kubectl apply -f -

kubectl -n basebox create secret docker-registry regcred-bb \
  --docker-server=gitea.basebox.health \
  --docker-username='<REGISTRY_USERNAME_OR_EMAIL>' \
  --docker-password='<REGISTRY_PASSWORD_OR_TOKEN>' \
  --docker-email='<EMAIL>' \
  --dry-run=client -o yaml | kubectl apply -f -

Step 4: Create database clusters

kubectl apply -f db-clusters.customer.yaml

kubectl -n basebox wait --for=condition=Ready cluster.postgresql.cnpg.io/aisrv-db --timeout=20m
kubectl -n basebox wait --for=condition=Ready cluster.postgresql.cnpg.io/idp-db --timeout=20m
kubectl -n basebox wait --for=condition=Ready cluster.postgresql.cnpg.io/storesrv-db --timeout=20m

Step 5: Initialize AISRV schema (clean install)

AISRV_DB_POD="$(kubectl -n basebox get pod -l cnpg.io/cluster=aisrv-db -o jsonpath='{.items[0].metadata.name}')"
EXAMPLES_DIR="<PATH_TO_EXAMPLES_DIR>"   # examples or helm/examples

cat "${EXAMPLES_DIR}/sql/aisrv/schema.sql" | kubectl -n basebox exec -i "$AISRV_DB_POD" -- psql -v ON_ERROR_STOP=1 -U postgres -d aisrv
cat "${EXAMPLES_DIR}/sql/aisrv/initdb.sql" | kubectl -n basebox exec -i "$AISRV_DB_POD" -- psql -v ON_ERROR_STOP=1 -U postgres -d aisrv

Step 6: Create domain override file

Set your domain once and keep all components aligned.

export BASEBOX_DOMAIN="<YOUR_DOMAIN>"

Create domain-override.yaml:

cat > domain-override.yaml <<EOF
global:
  baseUrl: "https://${BASEBOX_DOMAIN}"
  domain: "${BASEBOX_DOMAIN}"
  ingress:
    tls:
      enabled: true
      secretName: "${BASEBOX_DOMAIN//./-}-tls"
    annotations:
      cert-manager.io/cluster-issuer: "letsencrypt-prod"
      nginx.ingress.kubernetes.io/cors-allow-origin: "https://${BASEBOX_DOMAIN}"
idp:
  domain: "${BASEBOX_DOMAIN}"
  env:
    KC_HOSTNAME: "https://${BASEBOX_DOMAIN}/auth"
aisrv:
  domain: "${BASEBOX_DOMAIN}"
  env:
    AISRV_BASE_DOMAIN: "${BASEBOX_DOMAIN}"
    AISRV_OIDC_ISSUER_URL: "https://${BASEBOX_DOMAIN}/auth"
storesrv:
  domain: "${BASEBOX_DOMAIN}"
frontend:
  domain: "${BASEBOX_DOMAIN}"
  env:
    VITE_BB_OIDC_DOMAIN: "https://${BASEBOX_DOMAIN}/auth/realms/"
    VITE_BB_OIDC_FORCE_REALM: "primary"
    VITE_BB_GRAPHQL_URL: "https://${BASEBOX_DOMAIN}/graphql"
EOF

Verify that no placeholder domains remain in the generated files:

rg -n 'your-domain\.example|example\.com|<YOUR_DOMAIN>' values.customer.yaml domain-override.yaml

Step 7: Install Helm chart from OCI

# Login is optional; in case you get a permission denied error, use this login.
# See top info box for the token
helm registry login gitea.basebox.health -u pacman

helm upgrade --install basebox oci://gitea.basebox.health/basebox-distribution/helm/basebox.ai \
  --version 0.3.12 \
  -n basebox \
  -f values.customer.yaml \
  -f domain-override.yaml \
  --wait \
  --timeout 120m

Step 8: Wait for automatic Keycloak/OIDC reconciliation

kubectl -n basebox wait --for=condition=complete job/idp-keycloak-bootstrap --timeout=10m

This hook does both automatically: - syncs master/admin password to Helm value idp.keycloak.keycloak_admin_password - creates/updates primary/aiclient redirect/web-origin for your domain

If hook fails:

kubectl -n basebox logs job/idp-keycloak-bootstrap

Step 9: Smoke checks

kubectl -n basebox get pods -o wide
kubectl -n basebox get ingress
helm status basebox -n basebox

Expected:

  • All pods Running/Ready
  • Ingress host is exactly <YOUR_DOMAIN>

Step 10: Access checks

curl -I "https://<YOUR_DOMAIN>/"
curl -k -I "https://<YOUR_DOMAIN>/auth/realms/primary/protocol/openid-connect/auth?client_id=aiclient&redirect_uri=https%3A%2F%2F<YOUR_DOMAIN>%2Foidc%2Fcallback&response_type=code"
curl -k -X POST "https://<YOUR_DOMAIN>/graphql" \
  -H "Content-Type: application/json" \
  -H "X-Realm: primary" \
  --data-binary '{"query":"query { __typename }"}'

Expected:

  • / returns 200
  • auth URL returns login page (not 404 and not Invalid parameter: redirect_uri)
  • /graphql returns GraphQL JSON (not HTML and not 404)
curl -k -X POST "https://<YOUR_DOMAIN>/auth/realms/primary/protocol/openid-connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "client_id=aiclient" \
  --data-urlencode "grant_type=password" \
  --data-urlencode "username=<PRIMARY_ADMIN_EMAIL>" \
  --data-urlencode "password=<PRIMARY_ADMIN_PASSWORD>"

curl -k -X POST "https://<YOUR_DOMAIN>/auth/realms/master/protocol/openid-connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "client_id=admin-cli" \
  --data-urlencode "grant_type=password" \
  --data-urlencode "username=admin" \
  --data-urlencode "password=<KEYCLOAK_ADMIN_PASSWORD>"

Use these values from values.customer.yaml:

  • <PRIMARY_ADMIN_EMAIL> = aisrv.env.ADMIN_EMAIL
  • <PRIMARY_ADMIN_PASSWORD> = aisrv.env.ADMIN_PASSWORD
  • <KEYCLOAK_ADMIN_PASSWORD> = idp.keycloak.keycloak_admin_password

Expected:

  • both token requests return 200

Troubleshooting

  • 404 on your domain while nip.io works:
    • domain mismatch in ingress values.
    • regenerate/fix domain-override.yaml and redeploy.
  • /graphql returns frontend HTML instead of GraphQL JSON:
    • ingress host/domain alignment is still wrong for aisrv.
    • regenerate/fix domain-override.yaml and redeploy.
  • Invalid parameter: redirect_uri:
    • check post-install hook status/logs:
    • kubectl -n basebox logs job/idp-keycloak-bootstrap
  • Keycloak admin password mismatch after install:
    • check post-install hook logs for:
    • Syncing master admin password...
    • then retry token check against master realm
  • Helm validation fails for missing secrets:
    • set required values in values.customer.yaml:
      • AISRV_OIDC_SUPER_ADMIN_CLIENT_SECRET
      • AISRV_OIDC_SUPER_ADMIN_PASSWORD

Additional Resources