Skip to main content

Kubernetes

Use the official private Helm chart when you want a persistent self-hosted Synth instance. For a complete first install, use Helm + CaSc. This page explains the deployment shape and what to verify.

For guided single-pod installs, use the standalone-pvc shape: one app pod, one PVC mounted at /var/lib/synth, and Secrets for any provider keys, registry auth, and the Synth license key.

standalone-pvc is persistent but not HA. Do not raise replicaCount above 1; the chart renders with a Recreate strategy to protect the single-writer SQLite/filesystem state.

Install Shape

Private registry: official Synth image and Helm chart
Kubernetes Secrets: optional provider keys, license key, image pull key
Helm values: runtime settings plus synth.casc.raw
Helm chart: renders CaSc and imports it before app startup
PVC: stores Synth data under /var/lib/synth

Values To Set First

For any public install, set the official image, public URL, host allowlist, ingress, persistence, license Secret, and runtime Secret. Add provider defaults when model-backed jobs are part of the deployment:

image:
repository: us-central1-docker.pkg.dev/project-96337d0a-4d88-427b-af7/synth/synth
tag: latest
digest: sha256:...

imagePullSecrets:
- name: synth-registry-pull

synth:
publicUrl: https://synth.example.com
existingLicenseSecret: synth-license
defaultProvider: opencode
defaultModel: gpt-5.4-mini
security:
allowedHosts:
- synth.example.com
corsOrigins:
- https://synth.example.com
providers:
opencode:
enabled: true
baseUrl: https://opencode.ai/zen/v1
defaultModel: gpt-5.4-mini

secrets:
existingSecret: synth-runtime-secrets

persistence:
enabled: true

Canonical Project retrieval does not require a provider. When enabling an explicit model-assisted job, add provider keys through Kubernetes Secrets or your secret manager. Do not commit real keys to values.yaml.

Amazon Bedrock Provider

Bedrock is configured under synth.providers.bedrock. Always set enabled: true, regionName, and a model id that is available in that AWS region.

synth:
defaultProvider: bedrock
defaultModel: amazon.nova-micro-v1:0
providers:
bedrock:
enabled: true
regionName: us-east-1
defaultModel: amazon.nova-micro-v1:0

Pick one auth mode:

  1. EKS IRSA or the AWS default credential chain. Annotate the ServiceAccount and set serviceAccount.automountServiceAccountToken: true.
serviceAccount:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/synth-bedrock
automountServiceAccountToken: true
  1. Existing Secret with AWS access keys. The Secret should contain AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY; add AWS_SESSION_TOKEN for temporary credentials.
kubectl -n "$SYNTH_NAMESPACE" create secret generic synth-bedrock-aws \
--from-literal=AWS_ACCESS_KEY_ID="$AWS_ACCESS_KEY_ID" \
--from-literal=AWS_SECRET_ACCESS_KEY="$AWS_SECRET_ACCESS_KEY" \
--dry-run=client -o yaml | kubectl apply -f -
synth:
providers:
bedrock:
enabled: true
regionName: us-east-1
existingAccessKeySecret: synth-bedrock-aws
  1. Existing Secret with a Bedrock bearer token.
kubectl -n "$SYNTH_NAMESPACE" create secret generic synth-bedrock-bearer \
--from-literal=BEDROCK_BEARER_TOKEN="$BEDROCK_BEARER_TOKEN" \
--dry-run=client -o yaml | kubectl apply -f -
synth:
providers:
bedrock:
enabled: true
regionName: us-east-1
existingBearerTokenSecret: synth-bedrock-bearer

Before installing, render the chart and confirm Bedrock secrets are not in the CaSc ConfigMap:

helm template synth "$SYNTH_HELM_CHART" --version "$SYNTH_HELM_CHART_VERSION" \
-f values.k8s.yaml >/tmp/synth-rendered.yaml

grep -n "bedrock:" -A8 /tmp/synth-rendered.yaml
grep -n "BEDROCK_REGION\\|AWS_ACCESS_KEY_ID\\|BEDROCK_BEARER_TOKEN" /tmp/synth-rendered.yaml

Okta SSO With Bedrock

Okta and Bedrock are independent: Okta authenticates people, while Bedrock is used only by configured model-assisted ingestion jobs. Canonical Project search and exact-page reads remain provider-free.

For an EKS deployment using IRSA and Okta, create only the Okta secret and keep both the OIDC client secret and AWS credentials out of Git:

kubectl -n "$SYNTH_NAMESPACE" create secret generic synth-okta \
--from-literal=client-secret="$OKTA_CLIENT_SECRET" \
--dry-run=client -o yaml | kubectl apply -f -
synth:
publicUrl: https://synth.customer.example
existingLicenseSecret: synth-license
defaultProvider: bedrock
defaultModel: us.anthropic.claude-haiku-4-5-20251001-v1:0
providers:
bedrock:
enabled: true
regionName: us-east-1
defaultModel: us.anthropic.claude-haiku-4-5-20251001-v1:0
sso:
enabled: true
issuerUrl: https://customer.okta.com/oauth2/default
clientId: 0oa-customer-synth
existingSecret: synth-okta
existingSecretKey: client-secret
redirectUri: https://synth.customer.example/api/auth/oidc/callback
scopes: [openid, profile, email, groups]
autoProvision: true
defaultRole: member
adminClaim: groups
adminGroupValue: synth-admins

serviceAccount:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/synth-bedrock
automountServiceAccountToken: true

Before rollout, replace every example hostname, issuer, client ID, role ARN, region, model route, and group name. The Okta redirect URI must match exactly; the Bedrock model route must be enabled in the configured AWS region.

Install

Use the private OCI chart supplied with your license:

export SYNTH_NAMESPACE=synth

kubectl create namespace "$SYNTH_NAMESPACE" --dry-run=client -o yaml | kubectl apply -f -

cat secrets/gcp-artifact-registry-pull-key.json | helm registry login us-central1-docker.pkg.dev \
--username _json_key \
--password-stdin

kubectl -n "$SYNTH_NAMESPACE" create secret docker-registry synth-registry-pull \
--docker-server=us-central1-docker.pkg.dev \
--docker-username=_json_key \
--docker-password="$(cat secrets/gcp-artifact-registry-pull-key.json)" \
--dry-run=client -o yaml | kubectl apply -f -

kubectl -n "$SYNTH_NAMESPACE" create secret generic synth-runtime-secrets \
--from-literal=OPENCODE_API_KEY="$OPENCODE_API_KEY" \
--dry-run=client -o yaml | kubectl apply -f -

kubectl -n "$SYNTH_NAMESPACE" create secret generic synth-license \
--from-file=license-key=/secure/path/synth-license-key.txt \
--dry-run=client -o yaml | kubectl apply -f -

helm upgrade --install synth \
oci://us-central1-docker.pkg.dev/project-96337d0a-4d88-427b-af7/synth/charts/synth \
--namespace "$SYNTH_NAMESPACE" \
-f values.k8s.yaml \
--wait \
--timeout 10m

Project And Watcher Values

For a Project with a remote Git backend and a watcher, use the explicit synth.casc.raw form. Helm mounts the CaSc document, Synth imports it on startup, and the Project config is written under /var/lib/synth/projects/<project>/.synth/config.yaml.

The chart also has a small synth.projects shorthand for simple generated CaSc, but use synth.casc.raw when you need a complete copy-pasteable project, backend, credential, and watcher configuration.

synth:
casc:
raw: |
global_settings:
default_provider: opencode
default_model: gpt-5.4-mini
providers:
opencode:
enabled: true
credential_id: helm-opencode
base_url: https://opencode.ai/zen/v1
default_model: gpt-5.4-mini

credentials:
helm-opencode:
type: opencode_api_key
name: Helm OpenCode API key
data:
api_key:
env: OPENCODE_API_KEY
github-read-token:
type: github_pat
name: GitHub read token
data:
token:
env: GITHUB_PAT

projects:
docs:
backend:
type: git
config:
remote_url: https://github.com/your-org/synth-knowledge.git
branch: main
auto_push: true
auto_pull: true
sync_on_create: true
project_subdir: projects/docs
credential_id: github-read-token
schema:
categories:
- name: Guides
directory: guides
description: How-to guides and setup workflows.
- name: Reference
directory: reference
description: API, CLI, and configuration reference.
- name: Tutorials
directory: tutorials
description: Step-by-step learning material.
- name: Troubleshooting
directory: troubleshooting
description: Known issues and remediation steps.
watchers:
docs-main:
type: git
repo: https://github.com/your-org/docs-site.git
branch: main
interval: 300
auto_start: true
credential: github-read-token
files:
rules:
- name: Public docs
include:
- "docs/**/*.{md,mdx,markdown}"
exclude:
- "**/node_modules/**"
- "**/build/**"

secrets:
existingSecret: synth-runtime-secrets

The runtime Secret named by secrets.existingSecret must contain the keys referenced by CaSc credentials, such as OPENCODE_API_KEY and GITHUB_PAT. The project schema belongs at projects.<name>.schema.categories; older exports may call this field scheme, but schema is the canonical CaSc key. Use auto_start: false if you want to inspect the imported watcher before the first clone.

Verify

kubectl -n synth rollout status deploy/synth --timeout=10m
kubectl -n synth get deploy,svc,ingress,pvc
curl -fsS https://synth.example.com/health

Then sign in and confirm Settings -> Models, Projects, and Watchers show the expected imported configuration.

Finally, open Explore, search for a phrase from an approved source file, and open the returned exact page. Confirm that the Project is ready, the result comes from a real watched page, and its provenance identifies the expected source. Kubernetes health does not by itself prove that Project evidence was published successfully.

MCP Behind HTTPS

Set SYNTH_PUBLIC_URL or the chart equivalent so generated MCP config points to the external HTTPS origin:

https://synth.example.com/api/mcp/

Production Checklist

  • persistent volume enabled
  • HTTPS ingress configured
  • provider credentials loaded from secrets
  • CaSc imported from Helm values
  • first admin created
  • backup plan for SYNTH_HOME
  • scoped API keys for agents
  • organization credentials created before private watchers start

For single-VM POCs, Docker Compose is simpler. For team use, Kubernetes is the clean path.