Complete registry access and bundle extraction first. Commands use Bash (Git Bash on Windows).
For an SSO-only deployment, provision the license and first administrator mapping before startup using Authentication. Otherwise use the local administrator bootstrap below.
Install Enterprise with Helm
When using in-cluster PostgreSQL, install the CloudNativePG operator before the chart; see its installation instructions. External PostgreSQL does not need that operator.
The enterprise Helm chart is published as an OCI artifact in Harbor and
bundles the core chart as a vendored subchart. You do not need a separate
tokentimer-core install.
The bundle's helm/values.yaml documents the Enterprise overlay options, and
helm/examples/ contains scenario starter files (see the
table below).
a. Create the pull secret
kubectl create namespace tokentimer
# Use a new private directory; enter the robot token at the Docker prompt.
mkdir -m 700 ./registry-auth
docker --config ./registry-auth login harbor.tokentimer.ch -u 'robot$tokentimer-enterprise+<company>-<your-id>'
kubectl create secret generic tokentimer-enterprise-registry \
--type=kubernetes.io/dockerconfigjson \
--from-file=.dockerconfigjson=./registry-auth/config.json \
-n tokentimer --dry-run=client -o yaml | kubectl apply -f -
# Remove this temporary credential copy after verifying the Secret.
rm ./registry-auth/config.json
rmdir ./registry-auth
The pull secret name matches the chart default (global.imagePullSecrets).
If you choose a different name, override via
--set tokentimer.global.imagePullSecrets[0].name=<your-name>. More detail,
including GitOps-friendly manifest generation, is in
Registry access.
b. Authenticate Helm to Harbor
The Helm CLI needs Harbor credentials to fetch the chart (the pull secret from step a only covers container-image pulls by the cluster, not the chart pull by the Helm client):
helm registry login harbor.tokentimer.ch \
-u 'robot$tokentimer-enterprise+<company>-<your-id>'
# Enter the robot token at the password prompt.
c. Create the license secret (optional)
You can supply the license JWT via TT_LICENSE_KEY in values, via the
dashboard after boot, and/or via a file mounted from a Kubernetes Secret. The
chart treats the license volume as optional when no Secret exists.
File-based install:
read -r -p 'License file path: ' ENTERPRISE_LICENSE_FILE
kubectl create secret generic tokentimer-license \
--from-file=license.key="$ENTERPRISE_LICENSE_FILE" \
-n tokentimer
If you rely on env or UI only, skip this step and omit
--set license.existingSecret=... on install (or set license.existingSecret
to "" in values if you overrode it).
d. Pick a starter values file and customize
cp helm/examples/values-external-db.yaml ./my-values.yaml
$EDITOR my-values.yaml
Set public URLs before SSO or production use. Chart defaults leave
localhost origins in place; if you skip this step, Manage SSO providers
will show redirect URIs such as http://localhost:4000/auth/oidc/<slug>/callback.
In my-values.yaml, under the core subchart key tokentimer.config (same
names as Compose APP_URL / API_URL):
tokentimer:
config:
# Dashboard origin (maps to APP_URL): links in email, where users open the UI
baseUrl: https://tokentimer.your-domain.com
# API origin (maps to API_URL): must be the URL browsers and your IdP use to reach the API
apiUrl: https://tokentimer.your-domain.com
Set tokentimer.ingress.hosts and tokentimer.ingress.tls to the same public hostname, and supply the referenced TLS Secret (or configure your certificate issuer to create it).
Use the same host when ingress terminates TLS on one hostname for both UI and
API. Use separate hosts if you split them (for example
baseUrl: https://app.example.com, apiUrl: https://api.example.com). A SESSION_COOKIE_DOMAIN override is optional; keep the default host-only cookie unless your deployment requires a shared cookie domain. Do not use in-cluster DNS names (tokentimer-api, pod
IPs) here. See the
Canonical URLs section of the Configuration reference.
SSO callback paths default to ${apiUrl}/auth/oidc/<slug>/callback and
${apiUrl}/auth/saml/<slug>/callback (slug from your provider row). If the
API sits behind an ingress or reverse proxy, set trustProxyHops (top-level
chart value, maps to TRUST_PROXY_HOPS) so session cookies and redirects
respect X-Forwarded-* headers.
e. Install
Use Helm 3.14+ to install straight from the OCI registry; no intermediate
helm pull is needed. See Registry access
for how to list published chart versions and pick <chart-version>.
read -r -p 'Enterprise chart version: ' ENTERPRISE_VERSION
helm install tokentimer oci://harbor.tokentimer.ch/tokentimer-enterprise/charts/tokentimer-enterprise \
--version "$ENTERPRISE_VERSION" \
-n tokentimer \
-f my-values.yaml \
--set license.existingSecret=tokentimer-license \
--set tokentimer.config.adminEmail=admin@your-company.com
If you are not using a license Secret, drop license.existingSecret and set
TT_LICENSE_KEY under tokentimer.api.env (or paste the license in the UI
after install).
For air-gapped clusters, change-review workflows, or inspecting every chart
value, pull the chart locally first with
helm pull oci://harbor.tokentimer.ch/tokentimer-enterprise/charts/tokentimer-enterprise --version <chart-version>,
then install from the resulting .tgz. The full schema comes from
helm show values ./tokentimer-enterprise-<chart-version>.tgz; the bundle's
helm/values.yaml and example files alone are not enough for that. See
Registry access.
envFrom and license volume names are derived from the Helm release name
(<release>-enterprise-env, and so on). See helm/README.md in the bundle
(section: Enterprise env wiring).
f. Verify
kubectl get pods -n tokentimer -w
You should see: API and dashboard pods become Ready, migrations complete successfully, and worker CronJobs exist. Workers run as Jobs on their schedules; they are not permanent pods. Check with kubectl get jobs,cronjobs -n tokentimer.
Then open the dashboard at your ingress URL (tokentimer.config.baseUrl). If
you have no ingress yet:
kubectl port-forward -n tokentimer svc/tokentimer-dashboard 8080:80 &
Open http://localhost:8080 to inspect the UI. Use the configured HTTPS dashboard URL for your first sign-in; a dashboard-only port-forward does not configure the API origin or secure session cookies. Retrieve the administrator password from the install Secret if Helm generated it (see helm/README.md). Confirm System Settings → License and that import providers match your entitlements.
Optional API smoke check (does not validate the license; use /health only):
kubectl port-forward -n tokentimer svc/tokentimer-api 4000:4000 &
curl http://localhost:4000/health
Scenario values files
The bundle ships starter values files under helm/examples/:
| File | Use case |
|---|---|
values-minimal.yaml | Single-node CNPG, no ingress |
values-external-db.yaml | External PostgreSQL + ingress + SMTP |
values-full.yaml | Production-shaped overlay: external DB, ingress, TLS, monitoring (not every chart key; pull the chart from Harbor and run helm show values on the .tgz, or helm get values -a on a live release) |
values-full-test.yaml | Lab / Minikube with HPA, metrics, all workers |
values-sso-basic-oidc.yaml | Single OIDC provider bootstrap |
values-sso-basic-saml.yaml | Single SAML provider bootstrap |
values-sso-multi-provider.yaml | Entra OIDC + Keycloak OIDC + Keycloak SAML |
values-sso-full-reference.yaml | Every supported ssoProviders[] field (parameter catalog) |
Check the effective license in System Settings → License, then follow First asset and alert check. See configuration for secrets and deployment settings.