Skip to main content

Kubernetes Installation

info

See Obtaining the Software for how to get the symphony-<version>.tgz Helm chart.

Deploy Symphony into an existing Kubernetes cluster using the provided Helm chart. This method is suited for organisations that already operate Kubernetes infrastructure.

Prerequisites

  • Kubernetes 1.26+
  • Helm 3.10+
  • An ingress controller (handling ports 80, 443 and 4222) such as ingress-nginx or a Gateway API implementation
  • A DNS name that resolves to the ingress controller's or gateway's external IP
  • An OIDC provider (Auth0, Keycloak, Okta, or similar)—or use the bundled Keycloak option described later in this guide
  • TLS certificates (or cert-manager for automated provisioning)

Installation

1. Load the container image

The Symphony container image is distributed inside the Docker Compose archive (cirata-symphony-<version>-docker.tar.gz) under images/. Extract the image and load it into your container registry or directly into cluster nodes:

# Extract the image from the Docker archive
tar xzf cirata-symphony-*-docker.tar.gz --wildcards '*/images/*.tar'

# Option A: Push to a container registry
docker load -i symphony-*-docker/images/cirata-*.tar
docker tag cirata:latest registry.example.com/cirata:latest
docker push registry.example.com/cirata:latest

# Option B: Load directly into cluster nodes (for testing)
docker load -i symphony-*-docker/images/cirata-*.tar

2. Install the Helm chart

With an external OIDC provider, you can supply OIDC values in the Helm command to pre-populate the setup wizard, or omit them and enter them in the wizard instead:

# Minimal — OIDC configured in the setup wizard
helm install symphony symphony-*.tgz \
--set hostname=symphony.example.com

# With OIDC pre-populated (for automated deployments)
helm install symphony symphony-*.tgz \
--set hostname=symphony.example.com \
--set oidc.issuer=auth.example.com \
--set oidc.clientid=symphony-api \
--set oidc.uiclientid=symphony-ui

Or with the bundled Keycloak (no external OIDC provider required):

helm install symphony symphony-*.tgz \
--set hostname=symphony.example.com \
--set keycloak.enabled=true \
--set keycloak.adminPassword=changeme

If you pushed the image to a registry, also set:

  --set image.repository=registry.example.com/cirata \
--set image.tag=latest

3. Verify the deployment

kubectl get pods
kubectl get svc
kubectl get ingress

4. Complete setup

Open https://<hostname> in your browser and follow the administration setup wizard. If you did not supply OIDC values in the Helm command, the wizard provides fields for the issuer URL and client IDs along with a Validate Configuration button that checks your DNS and OIDC settings before you submit. Each check includes a description of any issue and instructions for resolving it.

tip

For help registering clients with your identity provider, see OIDC Configuration. For a detailed explanation of each validation check, see Setup Wizard Validation.

5. Create users in Keycloak

If you enabled bundled Keycloak, you need to create user accounts before anyone can sign in to Symphony.

  1. Open the Keycloak admin console at https://<hostname>/auth
  2. Sign in with username admin and the keycloak.adminPassword you set during install
  3. Select the symphony realm from the dropdown in the top-left corner
  4. Navigate to Users and click Create new user
  5. Fill in the username and email, then click Create
  6. Go to the Credentials tab, click Set password, enter a password, and toggle Temporary off if you do not want the user to be forced to change it on first login
  7. Click Save

Users can now sign in to Symphony at https://<hostname> using these credentials.

Bundled Keycloak

The Helm chart includes an optional bundled Keycloak instance that provides a turnkey OIDC setup. When enabled, Keycloak is deployed alongside Symphony with a pre-configured realm containing:

  • symphony-api—a confidential client for backend token validation (no login flow enabled)
  • symphony-ui—a public client for the browser-based SPA
  • Rolessymphony-admin and viewer realm roles
  • Groups mapper—maps realm roles into the groups token claim

Symphony's OIDC settings are automatically configured to use the bundled Keycloak instance. No manual OIDC configuration is needed.

Keycloak values

ValueDefaultDescription
keycloak.enabledfalseEnable the bundled Keycloak instance
keycloak.imagequay.io/keycloak/keycloak:26.1Keycloak container image
keycloak.adminUseradminKeycloak admin username
keycloak.adminPassword""Keycloak admin password (required when enabled)
keycloak.persistence.size1GiStorage size for Keycloak data
keycloak.persistence.keepOnUninstallfalseWhen true, annotates the Keycloak PVC with helm.sh/resource-policy: keep so helm uninstall does not delete it

Helm Values

The following values can be configured:

ValueDefaultDescription
hostnamesymphony.example.comPublic DNS hostname
listen_address0.0.0.0Listen address
ui_port8080Internal HTTP port
websocket_port9222Internal WebSocket port
nats_port4222Internal NATS port
oidc.issuer""OIDC issuer URL (optional—can be entered in the setup wizard)
oidc.clientid""OIDC client ID for API (optional—can be entered in the setup wizard)
oidc.uiclientid""OIDC client ID for UI (optional—can be entered in the setup wizard)
oidc.clientsecret""Client secret for the API client (optional, for confidential clients)
oidc.uiclientsecret""Client secret for the UI client (optional, for confidential UI clients)
oidc.caCert""Path (inside pod) to CA cert PEM for OIDC TLS—use with tls.caSecretName to mount it. Validated during setup when set.
oidc.tlsSkipVerifyfalseSkip TLS verification for OIDC—can also be set via the setup wizard. Use oidc.caCert for production.
oidc.internalBaseURL""Internal URL for OIDC provider
image.repositorycirataContainer image repository
image.taglatestContainer image tag
image.pullPolicyIfNotPresentImage pull policy
ingress.enabledtrueEnable ingress resource
ingress.classNamenginxIngress class name
tls.enabledfalseEnable TLS on ingress
tls.secretName""TLS secret name
tls.caSecretName""TLS CA secret name
persistence.storageClass""Storage class (empty for cluster default)
persistence.size1GiStorage size for Symphony data
persistence.accessModeReadWriteOncePVC access mode
persistence.keepOnUninstallfalseWhen true, annotates the PVC with helm.sh/resource-policy: keep so helm uninstall does not delete it. See Data persistence and backup below.
nats.tcpPassthroughfalseCreate TCP ConfigMap for NATS ingress passthrough
nats.tls.enabledfalseEnable native TLS on the NATS listener (port 4222)
nats.tls.secretName""Kubernetes TLS secret containing tls.crt and tls.key
nats.tls.caSecretName""Kubernetes secret containing ca.crt (optional, for client verification)

Override values using --set flags or a custom values file:

helm install symphony symphony-*.tgz -f my-values.yaml

Data Persistence and Backup

Symphony stores all durable state—operator and account signing seeds, account JWTs, users, roles, API keys, licensing records, and every user's private data—in the /config directory that the chart mounts from a PersistentVolumeClaim. This directory must be protected. The identity seeds inside it are the trust root for every JWT and API token Symphony has ever issued; losing them invalidates every extension credential and every user token in circulation.

PersistentVolume reclaim policy

Most Kubernetes dynamic provisioners create StorageClasses with a reclaim policy of Delete. That means a plain helm uninstall releases the PVC, which causes the cluster to permanently delete the underlying volume and all Symphony state. This default is dangerous for any deployment that holds real data.

Three complementary controls are available:

  1. Take regular external backups. This is the only defence against cluster loss, storage corruption, or accidental deletion. See Backup and Recovery in the operations guide for procedures. Reclaim-policy and annotation tricks are not a substitute for backups.

  2. Preserve the PVC across helm uninstall. Set persistence.keepOnUninstall=true (and keycloak.persistence.keepOnUninstall=true when Keycloak is bundled). The chart stamps helm.sh/resource-policy: keep on the PVC so Helm will not delete it on uninstall. Existing contents remain intact; to clean up later, kubectl delete pvc explicitly.

  3. Change the PV reclaim policy to Retain. Even with the PVC preserved, a subsequent kubectl delete pvc will still trigger the StorageClass reclaim policy. For long-lived production deployments, patch the PV after install:

    PVC=my-release-pvc
    NS=Symphony
    PV=$(kubectl -n "$NS" get pvc "$PVC" -o jsonpath='{.spec.volumeName}')
    kubectl patch pv "$PV" -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'

    A Retained PV requires manual cleanup when the data is genuinely no longer needed.

The chart defaults persistence.keepOnUninstall to false to preserve existing upgrade behaviour. Set it to true for any deployment that holds real data.

Backup approaches

The operations guide covers three procedures in detail. In brief:

  • cirata symphony backup (recommended)—a single command against the running instance produces a consistent archive of /config. Requires the symphony-admin role. See Backup and Recovery.
  • CSI volume snapshots—if your StorageClass supports them (kubectl get volumesnapshotclass), these capture a crash-consistent block-level image without any server-side coordination. Schedule with a CronJob or operator such as Velero.
  • Offline tar—scale the deployment to zero, tar /config from a helper pod mounted at the PVC, scale back up. Used for DR rehearsals and migrations between clusters.

Upstream references: Persistent Volumes, Change reclaim policy, Volume snapshots.

TLS Configuration

TLS is strongly recommended for production deployments. Enable it with tls.enabled=true and provide a certificate via one of the methods below.

Using cert-manager

If your cluster has cert-manager installed, configure the ingress to request a certificate automatically:

helm install symphony symphony-*.tgz \
--set hostname=symphony.example.com \
--set tls.enabled=true \
--set ingress.annotations."cert-manager\.io/cluster-issuer"=letsencrypt-prod

cert-manager will create a TLS secret automatically and the ingress will use it.

Manual TLS

Create a TLS secret and reference it during installation:

kubectl create secret tls symphony-tls \
--cert=fullchain.pem \
--key=privkey.pem

helm install symphony symphony-*.tgz \
--set hostname=symphony.example.com \
--set tls.enabled=true \
--set tls.secretName=symphony-tls

Optionally create and pass in a CA secret as well:

kubectl create secret generic symphony-tls-ca \
--from-file=ca.crt

helm install symphony symphony-*.tgz \
--set hostname=symphony.example.com \
--set tls.enabled=true \
--set tls.secretName=symphony-tls \
--set tls.caSecretName=symphony-tls-ca

NATS TLS

To encrypt native NATS connections (port 4222) used by extensions and CLI tools, enable NATS TLS separately from the ingress TLS above:

kubectl create secret tls symphony-nats-tls \
--cert=nats-cert.pem \
--key=nats-key.pem

helm install symphony symphony-*.tgz \
--set hostname=symphony.example.com \
--set tls.enabled=true \
--set tls.secretName=symphony-tls \
--set nats.tls.enabled=true \
--set nats.tls.secretName=symphony-nats-tls

When NATS TLS is enabled, the NATS server requires TLS on all connections. Client SDKs automatically attempt TLS using system CAs—no client-side configuration is needed when the certificate is signed by a trusted CA.

For certificates signed by a private CA, extensions can set the SYMPHONY_TLS_CA environment variable to the path of the CA certificate — this trust anchor applies to both the HTTPS registration exchange and NATS. NATS_TLS_CA remains supported as a NATS-only override.

Using the CLI

The cirata CLI tool lets you manage Symphony from the terminal. CLI binaries for Linux, macOS, and Windows are included in the installation archive under the cli/ directory. Copy the binary for your platform to a directory in your PATH:

# Example: install the macOS binary
chmod +x cli/cirata-darwin-arm64
sudo mv cli/cirata-darwin-arm64 /usr/local/bin/cirata

See the Command-Line Tool page for full setup and usage instructions.

Architecture

The Helm chart deploys the following resources:

  • Deployment—a single Symphony pod running the cirata binary with the symphony operate command
  • Service—exposes HTTP (80), WebSocket (81), and NATS (4222) ports
  • Ingress—two ingress resources: one for HTTP traffic (/ and optionally /auth), and one for WebSocket connections (/ws) with appropriate proxy settings
  • ConfigMap—stores environment configuration (hostname, ports, OIDC)
  • PersistentVolumeClaim—provides storage for configuration and runtime data

When bundled Keycloak is enabled, the chart additionally deploys:

  • Keycloak Deployment—Keycloak 26.1 with auto-imported realm configuration
  • Keycloak Service—exposes Keycloak on port 8180
  • Keycloak ConfigMap—contains the Symphony realm JSON for auto-import
  • Keycloak PVC—persistent storage for Keycloak data
  • Ingress /auth path—routes /auth traffic to Keycloak

Health and Readiness Probes

Symphony provides dedicated endpoints for Kubernetes probes:

EndpointPurposeHealthyDegraded
GET /api/v1/readyReadiness probe200 with {"ready": true}503 with {"ready": false}
GET /api/v1/healthLiveness / detailed health200 with component status and version503 with degraded components

The readiness probe checks that the NATS server is running and able to accept connections. The health probe additionally reports the status of individual components (NATS server, messaging connection) and includes build version information.

The Helm chart includes these probes by default. If you are customising the deployment, use the following configuration:

livenessProbe:
httpGet:
path: /api/v1/health
port: 8080
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /api/v1/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10

Managing the Installation

# View pod logs
kubectl logs -f deployment/symphony

# Upgrade
helm upgrade symphony symphony-NEW_VERSION.tgz

# Uninstall
helm uninstall symphony

# Check configuration
kubectl get configmap symphony-configmap -o yaml

Troubleshooting

  • Pod not starting—check kubectl describe pod and kubectl logs for error messages. Verify the container image is available.
  • Ingress not working—confirm your ingress controller is running and that DNS resolves to the correct IP. Check ingress controller logs.
  • 503 errors—if both / and /auth return 503, verify the service endpoints exist (kubectl get endpoints). Also ensure the ingress controller does not have conflicting rewrite-target annotations.
  • WebSocket not connecting—the chart creates a separate ingress resource for /ws with WebSocket-appropriate proxy settings. Verify both ingress resources exist with kubectl get ingress.
  • Storage issues—verify the PersistentVolumeClaim is bound (kubectl get pvc). If a PVC is stuck in a Terminating state from a previous install, you may need to remove its finalizers and force-delete it before reinstalling.
  • NATS connectivity for extensions—external extensions connect to NATS via the service on port 4222. If extensions run outside the cluster, enable nats.tcpPassthrough and configure your ingress-nginx controller with --tcp-services-configmap to expose port 4222.
  • Keycloak not starting—verify keycloak.adminPassword is set. Check Keycloak pod logs with kubectl logs -f deployment/symphony-keycloak. The realm import runs on first startup and may take a minute.
  • Users cannot sign in (bundled Keycloak)—ensure you have created user accounts in Keycloak under the symphony realm. See Create users in Keycloak above.
  • Empty credentials from /api/v1/usertoken—this usually means the Symphony pod cannot reach its own NATS server. The chart uses hostAliases to route the external hostname to 127.0.0.1 within the pod. Verify with kubectl get pod <pod> -o jsonpath='{.spec.hostAliases}'.