Helm

Helm

A first-party chart lives at deploy/helm/tabularium in the repo. It renders a Deployment, Service, Ingress, optional PVC, an optional chart-managed Secret, ServiceAccount, and an optional HPA — with non-root, read-only-rootfs security contexts baked in.

The chart isn't published to a Helm repo yet, so install from a checkout:

git clone https://github.com/TabularisDB/tabularium
cd tabularium
bash

Quick start

Generate the two required secrets, then install into a namespace. The chart's values.yaml still points image.repository at the old Codeberg registry — override it to the published GHCR image:

helm install registry ./deploy/helm/tabularium \
  --namespace tabularium --create-namespace \
  --set image.repository=ghcr.io/tabularisdb/tabularium \
  --set config.baseUrl=https://registry.example.com \
  --set config.webBaseUrl=https://registry.example.com \
  --set ingress.hosts[0].host=registry.example.com \
  --set secrets.jwtSecret=$(openssl rand -hex 32) \
  --set secrets.tokenEncKey=$(openssl rand -hex 32)
bash

First boot writes the bootstrap admin password to the logs:

kubectl -n tabularium logs -f deploy/registry-tabularium
bash

Sign in with admin@example.com + that password and the install wizard walks you through the rest. No ingress? Port-forward instead:

kubectl -n tabularium port-forward svc/registry-tabularium 3000:80
bash

Secrets

JWT_SECRET and TOKEN_ENC_KEY are mandatory in production — without them the app generates ephemeral keys at boot, invalidating every session and stored OAuth token on each restart.

Two ways to supply them:

  • Chart-managed (dev-friendly): --set secrets.jwtSecret=… --set secrets.tokenEncKey=…. The chart renders a Secret named <release>-tabularium.
  • Existing secret (production): manage the Secret out-of-band (sealed-secrets, sops, External Secrets) and reference it wholesale with --set secrets.existingSecret=my-secret. Its keys are loaded via envFrom, so it can also carry OAuth client secrets.

TOKEN_ENC_KEY must be exactly 64 hex chars (AES-256-GCM). JWT_SECRET must be ≥32 chars and not the placeholder change-me-in-production.

Database mode

config.databaseMode picks the storage backend:

ValueBehaviour
sqlite (default)SQLite on the PVC at config.dataDir (/data). Pins replicaCount to 1 — no shared-filesystem semantics — and rules out the HPA.
externalSet config.databaseUrl to a postgres://… or mysql://… DSN. Run migrations after the first rollout: kubectl exec deploy/registry-tabularium -- bun run migrate.

For a self-contained Postgres, flip on the bundled Bitnami subchart instead of running your own:

--set config.databaseMode=external \
--set postgresql.enabled=true \
--set config.databaseUrl=postgres://tabularium@registry-tabularium-postgresql:5432/tabularium
bash

There's a matching dragonfly.enabled subchart (Redis-compatible cache); pair it with --set config.cacheDriver=redis and point REDIS_URL at the service.

Persistence

A PVC mounts at config.dataDir (/data) and holds the SQLite DB (in sqlite mode) plus uploaded branding/plugin assets when the storage driver is disk.

KeyDefaultNotes
persistence.enabledtrueDisable only if you run both an external DB and S3-compatible storage — otherwise pod restarts lose uploads.
persistence.size5Gi
persistence.storageClass""Empty = cluster default.
persistence.existingClaim""Bind an existing PVC and skip chart creation.

Ingress

k3s ships Traefik — leave ingress.className empty and it's picked up automatically. TLS via cert-manager is a commented recipe in values.yaml:

--set ingress.annotations."cert-manager\.io/cluster-issuer"=letsencrypt \
--set ingress.tls[0].hosts[0]=registry.example.com \
--set ingress.tls[0].secretName=registry-tls
bash

The container serves the SPA, API (/api), OAuth callbacks (/auth), OpenAPI (/openapi) and uploads (/uploads) all under / — no path-based routing.

Scaling

The HPA (autoscaling.enabled) is off by default. Only enable it in external database mode — SQLite mode can't scale horizontally:

--set config.databaseMode=external \
--set autoscaling.enabled=true \
--set autoscaling.maxReplicas=5
bash

Common values

KeyDefaultNotes
image.repositorycodeberg.org/tabularium/tabulariumOverride to ghcr.io/tabularisdb/tabularium.
image.tag""Falls back to the chart's appVersion.
replicaCount1Ignored (forced to 1) in sqlite mode.
config.baseUrl / config.webBaseUrlhttps://registry.example.comPublic origins for OAuth callbacks + webhook URLs.
config.logLevelinfo
resources50m/128Mi → 500m/512Mirequests → limits.
service.type / service.portClusterIP / 80targetPort defaults to config.port (3000).
existingEnvSecret""Extra Secret loaded via envFrom (OAuth client secrets, etc.).

Full reference: deploy/helm/tabularium/values.yaml.

Upgrade & uninstall

# reuse previously-set values, bump the image tag
helm upgrade registry ./deploy/helm/tabularium --reuse-values --set image.tag=0.13.0

helm uninstall registry -n tabularium
bash

helm uninstall leaves PVCs behind by design — delete registry-tabularium-data manually if you really want the data gone.