Installation
This page takes you from an empty namespace to a running Forginate control plane.
1. Install the operators
# CloudNativePG (Postgres operator) — required
kubectl apply --server-side -f \
https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/main/releases/cnpg-1.24.1.yaml
# cert-manager — required for TLS
helm repo add jetstack https://charts.jetstack.io
helm upgrade --install cert-manager jetstack/cert-manager \
-n cert-manager --create-namespace --set crds.enabled=trueCreate a ClusterIssuer for your DNS provider (DNS-01 is required for the tenant-apps wildcard cert) and note its name — it goes into global.certIssuer.
You also need an ingress controller (Traefik, ingress-nginx, …) and its IngressClass name for global.ingressClass.
2. Bootstrap the infrastructure secrets
git clone https://github.com/the-pizza/forginate.git && cd forginate
bash deploy/scripts/bootstrap-secrets.sh forginateThe script creates the namespace (here forginate) and generates three secrets if they don't already exist — it is idempotent and never overwrites:
| Secret | Keys | Consumed by |
|---|---|---|
forginate-db-app-user | username, password | CNPG bootstrap (initdb.secret), backup CronJob |
forginate-redis | password | redis --requirepass, all services' REDIS_URL |
forginate-minio | root-user, root-password | MinIO root creds, gateway/chat-editor S3 access |
3. Create the application secrets
These are not created by the script — create them yourself:
NS=forginate
# Database URL used by api-gateway, orchestrator, chat-editor.
# Host is the CNPG read-write service: <postgres.name>-rw (default forginate-db-rw).
DB_PASS=$(kubectl -n $NS get secret forginate-db-app-user -o jsonpath='{.data.password}' | base64 -d)
kubectl -n $NS create secret generic forginate-db-url \
--from-literal=url="postgres://forginate:${DB_PASS}@forginate-db-rw:5432/forginate"
# JWT session signing key (min 32 chars — the gateway refuses to boot otherwise)
kubectl -n $NS create secret generic forginate-jwt \
--from-literal=secret="$(openssl rand -base64 48)"
# AES-256-GCM vault key for encrypting tenant BYO provider credentials at rest
kubectl -n $NS create secret generic forginate-credential-key \
--from-literal=key="$(openssl rand -base64 32)"
# Shared bearer for service-to-service calls (chat-editor/orchestrator → gateway)
kubectl -n $NS create secret generic forginate-internal-token \
--from-literal=token="$(openssl rand -base64 48)"
# GitHub OAuth app for user sign-in (create at github.com → Developer settings →
# OAuth Apps; callback URL: https://forginate.example.com/auth/github/callback)
kubectl -n $NS create secret generic forginate-github-oauth \
--from-literal=client-id="Iv1.example" \
--from-literal=client-secret="…"Required vs. optional
The five secrets above plus the three bootstrap secrets are required — the api-gateway's config schema hard-fails without DATABASE_URL, JWT_SECRET, GITHUB_CLIENT_ID/GITHUB_CLIENT_SECRET, and FORGINATE_CREDENTIAL_KEY. The following are optional (optional: true in the pod spec — the pods boot without them): forginate-github-app, forginate-stripe, forginate-aws, forginate-registry-pull. See Configuration.
If you pull images from a private registry, also create a docker-registry secret and reference it via apiGateway.imagePullSecret, orchestrator.imagePullSecret, orchestrator.runnerImagePullSecret, and chatEditor.imagePullSecret:
kubectl -n $NS create secret docker-registry ghcr-pull \
--docker-server=ghcr.io --docker-username=<user> --docker-password=<packages-read-PAT>4. Write your values file
Create values-prod.yaml (the shipped values.yaml defaults target the maintainers' dev environment — override every global.* key):
global:
environment: prod
domain: forginate.example.com
appsWildcard: apps.forginate.example.com
storageClass: standard # your StorageClass
ingressClass: nginx # your IngressClass
certIssuer: letsencrypt-prod # your ClusterIssuer
apiGateway:
founderGithubLogin: "your-github-login" # auto-flagged isFounder on first login
shareHost: share.forginate.example.com
serviceMonitor:
enabled: false # true only if prometheus-operator is installed
orchestrator:
runsNamespace: forginate-runs # chart creates this namespace5. Install
helm upgrade --install forginate deploy/helm/forginate \
-n forginate -f values-prod.yaml \
--wait --timeout 5m
kubectl -n forginate get pods -wExpected steady state: forginate-db-1 (CNPG), redis-0, minio-0, api-gateway-*, orchestrator-*, chat-editor-* all Running, plus the forginate-pg-backup CronJob.
First boot ordering
CNPG takes a minute or two to initialize the cluster. The services crash-loop harmlessly until forginate-db-rw answers; database migrations run on gateway boot and are idempotent.
6. Verify
# In-cluster health check
kubectl -n forginate run curl --rm -it --restart=Never --image=curlimages/curl -- \
curl -s http://api-gateway.forginate.svc/healthz
# Through the ingress
curl -s https://forginate.example.com/healthzThen open https://forginate.example.com, sign in with GitHub — the account matching apiGateway.founderGithubLogin becomes the founder/admin — and continue with:
- LLM Providers — add a platform key so signups can run jobs.
- GitHub App — enable tenant repo access.
- Backups — verify the CronJob and run a restore drill.