v0.6.0
Install guide
The homepage covers the two commands
that get you running: a bundled evaluation Postgres with TLS off, or
your own Postgres with cert-manager issuing the certificate. Everything
below is what to add on top of either one — six real decisions the
chart makes you take a side on, each with the reasoning and the exact
--set flags for it.
Nothing here is required. Skip a section entirely and the chart's default is the one described in its first branch.
Authentication
Local accounts (admin-created, argon2-hashed passwords) always work — it's the only way in on a fresh install, and the only path for the bootstrap admin account. LDAP/Active Directory and OIDC (SSO) are two independent, optional backends on top of it, both tried automatically whenever a login isn't a valid local password.
Do you want people to log in with an existing directory or identity provider, instead of an admin creating every account by hand?
No — local accounts are fine
Nothing to configure. Create accounts from the Users admin tab; this is the chart's default.
Yes — we run LDAP or Active Directory
Helve binds as a service account to search for a user's DN, then opens a second connection and binds as that DN with the submitted password — the actual credential check.
--set ldap.enabled=true \
--set ldap.url=ldaps://dc1.example.com \
--set ldap.bindDn="cn=svc-helve,ou=service,dc=example,dc=com" \
--set ldap.existingSecret=helve-ldap-bind \
--set ldap.baseDn="ou=people,dc=example,dc=com" \
--set 'ldap.userFilter=(sAMAccountName={username})'
ldap.existingSecret is a Secret you create yourself
(key bindPassword) — the bind password never goes in
values.yaml. Use
(uid={username}) instead of sAMAccountName
for a plain OpenLDAP directory. ldap.url should be
ldaps:// — a plain ldap:// sends the bind
password and directory contents unencrypted, and the chart warns
about it at startup if you do.
Should logging in for the first time create a Helve account automatically?
Yes — anyone who authenticates gets an account
This is the default (ldap.autoProvision: true) —
nothing to add. The role is user unless the
group mapping below says otherwise.
No — an admin decides who gets an account
--set ldap.autoProvision=false
Pre-create the account from the Users tab first (any password — it's never checked once LDAP vouches for that username). A directory user with no matching account gets a clear 403, not a generic login failure.
Should membership in a directory group grant the admin role?
Yes
--set ldap.adminGroupDn="cn=helve-admins,ou=groups,dc=example,dc=com"
Re-checked on every login, not just the first — the directory becomes the source of truth for that account's role, overriding a manual change made from the Users tab.
No
Leave it unset (the default) — every LDAP-provisioned account is a regular user until an admin promotes it by hand.
Yes — we use an OIDC / SSO provider
Okta, Azure AD, Google Workspace, Keycloak, Authentik — anything that speaks standard OIDC discovery. Redirect-based: a "Log in with SSO" button appears on the login page automatically once this is on.
--set oidc.enabled=true \
--set oidc.issuerUrl=https://your-idp.example.com/realms/your-realm \
--set oidc.clientId=helve \
--set oidc.existingSecret=helve-oidc-secret
oidc.existingSecret holds the client secret (key
clientSecret). Register the redirect URI with your
IdP as https://<host>/api/auth/oidc/callback.
Should logging in for the first time create a Helve account automatically?
Yes — anyone the IdP authenticates gets an account
The default (oidc.autoProvision: true) —
nothing to add. Accounts are matched by the token's
sub claim, the only value the spec guarantees
is stable; a username collision with an existing account is
refused with a clear error rather than silently taking it
over.
No — an admin decides who gets an account
--set oidc.autoProvision=false
Pre-create the account from the Users tab with a matching
username first. It links to the IdP identity on that
person's first SSO login and every login after that is
matched by sub, not username again. An
unrecognized identity with no matching pre-created account
gets a clear 403.
Should membership in an IdP group grant the admin role?
Yes
--set oidc.adminGroup=helve-admins
Matched against the token's groups claim by
default — set oidc.groupsClaim if your IdP
calls it something else. Re-checked on every login, same as
the LDAP group mapping above.
No
Leave it unset — every auto-provisioned account is a
regular user. oidc.usernameClaim also has a
default (preferred_username) worth checking
against what your IdP actually issues.
Both — LDAP for existing staff, SSO for everyone else
Enable both blocks above together. They're independent and simultaneous — local password login stays available underneath either way.
Home directories
Off by default. A template opts in individually by setting a "Home
directory mount path" (e.g. /home/jovyan) — this decision
only picks where the storage actually comes from.
Do any of your templates need a home directory that survives a pod restart?
No
Nothing to configure — homeDrives.mode: off is the
default. A template can still mount one shared, out-of-band
PersistentVolumeClaim (not per-user) via its own storage-mount
field.
Yes
Do all your nodes already mount the same shared filesystem (NFS, CephFS, a parallel filesystem) at the OS level?
Yes
--set homeDrives.mode=hostPath \
--set homeDrives.hostPath.basePath=/mnt/homes
Mounts <basePath>/<username> as a
hostPath volume — no StorageClass or CSI
driver involved at all, since the path looks identical on
every node already. fsGroup does not
apply to hostPath volumes, though — grant
write access with a per-user supplemental group
(Users tab) matching the export's ownership instead.
No, but I have a ReadWriteMany-capable StorageClass
One PVC per user that Helve creates and owns, or one shared PVC you provision yourself?
Per-user, created automatically
--set homeDrives.mode=pvc \
--set homeDrives.pvc.strategy=dynamic \
--set homeDrives.pvc.storageClassName=your-rwx-class \
--set homeDrives.pvc.size=10Gi
One home-<username> PersistentVolumeClaim
per user, on that user's first home-drive launch — a
size cap per person, but Helve never deletes one,
not even on user deletion.
One shared PVC, subdirectory per user
--set homeDrives.mode=pvc \
--set homeDrives.pvc.strategy=shared \
--set homeDrives.pvc.existingClaimName=your-existing-claim
You provision exactly one PVC yourself; every user's
pod mounts it with subPath: <username>.
Helve creates no PVC at all here — one shared capacity
pool instead of a size cap per person.
Neither — no shared filesystem, no RWX StorageClass
Stay at homeDrives.mode: off for now; get one
of the two in place first. A single, statically-bound
PV/PVC pair still works for a shared (not
per-user) mount via a template's own storage-mount field.
Database
Do you already run Postgres somewhere reachable from the cluster?
Yes
--set database.existingSecret=helve-db-app
A Secret holding a connection string (key uri by
default — override with database.existingSecretKey).
Helve runs its own migrations on startup; nothing else needs
creating beyond the database and role themselves.
No — just evaluating
--set database.deploy.enabled=true
A single-replica CloudNativePG Postgres deployed alongside Helve, no backups. Requires the CloudNativePG operator already installed. Not for anything you'd be upset to lose.
TLS
Do you want the chart to get you a certificate?
Yes — cert-manager is installed
--set ingress.tls.issuerRef.name=letsencrypt
This is the chart's default mode (ingress.tls.mode:
certManager) — an Issuer/ClusterIssuer
name is the one thing it can't default for you. If you're also
using per-deployment proxy origins below, you need a
DNS-01 solver — HTTP-01 cannot issue a wildcard
certificate.
I already have a certificate
--set ingress.tls.mode=existingSecret \
--set ingress.tls.secretName=your-tls-secret
A Secret you manage yourself, used as-is.
No TLS — plain HTTP, evaluation only
--set ingress.tls.enabled=false
Session cookies can't be marked Secure over plain
HTTP — fine for a throwaway evaluation, not for anything real.
Proxy origins
This is the one setting where the safe answer isn't the chart's literal default — see "The part most dashboards get wrong" on the homepage for why same-origin proxying is a real risk, not a style preference.
Will more than one person use this cluster, or might you ever launch a workload you don't fully control (someone else's notebook image, a random model server)?
Yes — the common case
Keep proxy.separateOrigins: true (the default) —
nothing to add beyond a wildcard DNS record, and a matching
wildcard certificate, for *.proxy.<host>.
Each deployment then gets served from its own origin, so its
JavaScript can never call Helve's own API as whoever is
browsing it.
No — single-user local development only
--set proxy.separateOrigins=false \
--set proxy.allowSameOriginProxy=true
The chart refuses to render separateOrigins=false
without this explicit second flag, on purpose — it's an
acknowledged risk, not a silent downgrade.
Pod-to-pod isolation
Everything above is enforced at the HTTP layer, on the routes Helve itself serves. By default, that's the only boundary between tenants: any pod in the shared namespace — another user's notebook kernel, a coding agent hitting an LLM engine — can still reach any other pod's ClusterIP directly, bypassing Helve entirely.
Do multiple people launch workloads on this namespace, and do you want the network itself, not just Helve's own checks, to keep them apart?
No, or not yet — the default
Leave istio.enabled unset (false).
Nothing changes: pods behave exactly as they always have.
Yes — I already run Istio
kubectl label namespace helve istio-injection=enabled
--set istio.enabled=true
Requires Istio 1.17+ already installed — this chart never
installs or configures Istio itself, the same "points at it,
never provisions it" stance it already takes with an external
LDAP/OIDC server. From then on, every newly launched
deployment gets its own ServiceAccount and a per-deployment
AuthorizationPolicy allowing inbound traffic only
from Helve's own backend and that deployment's owner —
enforced symmetrically at every pod's own sidecar, so
turning it on doesn't require a separate egress rule per
tenant. It only gates traffic between launched pods in this
namespace; it doesn't restrict egress to the internet, other
namespaces, or the Kubernetes API. Anything already running
stays unprotected until its owner relaunches it — Helve
never recreates a pod on its own.
Put it together
Take either command from the homepage
and append whichever --set flags above apply — they all
stack. Full field-by-field reference, including everything not
covered here, lives in the chart itself.