Codapult ships a Helm chart in infra/helm/codapult/. It deploys the standalone Next.js container, optional BullMQ worker, optional Redis, ingress, service account, secrets, and autoscaling.
Chart Structure
infra/helm/codapult/
├── Chart.yaml
├── values.yaml
└── templates/
├── deployment.yaml
├── worker.yaml
├── redis.yaml
├── service.yaml
├── ingress.yaml
├── hpa.yaml
├── secrets.yaml
└── serviceaccount.yaml
Install
cp infra/helm/codapult/values.yaml my-values.yaml
helm install codapult ./infra/helm/codapult -f my-values.yaml
Upgrade:
helm upgrade codapult ./infra/helm/codapult -f my-values.yaml
Image
Build and push the standalone image first:
docker build -t ghcr.io/acme/codapult:2026-05-24 .
docker push ghcr.io/acme/codapult:2026-05-24
Set:
image:
repository: ghcr.io/acme/codapult
tag: '2026-05-24'
Secrets
Production should reference an existing Kubernetes Secret:
kubectl create secret generic codapult-secrets \
--from-literal=TURSO_DATABASE_URL="libsql://..." \
--from-literal=TURSO_AUTH_TOKEN="..." \
--from-literal=BETTER_AUTH_SECRET="..." \
--from-literal=STRIPE_SECRET_KEY="..." \
--from-literal=STRIPE_WEBHOOK_SECRET="..."
Then:
existingSecret: codapult-secrets
For local or disposable environments, values.yaml also supports inline secrets, but avoid that in production git repositories.
Core Values
| Value | Description |
|---|---|
replicaCount | Number of web pods when HPA is disabled |
image.repository, image.tag, image.pullPolicy | App image |
app.url, app.name, app.port | Public URL, display name, container port |
env | Non-secret environment variables |
existingSecret | Existing Secret used by envFrom |
ingress.enabled, ingress.className, ingress.hosts, ingress.tls | HTTP ingress |
resources | Web pod resource requests/limits |
autoscaling | HorizontalPodAutoscaler settings |
worker.enabled | Dedicated BullMQ worker deployment |
redis.enabled, redis.externalUrl | In-cluster or external Redis |
Workers and Redis
Enable production jobs with a dedicated worker:
env:
JOB_PROVIDER: bullmq
CODAPULT_DISABLE_IN_PROCESS_JOBS: 'true'
worker:
enabled: true
replicaCount: 1
redis:
enabled: true
The worker pod sets CODAPULT_WORKER_MODE=true and starts registered BullMQ handlers and cron schedules. Use an external Redis instead of in-cluster Redis when you need managed persistence:
redis:
enabled: false
externalUrl: redis://redis.example.com:6379
Ingress and TLS
The default chart assumes nginx ingress and cert-manager:
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: app.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: codapult-tls
hosts:
- app.example.com
Update annotations for your ingress controller if you use ALB, Traefik, Gateway API, or a managed platform ingress.