Skip to content

Installation

This page takes you from an empty namespace to a running Forginate control plane.

1. Install the operators

bash
# 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=true

Create 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

bash
git clone https://github.com/the-pizza/forginate.git && cd forginate
bash deploy/scripts/bootstrap-secrets.sh forginate

The script creates the namespace (here forginate) and generates three secrets if they don't already exist — it is idempotent and never overwrites:

SecretKeysConsumed by
forginate-db-app-userusername, passwordCNPG bootstrap (initdb.secret), backup CronJob
forginate-redispasswordredis --requirepass, all services' REDIS_URL
forginate-minioroot-user, root-passwordMinIO root creds, gateway/chat-editor S3 access

3. Create the application secrets

These are not created by the script — create them yourself:

bash
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:

bash
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):

yaml
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 namespace

5. Install

bash
helm upgrade --install forginate deploy/helm/forginate \
  -n forginate -f values-prod.yaml \
  --wait --timeout 5m

kubectl -n forginate get pods -w

Expected 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

bash
# 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/healthz

Then open https://forginate.example.com, sign in with GitHub — the account matching apiGateway.founderGithubLogin becomes the founder/admin — and continue with:

  1. LLM Providers — add a platform key so signups can run jobs.
  2. GitHub App — enable tenant repo access.
  3. Backups — verify the CronJob and run a restore drill.

Forginate — build software with an AI factory.