Local development
Prerequisites
Section titled “Prerequisites”docker, kind, helm, kubectl, git.
Two stacks
Section titled “Two stacks”cd dev
make up # kind + MetalLB + Envoy Gateway + cert-manager + Keycloak # + nebari-operator + Ray Serve with both NebariAppsmake up-standalone # kind + Ray Serve only, no operatormake down # delete the clustermake up finishes by printing both URLs:
| Default hostname | |
|---|---|
| Ray Serve | https://rayserve.nebari.local |
| Ray dashboard | https://ray-dashboard.nebari.local |
Override with make up HOSTNAME=... DASH_HOSTNAME=..., or CLUSTER_NAME= for the kind
cluster (default rayserve-dev).
make up-standalone skips the platform entirely and reuses an existing cluster if one is
there. Reach it by port-forward:
kubectl port-forward svc/rayserve-nebari-rayserve-pack-serve-svc 8000:8000kubectl port-forward svc/rayserve-nebari-rayserve-pack-head-svc 8265:8265What make up does
Section titled “What make up does”- Creates the kind cluster and installs MetalLB, deriving its IP pool from the
kindDocker network at runtime. - Clones nebari-operator into
.cache/and runs its service-install and Keycloak setup scripts (Envoy Gateway, cert-manager, Keycloak). - Installs the operator and labels the
defaultnamespacenebari.dev/managed=true. helm dependency updatethenhelm upgrade --installwith bothNebariApps enabled.- Waits for
nebariapp/rayserve-nebari-rayserve-packandnebariapp/rayserve-nebari-rayserve-pack-dashboardto reachReady. - Runs the operator’s
update-hosts.shto add both hostnames to/etc/hosts— this uses sudo.
The --wait --timeout 5m on the Helm install is doing real work: the Ray image is large,
and the head has to be up before workers can join.
Iterating on the chart
Section titled “Iterating on the chart”make up # re-runs helm upgrade --install against the existing clusterThe cluster and platform stay in place, so the loop is a Helm upgrade rather than a rebuild. For a faster check with no cluster at all:
helm dependency update chart # once — templating fails without the kuberay-operator subcharthelm template rayserve chart --set nebariapp.enabled=true \ --set nebariapp.serve.enabled=true \ --set nebariapp.hostname=rayserve.nebari.local \ --set nebariapp.dashboard.hostname=ray-dashboard.nebari.local | lessUseful for confirming that a values change renders what you expect — particularly the CA bundle injection and the GPU toleration, both of which are conditional.
Watching it come up
Section titled “Watching it come up”kubectl get pods -wkubectl get rayservice,rayclusterkubectl logs -l ray.io/node-type=head -fThe dev stack installs into default, not a rayserve namespace — that is what the
Makefile labels for the operator.
Cleaning up
Section titled “Cleaning up”make downDeletes the kind cluster. The /etc/hosts entries and the .cache/nebari-operator clone
are left behind.
What local development cannot tell you
Section titled “What local development cannot tell you”- The Argo CD
ignoreDifferencesbehaviour. The dev stack uses Helm, so the interaction that can silently drop the CA bundle never appears here. - GPU scheduling. No GPU nodes, no taints — the toleration logic renders but is never exercised.
- Real TLS. Certificates are issued locally; a public ACME issuer and DNS validation behave differently.
- Version-matched notebooks. There is no JupyterHub in the dev stack, so the Ray client version coupling described in Connecting from Jupyter goes untested.
Docs site
Section titled “Docs site”cd docsnpm cinpm run dev # hot reload at http://localhost:4321npm run build # static build into docs/dist/npm test # unit testsPages live in docs/src/content/docs/; the sidebar is in docs/astro.config.mjs. Merges to
main publish to packs.nebari.dev/rayserve-pack/,
and pull requests touching docs/ get a preview URL posted as a comment.