Concepts
This section explains the key concepts behind Nebari Software Packs and the deployment methods the template supports.
The NebariApp integration point
Section titled “The NebariApp integration point”Every software pack has exactly one integration point with the Nebari platform: the NebariApp custom resource. When you create a NebariApp, the nebari-operator watches for it and automatically configures routing, TLS, and authentication.
The NebariApp is just a Kubernetes resource - it can live in a plain YAML file, a Kustomize base, or a Helm template. In Helm charts, you typically make it conditional so the chart works both standalone and on Nebari:
{{- if .Values.nebariapp.enabled }}apiVersion: reconcilers.nebari.dev/v1kind: NebariAppmetadata: name: {{ include "my-pack.fullname" . }}spec: hostname: {{ required "nebariapp.hostname is required" .Values.nebariapp.hostname }} service: name: {{ include "my-pack.fullname" . }} port: 80{{- end }}With plain YAML or Kustomize, the NebariApp manifest is always present. When deploying standalone, skip that file or exclude it from your apply command.
Deployment methods
Section titled “Deployment methods”All three Kubernetes deployment methods are first-class in the template and in ArgoCD.
Plain YAML
Section titled “Plain YAML”The lowest barrier to entry. Your pack is a set of .yaml files - deployment.yaml,
service.yaml, and nebariapp.yaml. Users run kubectl apply -f ..
Best for: packs with no configuration variability, or internal tools where simplicity trumps flexibility.
Kustomize
Section titled “Kustomize”Kustomize overlays let you patch environment-specific values (hostname, auth settings, resource limits) on top of a shared base without duplicating the base manifests.
base/ kustomization.yaml deployment.yaml service.yaml nebariapp.yamloverlays/ dev/ kustomization.yaml nebariapp-patch.yaml # dev hostname, no auth production/ kustomization.yaml nebariapp-patch.yaml # prod hostname, auth + groupsBest for: packs deployed to multiple environments with known configuration differences.
Helm is the most common choice for packs that wrap existing upstream software. You add the upstream chart as a dependency and add a NebariApp template that points to its service - you do not rewrite the app.
# Chart.yaml - add the upstream chart as a dependencydependencies: - name: podinfo version: 6.10.1 repository: oci://ghcr.io/stefanprodan/charts# templates/nebariapp.yaml - the only template you write{{- if .Values.nebariapp.enabled }}apiVersion: reconcilers.nebari.dev/v1kind: NebariAppspec: hostname: {{ .Values.nebariapp.hostname }} service: name: {{ .Release.Name }}-podinfo # upstream service name port: 9898{{- end }}Best for: wrapping existing Helm charts and for packs with rich configuration needs.
Template examples
Section titled “Template examples”The template repo ships five examples of increasing complexity.
Example 1: Vanilla YAML
Section titled “Example 1: Vanilla YAML”Plain Kubernetes manifests. kubectl apply -f examples/vanilla-yaml/. No tooling
beyond kubectl. The NebariApp sits alongside deployment.yaml and service.yaml
as a peer file.
Example 2: Kustomize (Nginx)
Section titled “Example 2: Kustomize (Nginx)”Same nginx app as the vanilla example, structured with Kustomize overlays for dev and production. Demonstrates patching hostname and auth settings per environment without duplicating the base.
Example 3: Helm - Basic Pack (Nginx)
Section titled “Example 3: Helm - Basic Pack (Nginx)”The simplest possible Helm chart - nginx with a conditional NebariApp template and a
nebariapp.enabled toggle. Shows the standalone / Nebari mode switch.
Example 4: Helm - Auth-Aware FastAPI
Section titled “Example 4: Helm - Auth-Aware FastAPI”A custom Python app that reads the IdToken-* cookie set by Envoy Gateway after
Keycloak authentication. The key snippet:
def get_id_token(request: Request) -> str | None: for name, value in request.cookies.items(): if name.startswith("IdToken-"): return value return NoneShows how to extract and decode the JWT to get preferred_username, email, and
groups.
Example 5: Helm - Wrapping an Existing Chart (Podinfo)
Section titled “Example 5: Helm - Wrapping an Existing Chart (Podinfo)”This is the most realistic use case. Most Helm-based packs wrap existing software. You add the upstream chart as a dependency, override values, and add a NebariApp that points to the upstream service. You do not write a Deployment or Service of your own.
Local development
Section titled “Local development”The dev/ directory provides a Makefile for local development with
kind. Running any up-* target automatically creates a
kind cluster with the full Nebari infrastructure stack - MetalLB, Envoy Gateway,
cert-manager, Keycloak, and the nebari-operator.
cd dev
make up-vanilla # deploy vanilla YAML examplemake up-kustomize # deploy kustomize example (dev overlay)make up-basic # deploy Helm nginx examplemake up-fastapi # deploy FastAPI Helm example (auth enabled)make update-hosts # update /etc/hosts with NebariApp hostnamesmake down # delete the kind clusterThe first make up-* run takes 5-10 minutes (cluster and infrastructure setup).
Subsequent runs reuse the existing cluster and are fast.
Pack metadata
Section titled “Pack metadata”Every tracked pack has a pack-metadata.yaml at its repo root. Key fields:
level: experimental # experimental | alpha | beta | gaowner: github-usernamenebariapp_integration: full # none | partial | full | nascope: standalone-supported: yesThe software pack dashboard aggregates these files and renders a single view of every tracked pack. See the Release Readiness page for the full field reference and the promotion checklist.