Skip to main content
Production deployment uses the same Helm chart as local development, configured for external managed services, real OAuth authentication, TLS ingress, and a container registry for agent images.

Prerequisites

  • A Kubernetes cluster (EKS, GKE, AKS, or any conformant cluster)
  • Helm 3+ (helm version)
  • A managed PostgreSQL instance (e.g. RDS, Cloud SQL, Supabase, Neon)
  • A managed Redis instance (e.g. ElastiCache, Upstash, Redis Cloud)
  • A container registry for agent images (e.g. GHCR, ECR, Docker Hub)
  • At least one OAuth provider (GitHub, Google, or GitLab) with a registered application
  • metrics-server installed in the cluster
  • A domain name with DNS pointing to your cluster’s load balancer

Deployment

1

Generate an encryption key

Optio encrypts all secrets at rest with AES-256-GCM. Generate a key and keep it somewhere safe — if you lose it, stored secrets (API keys, OAuth tokens) cannot be decrypted.
openssl rand -hex 32
Store the output. You will pass it as --set encryption.key=<value> during install.
2

Push agent images to your registry

Build and push the agent image presets to a registry your cluster can pull from:
# Build all presets
./images/build.sh

# Tag and push (example using GHCR)
docker tag optio-base:latest ghcr.io/your-org/optio-agent-base:latest
docker tag optio-node:latest ghcr.io/your-org/optio-agent-node:latest
docker tag optio-python:latest ghcr.io/your-org/optio-agent-python:latest
docker tag optio-go:latest ghcr.io/your-org/optio-agent-go:latest
docker tag optio-rust:latest ghcr.io/your-org/optio-agent-rust:latest
docker tag optio-full:latest ghcr.io/your-org/optio-agent-full:latest

docker push ghcr.io/your-org/optio-agent-base:latest
# ... repeat for each preset
Set agent.imagePullPolicy to IfNotPresent or Always (not Never) so Kubernetes can pull from the registry.
3

Configure an OAuth provider

Register an OAuth application with at least one provider. Optio supports GitHub, Google, and GitLab.GitHub — go to Settings → Developer settings → OAuth Apps → New OAuth App:
  • Homepage URL: https://optio.example.com
  • Authorization callback URL: https://optio.example.com/api/auth/github/callback
Note the Client ID and Client Secret.
The callback URL must match API_PUBLIC_URL exactly, including the /api/auth/<provider>/callback path. If these do not match, OAuth login will fail with a redirect URI mismatch error.
4

Install the Helm chart

helm install optio helm/optio \
  --namespace optio \
  --create-namespace \
  --set encryption.key=$(openssl rand -hex 32) \
  --set postgresql.enabled=false \
  --set externalDatabase.url="postgres://user:pass@your-db-host:5432/optio" \
  --set redis.enabled=false \
  --set externalRedis.url="redis://your-redis-host:6379" \
  --set ingress.enabled=true \
  --set ingress.hosts[0].host=optio.example.com \
  --set auth.github.clientId=your-github-client-id \
  --set auth.github.clientSecret=your-github-client-secret \
  --set agent.image.repository=ghcr.io/your-org/optio-agent-base \
  --set agent.imagePullPolicy=IfNotPresent
Replace values with your actual database URL, Redis URL, domain, OAuth credentials, and agent image repository.
Do not pass --set auth.disabled=true in production. Optio requires OAuth authentication to be active. If auth.disabled is set, any user can access the deployment without logging in.
5

Set the GitHub token secret

Optio needs a GitHub token for PR watching, issue sync, and repository detection. After the API pod is running, add it via the web UI (Settings → Secrets) or directly:
# The setup wizard in the web UI will prompt for this on first run
# Navigate to: https://optio.example.com/setup
The GITHUB_TOKEN secret requires repo scope for private repositories and public_repo for public ones.
6

Verify the deployment

Check that all pods are running:
kubectl get pods -n optio
Expected output shows optio-api-*, optio-web-*, and (if using in-cluster databases) optio-postgres-* and optio-redis-* pods all in Running state.Open https://optio.example.com — you should be redirected to the login page.

Production checklist

Review each item before going live:
#ItemNotes
1Encryption key generatedopenssl rand -hex 32; store it securely
2OAuth provider configuredAt least one of GitHub, Google, or GitLab
3Auth bypass disabledauth.disabled must be false or unset
4External PostgreSQLpostgresql.enabled=false + externalDatabase.url
5External Redisredis.enabled=false + externalRedis.url
6Public URLs setAPI_PUBLIC_URL and WEB_PUBLIC_URL match your domain
7Ingress with TLSingress.enabled=true with cert-manager or manual TLS
8Agent image in registryagent.imagePullPolicy=IfNotPresent or Always
9GitHub token secret setRequired for PR watching, issue sync, repo detection
10Resource limits tunedAdjust api.resources and agent PVC size for your workload
11metrics-server installedRequired for resource usage display in the cluster view

TLS with cert-manager

If cert-manager is installed in your cluster, enable automatic certificate provisioning:
helm upgrade optio helm/optio -n optio --reuse-values \
  --set ingress.certManager.enabled=true \
  --set ingress.certManager.clusterIssuer=letsencrypt-prod
For manual TLS certificate management, configure the ingress.tls field directly in a values file:
ingress:
  enabled: true
  tls:
    - secretName: optio-tls
      hosts:
        - optio.example.com

RBAC requirements

The Helm chart creates a ServiceAccount and Role for the API server automatically. The API server needs these permissions to manage agent pods:
  • pods: get, list, watch, create, delete
  • pods/exec: create (for running agents in pods)
  • pods/log: get
  • secrets: get, list, create, update, delete
  • persistentvolumeclaims: get, list, create, delete
  • services: get, list, create, delete
  • events: get, list, watch
A separate ClusterRole covers read access to nodes, namespaces, and metrics for the cluster health view. Do not remove or restrict these permissions — the API server will fail to provision agent pods.

Updating

To apply new configuration changes or upgrade to a newer version:
# Apply changes while preserving existing values
helm upgrade optio helm/optio -n optio --reuse-values
To trigger a rolling restart of the API and web servers (e.g. after updating a secret or environment variable):
kubectl rollout restart deployment/optio-api deployment/optio-web -n optio

Troubleshooting

  • Verify that the callback URL registered with the OAuth provider exactly matches {API_PUBLIC_URL}/api/auth/<provider>/callback
  • Set API_PUBLIC_URL explicitly in your Helm values if it is not being inferred correctly
  • Check for trailing slashes or http vs https mismatches
  • Confirm the agent image is pushed to your registry and the tag is correct
  • Check that imagePullSecrets is configured if your registry requires authentication
  • Verify agent.imagePullPolicy is IfNotPresent or Always (not Never)
  • Confirm externalDatabase.url is formatted correctly: postgres://user:pass@host:5432/dbname
  • Test connectivity from within the cluster: kubectl run -it --rm debug --image=postgres:16 --restart=Never -n optio -- psql "$DATABASE_URL"
  • Check that the database allows connections from the cluster’s IP range
  • Increase resource limits for agent pods via the image preset or Helm values (agent.pvc.size, API resource limits)
  • Check pod_health_events via the cluster view in the web UI for crash history
  • The cleanup worker automatically fails tasks on dead pods and recreates pods on the next task