Skip to content
Operations · Self-host Advanced

Self-hosted on Kubernetes / OpenShift

Deploy the Sluicio cell with the Helm chart — install from the OCI registry or a checkout, pick a values variant, activate Enterprise with a license Secret, expose it via Ingress or Routes, and harden with NetworkPolicies and PodDisruptionBudgets.

SLSluicio team 14 min read Updated Jul 2026

The single-host bootstrap is the right shape for most installs. If your organization standardizes on Kubernetes or OpenShift, the cell Helm chart (deploy/helm/cell in the app repo, Apache-2.0 licensed) is the supported path instead — this guide walks an Enterprise install end to end.

One chart serves both editions. Enterprise is activated by the license key at runtime; the same chart without a license runs the Community Edition, which is a fully valid deployment — nothing below except the license step is Enterprise-specific.

The chart is linted, templated and kubeconform-validated in CI across all three values variants, and smoke-tested end to end in a kind cluster.

For releases after v0.11.40, install straight from the OCI registry — the chart version matches the release without the v, and its appVersion pins the matching images:

Terminal window
helm install cell oci://ghcr.io/sluicio/charts/sluicio-cell --version <X.Y.Z>

Up to and including v0.11.40, install from a repo checkout instead:

Terminal window
git clone https://github.com/SLUICIO/sluicio-app.git
cd sluicio-app
helm install cell ./deploy/helm/cell -f deploy/helm/cell/values-external-db.yaml

Images are public on ghcr.io/sluicio, so no pull secrets are needed unless you mirror them into a private registry (then set global.imagePullSecrets).

The chart ships three starting points — copy one and edit it rather than assembling values from scratch:

VariantWhat it doesUse it for
values-bundled.yamlRuns single-replica Postgres + ClickHouse in-cluster with PVCs (non-HA)Evaluation and small installs
values-external-db.yamlBring your own databases — Postgres DSN (postgres.dsn) and ClickHouse endpoint/credentials (clickhouse.*)Production (recommended)
values-openshift.yamlPasses the restricted-v2 SCC clean — no fixed UIDs or fsGroup — and pairs with RoutesOpenShift

cell-api applies its Postgres migrations and creates the ClickHouse tables on startup, so external databases only need to exist and be reachable with the credentials you provide.

Create a Secret holding the license key, then reference it from the chart. The key inside the Secret defaults to license:

Terminal window
kubectl create secret generic sluicio-license --from-literal=license=<key>
helm upgrade cell oci://ghcr.io/sluicio/charts/sluicio-cell --version <X.Y.Z> \
--reuse-values --set license.existingSecret=sluicio-license

Prefer license.existingSecret over the inline license.key value — with a referenced Secret the key never lands in values files, Helm release history, or CI logs. Unlicensed, the install keeps running as Community Edition; the license flips on Enterprise features at runtime, no reinstall needed.

There is no edition flag to set — Community Edition is simply the absence of a license. Leave both license.existingSecret and license.key empty and the chart doesn’t render the license variable at all. The same applies to the MFA key, which is independent of licensing:

Terminal window
helm upgrade cell oci://ghcr.io/sluicio/charts/sluicio-cell --version <X.Y.Z> \
--reuse-values \
--set license.existingSecret= --set license.key= \
--set mfa.existingSecret= --set mfa.key=

Everything works except SSO, the audit log, notification profiles, retention beyond 14 days, the require-MFA policy and advanced RBAC. Adding a license later is a helm upgrade, never a reinstall — your databases are untouched.

Two things every install should set beyond the database connection:

  • MFA at-rest key — encrypts enrolled MFA secrets. Generate one with openssl rand -base64 32 and provide it via mfa.existingSecret (or mfa.key for a quick eval).
  • SMTP — invitations, alert emails and password resets need an outbound mail relay: smtp.host, smtp.from, with credentials via the secret-backed smtp.existingSecret.

The UI and API must share one hostname (session cookies are scoped to it) — the standard Ingress template serves / and /api from one host, with a separate host for the OTLP ingest endpoint so producers stay pointed at ingest even if the UI moves:

ingress:
enabled: true
className: nginx
host: sluicio.acme.com
ingest:
enabled: true
host: ingest.acme.com
app:
appUrl: https://sluicio.acme.com
ingestUrl: https://ingest.acme.com

TLS follows your cluster’s usual pattern (ingress.tls / ingress.ingest.tls).

On OpenShift, set route.enabled to get Routes instead of Ingress resources — the intended pairing with values-openshift.yaml.

cell-api runs single-replica by design — it hosts in-process schedulers, and database migrations run at its startup. The chart pins cellApi.replicaCount to 1 with a Recreate deployment strategy; don’t scale it. A consequence worth knowing: upgrades briefly take the API down while the pod is replaced. The frontend and cell-ingest are separate deployments, so telemetry ingest keeps flowing through an API upgrade — and both can scale horizontally when load calls for it.

Two opt-in switches, both off by default:

  • networkPolicy.enabled — default-deny ingress around the bundled data stores plus scoped ingress rules for the app pods. Only effective on a CNI that enforces NetworkPolicies (Calico, Cilium, OVN-Kubernetes…); on one that doesn’t, the objects are created but ignored.
  • podDisruptionBudget.enabled — PodDisruptionBudgets for the frontend and ingest, meaningful once you scale them beyond 1 replica so node drains can’t take all copies down at once.

”Couldn’t reach the cell-api” / 502 Bad Gateway

Section titled “”Couldn’t reach the cell-api” / 502 Bad Gateway”

The frontend proxies /api to the cell-api Service, so a 502 means nothing healthy is behind that Service — almost always cell-api not being Ready rather than a proxy misconfiguration. cell-api only reports Ready once it has connected to both Postgres and ClickHouse:

Terminal window
kubectl get pods -l app.kubernetes.io/instance=<release>
kubectl logs deploy/<release>-cell-api

cell-api stuck at CreateContainerConfigError

Section titled “cell-api stuck at CreateContainerConfigError”

The kubelet couldn’t assemble the container’s configuration. In practice this means a referenced Secret is missing, or the key inside it isn’t the one the chart asks for. The pod events name the exact cause:

Terminal window
kubectl describe pod -l app.kubernetes.io/component=cell-api
Event messageCauseFix
secret "X" not foundThe Secret isn’t in the namespace you installed intoCreate it there, or correct *.existingSecret
couldn't find key Y in Secret XThe Secret exists but uses a different key nameRecreate it, or set license.secretKey / mfa.secretKey / smtp.secretKey

The expected key names are license, mfa-key and password. Creating a Secret in one OpenShift project and installing the chart into another is the most common way to hit the first row.

A useful signal: cell-ingest has no Secret references, so ingest running while cell-api doesn’t points at this problem rather than at images, SCC or scheduling. The fastest way back to a working install is to drop the references — see Running the Community Edition.


See also: the capacity planner for sizing the databases, Deploy to production for the single-host path and database background, and the security model for ingest keys, tokens and TLS. The chart’s values.yaml in the app repo documents the full set of options.