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.