Skip to content

Getting started

  • A Nebari cluster with the nebari-operator installed (it provides the NebariApp CRD), Envoy Gateway, and a Keycloak realm.
  • cert-manager with a cluster issuer — only when TLS is enabled (the default).
  • Helm 3.8+.
Terminal window
helm install nebari-apps charts/nebari-apps \
--namespace nebari-apps --create-namespace \
--set clusterDomain=example.ai \
--set keycloak.url=https://keycloak.example.ai/auth
# The nebari-operator only processes namespaces that opt in:
kubectl label namespace nebari-apps nebari.dev/managed=true

This deploys four components:

ComponentWhere it ends up
apps-operatorWatches App resources cluster-wide.
apps-apiIn-cluster Service; the UI proxies /api to it same-origin.
apps-uihttps://apps.example.ai — with a landing-page tile (“Apps”).
apps-mcphttps://apps.example.ai/mcp — agent tools, proxied by the UI (see MCP server).

Every app launched afterwards gets https://<subdomain>.apps.example.ai.

ValueDefaultPurpose
clusterDomain— (required)The cluster’s base domain.
appsDomainapps.<clusterDomain>Domain apps are served under.
tls.enabledtrueSet false to serve plain HTTP (no certificates).
keycloak.urlBrowser-facing Keycloak base URL (required when auth is on).
keycloak.realmnebariKeycloak realm.
keycloak.internalUrlOptional in-cluster Keycloak URL (split horizon) for JWKS.
api.auth.enabledtrueKeycloak JWT auth for the API + keycloak-js login in the UI.
api.allowedNamespaces["apps"]Namespaces users may launch into via the API/UI/MCP. Empty list = every namespace labeled nebari.dev/managed=true.
ui.hostname<appsDomain>Where the UI itself is served.
ui.title, ui.branding.*Rebrand the UI (title, logos, favicon, theme, banners) — see Branding.
ui.landingPage.*”Apps” tileLanding-page tile label, description, category, icon, and health check — see Branding.
gatewaypublicShared Gateway apps attach to (public | internal).
staticImagenginxinc/nginx-unprivileged:1.27-alpineServes static app content.
gitImagealpine/git:v2.47.2Init-container image for git sources.
pythonImageghcr.io/prefix-dev/pixi:0.68.1-nobleRuns Python/pixi apps (runtime.pixiTask).

Namespaces that host apps must also opt in:

Terminal window
kubectl create namespace apps
kubectl label namespace apps nebari.dev/managed=true

apps is the default entry in api.allowedNamespaces — to launch into other namespaces from the UI/API/MCP, add them to that list (or set it empty to allow every managed namespace). kubectl apply is only gated by the namespace label.

Then either open the UI at https://apps.example.ai and use the launch form, or apply a sample App:

Terminal window
kubectl apply -n apps -f examples/static-inline-app.yaml # or python-inline-app.yaml
kubectl get apps -n apps -w
NAME SOURCE PHASE URL
docs-site inline Running https://docs-site.apps.example.ai

When PHASE reaches Running, open the URL. Private apps redirect to Keycloak; public apps (access.public: true) are reachable anonymously.

Point Claude Code (or any MCP client) at the cluster and launch with natural language:

Terminal window
claude mcp add --transport http nebari-apps https://apps.example.ai/mcp

The first tool call will ask you to log in via the Keycloak device flow (the agent shows a verification URL and code). See the MCP server guide for the tool list and the scaffolding skill for generating apps the agent can launch.

Terminal window
# The App's own status: phase, URL, replicas, conditions
kubectl describe app docs-site -n apps
# The children the operator created
kubectl get deploy,svc,cm,nebariapp -n apps
# Expected App conditions:
# Validated: True - spec is coherent, namespace opted in
# WorkloadReady: True - all replicas ready
# RoutingReady: True - the NebariApp reports Ready (routing/TLS/auth)

Deleting an App cascades: the Deployment, Service, ConfigMap, and NebariApp (and through it the HTTPRoute, certificate, and OIDC client) are all garbage-collected.