JupyterHub
JupyterHub provides multi-user notebook servers for teams, classrooms, and research platforms. The HelmForge chart deploys the Hub and configurable-http-proxy with KubeSpawner-oriented defaults.
Key Features
- Hub and configurable-http-proxy workloads with managed proxy token Secret
- KubeSpawner configuration for user notebook pods
- Optional user PVCs and single-user image profiles
- Namespaced RBAC for user pods
- Ingress, Gateway API, dual-stack Service fields, NetworkPolicy, ServiceMonitor, PDB, and External Secrets
- Guardrails for Hub replica count, persistence, and cookie secret behavior
Installation
helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install jupyterhub helmforge/jupyterhub --namespace jupyterhub --create-namespace
helm install jupyterhub oci://ghcr.io/helmforgedev/helm/jupyterhub --namespace jupyterhub --create-namespace
Examples
Single-user image:
singleuser:
image:
name: quay.io/jupyter/scipy-notebook
tag: '2026-05-26'
Profiles:
singleuser:
profiles:
- display_name: Standard
slug: standard
default: true
kubespawner_override:
cpu_limit: 1
mem_limit: 2G
Operations
Keep the default SQLite Hub database to a single Hub replica. For HA Hub deployments, configure an external database, provide a shared cookie secret, and avoid single-writer PVC modes for shared Hub state.
Architecture
The chart deploys a JupyterHub Hub, configurable-http-proxy, and RBAC for KubeSpawner-managed user pods. The Hub owns authentication, spawning decisions, and user state. The proxy receives browser traffic and routes each authenticated user to the correct notebook server.
Main runtime pieces:
- Public traffic reaches configurable-http-proxy through Ingress or Gateway API.
- The Hub authenticates users and instructs KubeSpawner to create notebook pods.
- User notebook pods run with the configured single-user image and optional per-user storage.
- The proxy routes each user to their notebook server.
- Optional metrics and ServiceMonitor expose Hub and proxy observability.
Production Values
For production, replace DummyAuthenticator, use durable storage, configure a real authenticator, and keep metrics private:
auth:
type: custom
hub:
extraConfig: |
c.JupyterHub.authenticator_class = "nativeauthenticator.NativeAuthenticator"
# Configure c.JupyterHub.db_url before increasing hub.replicaCount.
persistence:
enabled: true
storageClass: fast-retain
size: 20Gi
singleuser:
storage:
enabled: true
storageClass: fast-retain
capacity: 20Gi
profiles:
- display_name: Standard Lab
slug: standard
default: true
kubespawner_override:
default_url: /lab
metrics:
authenticatePrometheus: true
serviceMonitor:
enabled: false
Do not expose the default dummy authentication mode on a public route. It is useful for local validation only.
Hub Database And HA
The default Hub state uses SQLite on the Hub PVC. Keep hub.replicaCount=1 in that mode. Before increasing replicas:
- Configure an external database with
c.JupyterHub.db_urlinhub.extraConfig. - Provide a stable shared cookie secret.
- Avoid single-writer PVC modes for state shared by multiple Hub replicas.
- Validate login, spawn, stop, and resume flows behind your load balancer.
For HA Hubs without Hub persistence, create a Secret containing the hex-encoded JupyterHub cookie secret:
hub:
cookieSecret:
existingSecret: jupyterhub-cookie-secret
persistence:
enabled: false
Without a shared cookie secret, login sessions can fail when requests move between Hub replicas.
Single-User Profiles
Profiles give platform teams controlled notebook options:
singleuser:
image:
name: quay.io/jupyter/scipy-notebook
tag: '2026-05-26'
profiles:
- display_name: Data Science
slug: data-science
default: true
kubespawner_override:
cpu_limit: 2
mem_limit: 4G
- display_name: Lightweight
slug: lightweight
kubespawner_override:
cpu_limit: 1
mem_limit: 1G
Use profile overrides to limit CPU, memory, GPU access, default URL, images, and storage behavior.
Networking
Ingress example:
ingress:
enabled: true
ingressClassName: traefik
hosts:
- host: hub.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: jupyterhub-tls
hosts:
- hub.example.com
Gateway API example:
gateway:
enabled: true
parentRefs:
- name: public
namespace: gateway-system
hostnames:
- hub.example.com
Enable NetworkPolicy once you have confirmed the CNI enforces it and user pods can still reach the Hub and proxy.
Secrets
The proxy token can be chart-generated for local environments. Production should use an existing Secret or External Secrets Operator:
proxy:
existingSecret: jupyterhub-proxy-token
externalSecrets:
enabled: true
secretStoreRef:
name: platform-secrets
kind: ClusterSecretStore
data:
- secretKey: proxy-token
remoteRef:
key: jupyterhub/proxy
property: proxy-token
Validation
After deployment:
helm test jupyterhub -n jupyterhub
kubectl get pods -n jupyterhub -l app.kubernetes.io/name=jupyterhub
kubectl logs -n jupyterhub deploy/jupyterhub-hub --since=10m
kubectl get events -n jupyterhub --sort-by=.lastTimestamp
Also validate a full user workflow: login, spawn a notebook, restart the Hub, and confirm the notebook session survives
when hub.cleanupServers=false.
Common Issues
| Symptom | Likely Cause | Fix |
|---|---|---|
| Login sessions fail behind multiple Hubs | Cookie secret is not shared | Set hub.cookieSecret.existingSecret. |
| HA Hub render fails | SQLite DB or RWO persistence is incompatible | Configure external DB and compatible persistence. |
| ServiceMonitor cannot scrape | Prometheus auth remains enabled | Use private scrape credentials or explicitly disable metrics auth for private paths. |
| User pods stay pending | RBAC, quotas, storage class, or image pulls fail | Describe the user pod and check namespace events. |
Values
| Parameter | Default | Description |
|---|---|---|
hub.replicaCount | 1 | Number of Hub replicas. |
hub.persistence.enabled | true | Persist Hub state. |
proxy.secretToken | "" | Inline proxy token, generated when empty. |
proxy.existingSecret | "" | Existing Secret for proxy token. |
singleuser.image.name | quay.io/jupyter/base-notebook | Default notebook image. |
singleuser.storage.enabled | true | Create user notebook PVCs. |
service.ipFamilyPolicy | null | Optional Service dual-stack policy. |
gateway.enabled | false | Render Gateway API HTTPRoute. |
metrics.serviceMonitor.enabled | false | Render ServiceMonitor. |
externalSecrets.enabled | false | Render ExternalSecret resources. |