Deploy on Kubernetes
Terrantula deploys to Kubernetes via a Helm chart (infra/k8s/). The API and Worker run as long-running Bun processes; pg-boss (Postgres-native) is the job queue. The bundled Bitnami PostgreSQL chart provides both the database and the queue — no additional infrastructure is required.
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Ingress (nginx) │
└────────────────────────────┬────────────────────────────────────┘
│
┌──────────────▼──────────────┐
│ API (Bun) │
│ Hono + Drizzle + Zod │
│ │
│ ┌─────────────────────┐ │
│ │ Business Logic │ │
│ │ - apply │ │
│ │ - constraints │ │
│ │ - recommendations │ │
│ │ - interpolation │ │
│ └─────────────────────┘ │
└──────┬───────────┬──────────┘
│ │
┌──────────▼───────────▼──────────┐
│ PostgreSQL │
│ (data + pg-boss job queue) │
└─────────────────────────────────┘
│
┌───────▼─────────────────┐
│ Worker (Bun) │
│ pg-boss long-poll │
│ - resolve + decrypt │
│ secrets │
│ - interpolate payload │
│ - fire webhook or PR │
│ - update ActionRun │
└──────────────────────────┘
Prerequisites
- Kubernetes 1.25+
helm 3.x
kubectl configured for the target cluster
1. Pull chart dependencies
helm dependency update infra/k8s
This downloads the bitnami/postgresql subchart.
2. Install
helm install terrantula infra/k8s
The chart deploys PostgreSQL, the API, and the Worker. DATABASE_URL is constructed automatically from the bundled PostgreSQL credentials — no manual secret management needed for a default install.
3. Run migrations
Migrations are not run automatically on Kubernetes. Run them as a one-off pod after install or upgrade:
kubectl run terrantula-migrate \
--image=ghcr.io/terrantula-io/terrantula-api:latest \
--restart=Never \
--env="DATABASE_URL=$(kubectl get secret terrantula-secrets -o jsonpath='{.data.DATABASE_URL}' | base64 -d)" \
-- bun run src/db/migrate.ts
Configuration
All values can be overridden with --set or a values override file (-f my-values.yaml).
PostgreSQL
| Value | Default | Description |
|---|
postgresql.enabled | true | Deploy bundled PostgreSQL. Set to false to use an external database. |
postgresql.auth.username | terrantula | Database username |
postgresql.auth.password | terrantula | Database password — change this in production |
postgresql.auth.database | terrantula | Database name |
postgresql.primary.persistence.size | 8Gi | PVC size for PostgreSQL data |
API
| Value | Default | Description |
|---|
api.replicaCount | 2 | Number of API pod replicas |
api.port | 3000 | Container port |
image.api.repository | ghcr.io/terrantula-io/terrantula-api | API image repository |
image.api.tag | latest | API image tag |
Worker
| Value | Default | Description |
|---|
worker.replicaCount | 1 | Number of Worker pod replicas |
image.worker.repository | ghcr.io/terrantula-io/terrantula-worker | Worker image repository |
image.worker.tag | latest | Worker image tag |
Ingress
| Value | Default | Description |
|---|
ingress.enabled | true | Deploy an Ingress resource |
ingress.className | nginx | Ingress class |
ingress.host | terrantula.example.com | Hostname |
ingress.tls | [] | TLS configuration |
Common scenarios
External database
Disable the bundled PostgreSQL and supply a connection string:
helm install terrantula infra/k8s \
--set postgresql.enabled=false \
--set secrets.databaseUrl="postgresql://user:pass@your-db-host:5432/terrantula"
Production credentials
Use a values override file to avoid passing secrets on the command line:
# my-values.yaml
postgresql:
auth:
password: "a-strong-random-password"
ingress:
host: terrantula.your-domain.com
tls:
- hosts:
- terrantula.your-domain.com
secretName: terrantula-tls
helm install terrantula infra/k8s -f my-values.yaml
Upgrade
helm upgrade terrantula infra/k8s
Building images
If you're self-building rather than using published images, build from the workspace root (required for monorepo dependency resolution):
docker build -f packages/api/Dockerfile -t ghcr.io/your-org/terrantula-api:latest .
docker build -f packages/worker/Dockerfile -t ghcr.io/your-org/terrantula-worker:latest .
docker push ghcr.io/your-org/terrantula-api:latest
docker push ghcr.io/your-org/terrantula-worker:latest
Then reference your images at install time:
helm install terrantula infra/k8s \
--set image.api.repository=ghcr.io/your-org/terrantula-api \
--set image.worker.repository=ghcr.io/your-org/terrantula-worker