Skip to content

Configuration

Updated 6 min read

The chart’s defaults live in charts/nebari-apps/values.yaml. A ready-to-edit values file for a full Nebari deployment is at examples/nebari-values.yaml; only clusterDomain and keycloak.url need changing.

ValueDefaultPurpose
clusterDomain— (required)The cluster’s base domain. The chart fails to render without it.
appsDomainapps.<clusterDomain>Domain apps are served under: https://<subdomain>.<appsDomain>.
gatewaypublicShared Gateway the UI’s and every app’s NebariApp attach to (public | internal).
tls.enabledtrueTLS for the UI and every launched app. false serves plain HTTP (no certificates); local dev only.
keycloak.url— (required when api.auth.enabled)Browser-facing Keycloak base URL, e.g. https://keycloak.example.ai/auth.
keycloak.realmnebariRealm the API validates tokens against and the UI/MCP log in to.
keycloak.internalUrl—In-cluster Keycloak URL (split horizon) used only to fetch JWKS.
ValueDefaultPurpose
apps.defaultResources.static / .python50m/64Mi → 500m/256Mi; 250m/512Mi → 2/2GiRequests/limits filled in for any cpu/memory value an App’s runtime.resources leaves unset (static apps vs runtime.pixiTask apps).
apps.limitRange.enabledfalseRender a LimitRange (default, defaultRequest, max) into every api.allowedNamespaces namespace. The namespaces must exist.
apps.networkPolicy.enabledfalseRender a NetworkPolicy for launched app pods in every api.allowedNamespaces namespace: ingress only from gatewayNamespace on port 8080; egress only to DNS and to the internet minus blockedCIDRs (private, CGNAT and link-local ranges by default), so app code cannot reach cluster-internal services. Needs a CNI that enforces NetworkPolicy.
apps.landingPage.enabledfalseCluster default for registering launched apps as tiles on the Nebari landing page. Off: an app appears only if it sets spec.landingPage.enabled: true. On: every app gets a tile unless it sets enabled: false. Apps are reachable at their URL and listed in the Apps UI either way.
staticImagenginxinc/nginx-unprivileged:1.27-alpineServes static app content; must listen on 8080 as non-root.
gitImagealpine/git:v2.47.2Init-container image that clones git sources.
pythonImageghcr.io/prefix-dev/pixi:0.68.1-nobleRuns Python/pixi apps (runtime.pixiTask); must provide pixi and run as non-root.
ValueDefaultPurpose
operator.image.repository / tag / pullPolicyghcr.io/nebari-dev/apps-pack/apps-operator / "" (chart appVersion) / IfNotPresentOperator image.
operator.replicaCount1Replicas; more than one needs leaderElection.
operator.leaderElectiontrueLease-based leader election in the release namespace.
operator.resources100m/128Mi requests, 500m/256Mi limitsPod resources.

The operator reads its configuration from flags the chart renders (--apps-domain, --gateway, --static-image, --git-image, --python-image, --tls-disabled, --landing-page, --leader-elect); each flag also accepts the matching environment variable (APPS_DOMAIN, GATEWAY, STATIC_IMAGE, GIT_IMAGE, PYTHON_IMAGE, TLS_DISABLED, LANDING_PAGE).

ValueDefaultPurpose
api.enabledtrueDeploy the REST API (the UI and MCP are thin clients over it).
api.image.repository / tag / pullPolicyghcr.io/nebari-dev/apps-pack/apps-api / "" / IfNotPresentAPI image.
api.replicaCount1The API is stateless; more replicas are safe.
api.auth.enabledtrueKeycloak JWT bearer auth for the API and keycloak-js login in the UI. Disable only for local development; off, every caller is anonymous with full access. Rendering fails if it is off while tls.enabled=true, unless api.auth.insecureAllowAnonymous is set.
api.auth.insecureAllowAnonymousfalseExplicit opt-in to an unauthenticated API on a TLS install. Leave off.
api.adminGroups["admin"]Keycloak groups whose members see and manage every App. Everyone else sees and manages only the Apps they launched.
api.allowedNamespaces["apps"]Namespaces users may launch into. Empty list = every namespace labeled nebari.dev/managed=true. default and kube-* are never offered. The API gets a RoleBinding in each listed namespace (they must exist before install/upgrade); an empty list binds its namespaced role cluster-wide instead.
api.resources100m/128Mi requests, 500m/256Mi limitsPod resources.

Environment the API container receives: APPS_DOMAIN, APPS_NAMESPACES, APPS_ADMIN_GROUPS, AUTH_ENABLED, TLS_DISABLED, OIDC_ISSUER, OIDC_ISSUER_INTERNAL, UI_KEYCLOAK_URL, UI_KEYCLOAK_REALM, UI_KEYCLOAK_CLIENT_ID and OIDC_DEVICE_CLIENT_ID (from the Secret the UI’s NebariApp provisions), and OIDC_ALLOWED_CLIENTS (built from those two: a token is accepted only if its azp or aud names one of them; if the Secret is missing every token is rejected). The MCP server receives the same OIDC_ALLOWED_CLIENTS. Not rendered by the chart but honoured when set on the container: OIDC_JWKS_URL, OIDC_AUDIENCE, LOG_LEVEL (default INFO), MAX_UPLOAD_BYTES (default 921600, the cap on an upload and on its extracted contents) and MAX_REQUEST_BYTES (default 2097152, the cap on any request body, enforced while it streams in).

ValueDefaultPurpose
ui.enabledtrueDeploy the web UI (also the nginx front door that proxies /api/ and /mcp).
ui.image.repository / tag / pullPolicyghcr.io/nebari-dev/apps-pack/apps-ui / "" / IfNotPresentUI image.
ui.replicaCount1Replicas.
ui.hostname<appsDomain>Hostname the UI is served at.
ui.titleNebari AppsBrowser-tab title.
ui.branding.logoUrl / logoUrlDark / faviconUrlbuilt-in Nebari assetsLogo and favicon overrides (http(s), root-relative, or data: image URI).
ui.branding.theme.light / dark{}CSS token overrides, validated in the browser — see Branding.
ui.branding.banners.top / bottom{}Classification banners (text, background, foreground).
ui.landingPage.displayName / description / category / iconApps / Launch and manage web applications on this cluster. / Platform / built-in iconThe pack’s own tile on the landing page (always registered while ui.enabled).
ui.landingPage.healthCheck.enabled / path / intervalSeconds / timeoutSecondstrue / /healthz / 30 / 5Health indicator next to the tile, polled by the nebari-operator.
ui.resources50m/64Mi requests, 250m/128Mi limitsPod resources.
ValueDefaultPurpose
mcp.enabledtrueDeploy the MCP server at https://<ui hostname>/mcp (proxied by the UI’s nginx; no separate hostname). When off, /mcp returns 404.
mcp.image.repository / tag / pullPolicyghcr.io/nebari-dev/apps-pack/apps-mcp / "" / IfNotPresentMCP image.
mcp.replicaCount1Keep at 1: device-flow sessions are held in process memory.
mcp.resources50m/128Mi requests, 250m/256Mi limitsPod resources.

Environment: API_URL, AUTH_ENABLED, OIDC_ISSUER, OIDC_ISSUER_INTERNAL, OIDC_DEVICE_CLIENT_ID (from the UI’s OIDC Secret); optional OIDC_JWKS_URL, OIDC_AUDIENCE, HOST, PORT, LOG_LEVEL.

apps-api and apps-mcp write one JSON object per line to stdout (timestamp, level, logger, event, plus fields). Every HTTP request gets an access line with request_id, method, path, status, duration_ms and, on the API, the authenticated user. The id comes from the incoming X-Request-ID header (on a Nebari cluster, the one Envoy assigns at the gateway) or is generated, and is returned on the response. The UI’s nginx and the MCP server pass it on to apps-api, and MCP tool calls log it with the tool name, so one id ties a browser or agent action to the API’s lines for it. Probe requests log at DEBUG.

ComponentLivenessReadiness
apps-apiGET /api/v1/healthzGET /api/v1/readyz: Kubernetes API reachable and, with auth on, the realm JWKS loads
apps-mcpGET /healthzGET /healthz
apps-uiGET /healthzGET /healthz
ValueDefaultPurpose
nameOverride / fullnameOverride""Standard Helm name overrides.

The pack integrates with the platform through two kinds of NebariApp (reconcilers.nebari.dev/v1). Field semantics are documented in the NebariApp CRD reference.

The UI’s NebariApp (<release>-ui, rendered by templates/ui.yaml):

FieldValueSource
hostnameui.hostname or <appsDomain>chart
service.name / portthe UI Service / 8080chart
routing.routesone PathPrefix / routefixed
routing.tls.enabledtls.enabledchart
gatewaygatewaychart
auth.enabledapi.auth.enabledchart
auth.provider / provisionClientkeycloak / truefixed (when auth is on)
auth.enforceAtGatewayfalsefixed — the UI logs in itself with keycloak-js; the API checks bearer tokens
auth.redirectURI/*fixed
auth.scopesopenid profile email groupsfixed
auth.spaClient.enabledtruefixed — provisions the public PKCE client the UI uses; its id lands in the <release>-ui-oidc-client Secret (spa-client-id)
auth.deviceFlowClient.enabledtrue when mcp.enabledprovisions the RFC 8628 client the MCP server uses (device-client-id in the same Secret)
landingPage.enabledtruefixed
landingPage.displayName / description / category / iconui.landingPage.*chart (icon defaults to the bundled SVG as a data URI)
landingPage.healthCheck.*ui.landingPage.healthCheck.*chart

One NebariApp per launched App (app-<name>, emitted by the operator from the App’s spec):

FieldValueSource
hostname<spec.access.subdomain>.<appsDomain>App + chart
service.name / portapp-<name> / 8080operator
routing.routesone PathPrefix / routefixed
routing.tls.enabledtls.enabledchart
gatewaygatewaychart
auth.enabled!spec.access.publicApp
auth.provider / provisionClient / scopeskeycloak / true / openid profile email groupsfixed (private apps)
auth.groupsspec.access.groupsApp — not enforced by the gateway in nebari-operator 0.1.0; see Access control
landingPage.enabledspec.landingPage.enabled, else apps.landingPage.enabledApp, then chart
landingPage.displayName / description / icon / categoryspec.displayName / spec.description / spec.thumbnail / AppsApp (only when the tile is enabled)

Fields the pack never sets: auth.users, auth.enforceAtGateway on apps (defaults to enforced), landingPage.priority, landingPage.iconLight/iconDark, landingPage.externalUrl.