Kubernetes
A Helm chart lives in charts/orbit-object-console
in the repository. It follows the Bitnami chart conventions — the same values
layout, resource presets, probe overrides, security defaults and extraDeploy
escape hatches — but keeps its template helpers local rather than depending on
the bitnami/common chart, so it lints, templates and installs with no chart
dependencies to fetch.
Prerequisites
- Kubernetes 1.23+
- Helm 3.8+
- PV provisioner support in the underlying infrastructure (when using SQLite)
- An OIDC provider (Keycloak, Authentik, Auth0, Google, Azure AD, ...)
Install
The console has no local passwords: sign-in is always delegated to an OIDC
provider, so oidc.issuerUrl, oidc.clientId and a client secret are always
required. The redirect URI registered with your provider must be
<external-url>/auth/callback; the chart derives it from the ingress hostname
when ingress.enabled=true, otherwise set oidc.redirectUri explicitly.
helm install orbit ./charts/orbit-object-console \
--namespace orbit --create-namespace \
--set oidc.issuerUrl=https://id.example.com/realms/main \
--set oidc.clientId=orbit-console \
--set oidc.clientSecret=<client-secret> \
--set ingress.enabled=true \
--set ingress.hostname=orbit.example.com
The chart refuses to render with a clear message when required values are
missing, when persistence is disabled while SQLite is in use, or when
replicaCount > 1 without an external database.
The chart (and its container image) were renamed from s3-object-client to
orbit-object-console. Renaming the chart renames the Kubernetes resources it
creates (Deployment, Service, Secret, PVC, ServiceAccount, ...), so helm upgrade from an old s3-object-client release will not adopt the existing
resources — it will either fail or create a conflicting, differently-named
release. An existing release must be installed fresh under the new chart
name rather than upgraded in place.
This also means the chart's auto-generated ENCRYPTION_KEY is not carried
over: a fresh install generates a new one unless you explicitly set
encryption.key or encryption.existingSecret to the previous value. See
Security for what happens if the key
changes.
Values overview
By default the console stores its data in SQLite on a PersistentVolumeClaim —
for more than one replica, point it at PostgreSQL with
externalDatabase.enabled=true.
| Mode | Values | Notes |
|---|---|---|
| SQLite (default) | persistence.enabled=true | Single replica only; the database file lives on the PVC at persistence.mountPath |
| PostgreSQL | externalDatabase.enabled=true | Required for replicaCount > 1; supply externalDatabase.host/user/password/database, or a full externalDatabase.url |
Key application settings:
| Name | Description | Default |
|---|---|---|
oidc.issuerUrl / oidc.clientId / oidc.clientSecret | OIDC provider (required) | "" |
oidc.redirectUri | Redirect URI; derived from the ingress hostname when empty | "" |
encryption.key / encryption.existingSecret | Encryption key for stored profile secrets; generated when empty | "" |
sessionTtl | Session cookie sliding expiry, in seconds | 28800 |
awsKeyless.enabled | Allow connection profiles that authenticate with the pod's AWS identity | false |
replicaCount | Number of replicas (must be 1 with SQLite) | 1 |
ingress.enabled / ingress.hostname | Ingress record generation | false / orbit.local |
The chart's full parameter tables (global, image, deployment, persistence, traffic exposure, autoscaling, and more) live in the chart README.
Ingress
ingress:
enabled: true
hostname: orbit.example.com
ingressClassName: nginx
tls: true
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
oidc:
issuerUrl: https://id.example.com/realms/main
clientId: orbit-console
existingSecret: orbit-oidc
PostgreSQL with multiple replicas
replicaCount: 3
externalDatabase:
enabled: true
host: postgresql.data.svc.cluster.local
user: orbit
database: orbit
existingSecret: orbit-postgresql
persistence:
enabled: false
pdb:
create: true
minAvailable: 2
Keyless AWS IAM
A connection profile can authenticate as the server's own AWS identity instead
of a stored access key/secret pair — no credentials to encrypt, rotate, or
leak. This only applies to AWS S3 profiles; other S3-compatible backends still
need a static access key/secret. It's controlled by awsKeyless.enabled, which
sets AWS_KEYLESS_ENABLED=true on the container (see
Configuration) and is off by default, because every
signed-in user of the deployment who creates a keyless profile shares that
same server identity — there's no per-user isolation.
Keyless profiles can optionally set a roleArn, in which case the server
assumes that role via STS (AssumeRole) before talking to S3, rather than
using its own identity directly.
The chart and application code support both delivery mechanisms below, but
real EKS Pod Identity / IRSA / STS AssumeRole credential delivery is not
exercised by the automated test suite — it requires a real EKS cluster and AWS
IAM setup. Verify the identity resolves as expected in a staging environment
before relying on it in production.
EKS Pod Identity
With Pod Identity, the role association is made out of band (via the EKS Pod
Identity Agent / aws eks create-pod-identity-association) against the pod's
service account — no chart-side annotation needed:
awsKeyless:
enabled: true
IRSA
With IRSA (IAM Roles for Service Accounts), annotate the service account with the role to assume:
awsKeyless:
enabled: true
serviceAccount:
create: true
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/orbit-object-console
Either way, grant the underlying IAM role only the S3 permissions your users need — it is shared by everyone who creates a keyless profile on this deployment.
Uninstalling
helm uninstall orbit
The PersistentVolumeClaim is not deleted automatically — remove it manually if you no longer need the data:
kubectl delete pvc orbit-orbit-object-console-data