Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions apps/docs/content/docs/en/platform/self-hosting/kubernetes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { FAQ } from '@/components/ui/faq'

## Prerequisites

- Kubernetes 1.19+
- Helm 3.0+
- Kubernetes 1.25+
- Helm 3.8+
- PV provisioner support

## Installation
Expand All @@ -23,47 +23,59 @@ git clone https://github.com/simstudioai/sim.git && cd sim
BETTER_AUTH_SECRET=$(openssl rand -hex 32)
ENCRYPTION_KEY=$(openssl rand -hex 32)
INTERNAL_API_SECRET=$(openssl rand -hex 32)
CRON_SECRET=$(openssl rand -hex 32)
POSTGRES_PASSWORD=$(openssl rand -hex 24)

# Install
helm install sim ./helm/sim \
--set app.env.BETTER_AUTH_SECRET="$BETTER_AUTH_SECRET" \
--set app.env.ENCRYPTION_KEY="$ENCRYPTION_KEY" \
--set app.env.INTERNAL_API_SECRET="$INTERNAL_API_SECRET" \
--set app.env.CRON_SECRET="$CRON_SECRET" \
--set postgresql.auth.password="$POSTGRES_PASSWORD" \
--namespace simstudio --create-namespace
```

## Cloud-Specific Values

These are cloud-tuned **alternatives** to the generic install above — pick one path, don't run both. The commands reuse the `$BETTER_AUTH_SECRET`, `$ENCRYPTION_KEY`, `$INTERNAL_API_SECRET`, `$CRON_SECRET`, and `$POSTGRES_PASSWORD` variables generated in [Installation](#installation) above, so run that block's `openssl` lines first in the same shell. They use `helm upgrade --install`, so they work whether or not a release exists yet. Two caveats when converting an existing generic install rather than starting fresh: (1) **reuse the original secret values** — recover them with `helm get values sim -n simstudio` if your shell no longer has them; supplying a newly generated `ENCRYPTION_KEY` makes every previously encrypted credential (OAuth tokens, provider keys, environment variables) undecryptable. (2) The cloud values rename the bundled PostgreSQL database to `simstudio`, but Postgres only applies that setting on first initialization — add `--set postgresql.auth.database=sim` to keep your existing database. If you'd rather start clean, `helm uninstall sim -n simstudio`, delete its PVCs, and run the cloud command fresh.

<Tabs items={['AWS EKS', 'Azure AKS', 'GCP GKE']}>
<Tab value="AWS EKS">
```bash
helm install sim ./helm/sim \
helm upgrade --install sim ./helm/sim \
Comment thread
greptile-apps[bot] marked this conversation as resolved.
--values ./helm/sim/examples/values-aws.yaml \
--set app.env.BETTER_AUTH_SECRET="$BETTER_AUTH_SECRET" \
--set app.env.ENCRYPTION_KEY="$ENCRYPTION_KEY" \
Comment thread
waleedlatif1 marked this conversation as resolved.
--set app.env.INTERNAL_API_SECRET="$INTERNAL_API_SECRET" \
--set app.env.CRON_SECRET="$CRON_SECRET" \
--set postgresql.auth.password="$POSTGRES_PASSWORD" \
Comment thread
waleedlatif1 marked this conversation as resolved.
--set app.env.NEXT_PUBLIC_APP_URL="https://sim.yourdomain.com" \
--namespace simstudio --create-namespace
```
</Tab>
<Tab value="Azure AKS">
```bash
helm install sim ./helm/sim \
helm upgrade --install sim ./helm/sim \
--values ./helm/sim/examples/values-azure.yaml \
--set app.env.BETTER_AUTH_SECRET="$BETTER_AUTH_SECRET" \
--set app.env.ENCRYPTION_KEY="$ENCRYPTION_KEY" \
--set app.env.INTERNAL_API_SECRET="$INTERNAL_API_SECRET" \
--set app.env.CRON_SECRET="$CRON_SECRET" \
--set postgresql.auth.password="$POSTGRES_PASSWORD" \
--set app.env.NEXT_PUBLIC_APP_URL="https://sim.yourdomain.com" \
--namespace simstudio --create-namespace
```
</Tab>
<Tab value="GCP GKE">
```bash
helm install sim ./helm/sim \
helm upgrade --install sim ./helm/sim \
--values ./helm/sim/examples/values-gcp.yaml \
--set app.env.BETTER_AUTH_SECRET="$BETTER_AUTH_SECRET" \
--set app.env.ENCRYPTION_KEY="$ENCRYPTION_KEY" \
--set app.env.INTERNAL_API_SECRET="$INTERNAL_API_SECRET" \
--set app.env.CRON_SECRET="$CRON_SECRET" \
--set postgresql.auth.password="$POSTGRES_PASSWORD" \
--set app.env.NEXT_PUBLIC_APP_URL="https://sim.yourdomain.com" \
--namespace simstudio --create-namespace
```
Expand Down Expand Up @@ -115,7 +127,7 @@ externalDatabase:

```bash
# Port forward for local access
kubectl port-forward deployment/sim-sim-app 3000:3000 -n simstudio
kubectl port-forward deployment/sim-app 3000:3000 -n simstudio

# View logs
kubectl logs -l app.kubernetes.io/component=app -n simstudio --tail=100
Expand All @@ -130,7 +142,7 @@ helm uninstall sim --namespace simstudio
<FAQ items={[
{ question: "What are the default resource limits for the Sim app pod?", answer: "The Helm chart defaults to 8 Gi memory limit / 4 Gi request and 2000m CPU limit / 1000m request per app pod. The pod runs as non-root (UID 1001) with fsGroup 1001 for security." },
{ question: "Can I use an external database instead of the bundled PostgreSQL?", answer: "Yes. Set postgresql.enabled to false and configure the externalDatabase section with your host, port, username, password, database name, and sslMode. The external database must have the pgvector extension installed." },
{ question: "How do I manage secrets securely in Kubernetes?", answer: "The Helm chart supports pre-existing Kubernetes secrets via app.secrets.existingSecret. Set enabled to true and provide the secret name. This integrates with External Secrets Operator, HashiCorp Vault, Azure Key Vault, and similar tools. Key mappings can be customized if your secret uses different key names." },
{ question: "How do I manage secrets securely in Kubernetes?", answer: "The Helm chart supports pre-existing Kubernetes secrets via app.secrets.existingSecret. Set enabled to true and provide the secret name. This integrates with External Secrets Operator, HashiCorp Vault, Azure Key Vault, and similar tools. The secret must use the standard key names (BETTER_AUTH_SECRET, ENCRYPTION_KEY, INTERNAL_API_SECRET, CRON_SECRET, ...) — it is consumed wholesale, so key remapping is not supported." },
{ question: "Can I scale the app to multiple replicas?", answer: "Yes. Set app.replicaCount in your values.yaml. The chart supports standard Kubernetes scaling. Ensure your database can handle the additional connections from multiple replicas." },
{ question: "Are there cloud-specific example configurations?", answer: "Yes. The Helm chart includes example values files for AWS EKS (values-aws.yaml), Azure AKS (values-azure.yaml), GCP GKE (values-gcp.yaml), as well as files for production, development, external databases, external secrets, and whitelabeled deployments." },
{ question: "What ingress options are supported?", answer: "The chart supports ingress with configurable className (e.g., nginx), TLS termination, and separate host configuration for the app. Enable it with ingress.enabled: true and set your host under ingress.app.host." },
Expand Down
4 changes: 2 additions & 2 deletions helm/sim/.claude/skills/sim-helm/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ These are non-negotiable. Violating any of these has burned users in the past.
2. **Never set `image.tag: latest`.** The chart defaults to `Chart.AppVersion` for a reason — reproducible rollouts. If the user pinned `latest`, push back.
3. **Never edit chart templates to work around a `fail` statement.** The validation exists because a misconfiguration would otherwise surface as a runtime CrashLoopBackOff with cryptic env errors.
4. **Never drop `automountServiceAccountToken: false`** unless the workload genuinely needs in-cluster API access (Sim's app/realtime/postgres pods do not).
5. **Never `kubectl delete sts` without `--cascade=orphan`** on a live Postgres. It deletes the pods and PVCs.
5. **Never `kubectl delete sts` without `--cascade=orphan`** on a live Postgres. It deletes the pods (PVCs survive, but the database goes down immediately).
6. **Never tell a user "the chart works on your cluster" without `helm lint` + `helm template` against their values.** Static reading is not validation.
7. **Always confirm before `helm uninstall` in a shared namespace.** PVCs survive but other namespace resources may not.

Expand All @@ -136,7 +136,7 @@ kubectl --namespace <ns> get pods,events --sort-by='.lastTimestamp'
kubectl --namespace <ns> logs deploy/sim-app --tail=200
kubectl --namespace <ns> logs deploy/sim-realtime --tail=200
kubectl --namespace <ns> logs sts/sim-postgresql --tail=200
kubectl --namespace <ns> logs job/sim-migrations --tail=200 2>/dev/null
kubectl --namespace <ns> logs deploy/sim-app -c migrations --tail=200 2>/dev/null
kubectl --namespace <ns> describe pod -l app.kubernetes.io/name=sim
```

Expand Down
2 changes: 1 addition & 1 deletion helm/sim/.claude/skills/sim-helm/references/secrets.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export POSTGRES_PASSWORD=$(openssl rand -base64 24 | tr -d '/+=') # if using ch
| `INTERNAL_API_SECRET` | Shared auth between `sim-app` ↔ `sim-realtime` pods | 32 bytes = 64 hex chars | Both deployments must roll together — temporary realtime errors during the rollout |
| `CRON_SECRET` | Authenticates scheduled CronJob pods to the app | 32 bytes = 64 hex chars | Rotating just needs `helm upgrade`; next cron run uses the new value |
| `API_ENCRYPTION_KEY` (optional) | Encrypts user-stored API keys (OpenAI tokens, etc.) at rest in Postgres | **Exactly 64 hex chars** (the app rejects other lengths) | Without it, keys are stored plain. Once set, never rotate without a migration |
| `POSTGRES_PASSWORD` (chart-bundled Postgres only) | Postgres superuser password | Any length ≥ 12 chars matching `^[a-zA-Z0-9._-]+$` | Requires Postgres pod restart + app rollout |
| `POSTGRES_PASSWORD` (chart-bundled Postgres only) | Postgres superuser password | ≥ 8 chars (schema-enforced) matching `^[a-zA-Z0-9._-]+$`; 32-byte hex recommended | Requires Postgres pod restart + app rollout |

The `^[a-zA-Z0-9._-]+$` constraint on the Postgres password exists because the chart embeds the password into `DATABASE_URL` without URL-encoding. The `tr -d '/+='` strips the three problematic characters from `openssl rand -base64` output. The chart enforces this regex at template time.

Expand Down
12 changes: 6 additions & 6 deletions helm/sim/.claude/skills/sim-helm/references/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ kubectl --namespace $NS describe pod -l app.kubernetes.io/instance=sim
kubectl --namespace $NS logs deploy/sim-app --tail=200
kubectl --namespace $NS logs deploy/sim-realtime --tail=200
kubectl --namespace $NS logs sts/sim-postgresql --tail=200
kubectl --namespace $NS logs job/sim-migrations --tail=200 2>/dev/null || true
kubectl --namespace $NS logs deploy/sim-app -c migrations --tail=200 2>/dev/null || true
```

---
Expand Down Expand Up @@ -63,9 +63,9 @@ Match the error:
|---|---|---|
| `Invalid env: ... NEXT_PUBLIC_APP_URL: Invalid url` | URL field set to empty string or invalid format | Set `app.env.NEXT_PUBLIC_APP_URL` to a valid URL — `https://sim.example.com` in prod, `http://localhost:3000` in dev |
| `getaddrinfo ENOTFOUND ... -postgresql` / `connect ECONNREFUSED` | App can't reach Postgres | Check `kubectl get pod -l app.kubernetes.io/name=postgresql` is `Running`; check `postgresql.auth.password` matches the password in the Secret |
| `password authentication failed for user "sim"` | Postgres password rotated but app pod wasn't restarted, OR password contains URL-unsafe chars | `kubectl rollout restart deploy/sim-app -n sim`; regenerate password with `openssl rand -base64 24 \| tr -d '/+='` |
| `password authentication failed for user "postgres"` | Postgres password rotated but app pod wasn't restarted, OR password contains URL-unsafe chars | `kubectl rollout restart deploy/sim-app -n sim`; regenerate password with `openssl rand -base64 24 \| tr -d '/+='` |
| `BETTER_AUTH_SECRET is missing` / `INTERNAL_API_SECRET is required` | Required env var not present in the Secret | Verify with `kubectl get secret sim-app-secrets -o jsonpath='{.data}' \| jq 'keys'`; if missing, fix your secret strategy |
| `Migration failed` or app starts before migration | Migration Job hasn't completed | `kubectl logs job/sim-migrations -n sim`; rerun with `kubectl delete job/sim-migrations && helm upgrade ...` |
| `Migration failed` | Migrations init container on the app pod failed | `kubectl logs deploy/sim-app -c migrations -n sim`; rerun with `kubectl rollout restart deploy/sim-app -n sim` (migrations run before each app pod starts, so the app can never start ahead of them) |

---

Expand Down Expand Up @@ -112,7 +112,7 @@ kubectl describe ingress -n sim
| No `ADDRESS` in `kubectl get ingress` | Ingress controller not installed — install `ingress-nginx`, AWS LBC, GCP LB controller, etc. |
| `ingressClassName` doesn't match installed controller | `kubectl get ingressclass` to list installed classes, set `ingress.className` to match |
| Address is set but DNS resolves to wrong IP | `dig <your-host>` — point DNS at the ingress controller's external IP / LoadBalancer / CNAME |
| TLS cert errors | If using cert-manager, check `kubectl describe certificate -n sim`; verify `ingress.tls.issuerRef` |
| TLS cert errors | If using cert-manager, check `kubectl describe certificate -n sim`; verify the `cert-manager.io/cluster-issuer` annotation on the ingress |
| `503 Service Unavailable` | Ingress routing is fine but app pod isn't `Ready` — go back to the diagnostic block |

---
Expand Down Expand Up @@ -153,7 +153,7 @@ Bump the relevant resource limit. Defaults:
|---|---|---|
| `app` | `1000m` CPU / `4Gi` memory | `2000m` CPU / `8Gi` memory |
| `realtime` | `250m` CPU / `512Mi` memory | `500m` CPU / `1Gi` memory |
| `postgresql` | `250m` CPU / `512Mi` memory | `1000m` CPU / `2Gi` memory |
| `postgresql` | `500m` CPU / `1Gi` memory | `2Gi` memory (no CPU limit) |

Override in values:

Expand All @@ -172,7 +172,7 @@ app:

```bash
helm version
kubectl version --short
kubectl version
helm get values sim -n sim --revision $(helm history sim -n sim | tail -1 | awk '{print $1}')
kubectl get all,pvc,ingress,externalsecret -n sim -o wide
kubectl describe pods -n sim -l app.kubernetes.io/instance=sim | head -200
Expand Down
4 changes: 2 additions & 2 deletions helm/sim/.claude/skills/sim-helm/references/values-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ The Sim chart splits configuration across **four** layers. Understanding which l
┌───────────────────────────────────────────────────────────────────────────┐
│ Layer 3: chart-computed (inline env: on the Deployment) │
│ → DATABASE_URL, SOCKET_SERVER_URL, OLLAMA_URL │
│ → DATABASE_URL, SOCKET_SERVER_URL, OLLAMA_URL, PII_URL
│ → Derived from postgresql.* / externalDatabase.* / service.* values │
│ → CANNOT be overridden via app.env — chart filters them out │
└───────────────────────────────────────────────────────────────────────────┘
Expand Down Expand Up @@ -63,7 +63,7 @@ The exhaustive list of keys per layer lives in `helm/sim/values.yaml`. Read the
| Rate limits | 2 (envDefaults) | `RATE_LIMIT_WINDOW_MS`, `RATE_LIMIT_FREE_SYNC`, etc. |
| Execution timeouts | 2 (envDefaults) | `EXECUTION_TIMEOUT_FREE`, `EXECUTION_TIMEOUT_PRO`, etc. |
| IVM pool / quotas | 2 (envDefaults) | `IVM_POOL_SIZE`, `IVM_MAX_CONCURRENT`, `IVM_MAX_PER_WORKER`, etc. |
| Connection strings | 3 (chart-computed) | `DATABASE_URL`, `SOCKET_SERVER_URL`, `OLLAMA_URL` |
| Connection strings | 4 (chart-computed) | `DATABASE_URL`, `SOCKET_SERVER_URL`, `OLLAMA_URL`, `PII_URL` |
| Custom downward API / configMapKeyRef | 4 (extraEnvVars) | anything that needs `valueFrom:` |

## Common authoring patterns
Expand Down
2 changes: 1 addition & 1 deletion helm/sim/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ apiVersion: v2
name: sim
description: A Helm chart for Sim - the open-source AI workspace where teams build, deploy, and manage AI agents
type: application
version: 1.0.0
version: 1.1.0
appVersion: "0.6.73"
kubeVersion: ">=1.25.0-0"
home: https://sim.ai
Expand Down
Loading
Loading