Deployment

June 23, 2026 · View on GitHub

Docker Compose (local / single-host)

cp .env.example .env        # fill every value
cd mastra-runner && npm install && cd ..    # one-time lockfile
docker compose up

All nine services start. Langfuse initialises its own database schema on first boot. The adapter runs alembic upgrade head before uvicorn starts.

With real-time collaboration

docker compose -f docker-compose.yml -f docker-compose.collab.yml up

Adds a tenth service: the y-websocket server on port 1234. Set VITE_COLLAB_SERVER_URL=ws://localhost:1234 in .env.local.


Helm chart (Kubernetes / on-prem)

The Helm chart is at deploy/helm/buildaharness/. It deploys all nine services with correct readiness probes, SIGTERM handling, and rolling updates.

helm install buildaharness ./deploy/helm/buildaharness \
  --set secrets.jwtSecret=$(openssl rand -base64 32) \
  --set secrets.postgresPassword=$(openssl rand -base64 24) \
  --set secrets.litellmMasterKey=$(openssl rand -base64 32) \
  --set secrets.langfuseNextauthSecret=$(openssl rand -base64 32) \
  --set secrets.langfuseSalt=$(openssl rand -base64 32) \
  --set secrets.langfuseEncryptionKey=$(openssl rand -hex 32) \
  --set secrets.clickhousePassword=yourpassword \
  --set ingress.enabled=true \
  --set ingress.host=buildaharness.your-domain.com

Post-install, helm status buildaharness prints the SSO setup guide from templates/NOTES.txt.

External Postgres / Redis (RDS, ElastiCache)

# values.yaml
postgresql:
  enabled: false          # disable Bitnami sub-chart
  external:
    host: my-rds.us-east-1.rds.amazonaws.com
    port: 5432
    database: buildaharness

redis:
  enabled: false          # disable Bitnami sub-chart
  external:
    host: my-elasticache.abc.cache.amazonaws.com
    port: 6379

Existing secrets

# values.yaml
secrets:
  existingSecret: my-buildaharness-secrets   # K8s Secret with all required keys

SSO / OIDC via Helm

# values.yaml
oidc:
  enabled: true
  issuerUrl: https://keycloak.example.com/realms/buildaharness
  clientId: buildaharness
  redirectUri: https://buildaharness.your-domain.com/auth/sso/callback
  adminGroups: buildaharness-admins

Set secrets.oidcClientSecret to your OAuth2 client secret.


SSO / OIDC (any deployment)

Environment variables

VariableDescription
OIDC_ENABLEDtrue to enable SSO login
OIDC_ISSUER_URLOIDC issuer base URL — e.g. https://keycloak.example.com/realms/buildaharness
OIDC_CLIENT_IDOAuth2 client ID
OIDC_CLIENT_SECRETOAuth2 client secret
OIDC_REDIRECT_URIFull callback URL — must match what's registered with the provider
OIDC_SCOPESSpace-separated scopes (default: openid email profile groups)
OIDC_GROUP_CLAIMJWT claim containing group names (default: groups)
OIDC_ADMIN_GROUPSComma-separated group names that map to org admin role
OIDC_ORG_SLUG_CLAIMClaim used to resolve the target org (default: org)
OIDC_AUTO_PROVISIONtrue (default) creates users on first SSO login
SCIM_BEARER_TOKENStatic bearer token for the SCIM 2.0 provisioning endpoint
REFRESH_TOKEN_TTL_DAYSRefresh token lifetime in days (default: 30)

Keycloak quick-start

Create a realm named buildaharness, add a client with:

  • Client ID: buildaharness
  • Access type: confidential
  • Valid Redirect URIs: https://your-domain/auth/sso/callback
  • Group mapper: map the groups claim to the access token

Then set OIDC_ISSUER_URL=https://keycloak.example.com/realms/buildaharness and the client credentials.

SCIM provisioning

Point your IdP's SCIM provisioning at:

Base URL:  https://your-domain/scim/v2
Auth:      Bearer <SCIM_BEARER_TOKEN>

Supported operations: list users, get user, deactivate user (PATCH with active: false). User creation is handled automatically on first SSO login when OIDC_AUTO_PROVISION=true.


Full environment variable reference

Required secrets

VariableHow to generate
JWT_SECRETopenssl rand -base64 32
POSTGRES_PASSWORDopenssl rand -base64 24
LITELLM_MASTER_KEYopenssl rand -base64 32
LANGFUSE_NEXTAUTH_SECRETopenssl rand -base64 32
LANGFUSE_SALTopenssl rand -base64 32
LANGFUSE_ENCRYPTION_KEYopenssl rand -hex 32 (must be exactly 64 hex chars)
CLICKHOUSE_PASSWORDany strong password
LANGFUSE_ADMIN_EMAILyour email
LANGFUSE_ADMIN_PASSWORDyour password

LLM keys

VariableDescription
OPENAI_API_KEYFor LLM nodes using OpenAI models
ANTHROPIC_API_KEYFor Anthropic models via LiteLLM

Adapter tuning

VariableDefaultDescription
REDIS_URLredis://redis:6379/1Redis connection string
ADAPTER_BASE_URLhttp://localhost:8000Public adapter URL used in generated endpoint URLs
A2A_BASE_URLADAPTER_BASE_URLOverride for A2A endpoint URLs
INVOKE_TIMEOUT_S120Synchronous invoke timeout in seconds
CORS_ORIGINShttp://localhost:3000,http://canvas:3000Comma-separated allowed origins
JWT_TTL_DAYS30Token lifetime in days
MAX_BODY_BYTES1048576Max request body size (1 MB)
JOB_TTL_HOURS4Hours before completed jobs are evicted
TRUST_PROXYtrueReads X-Real-IP/X-Forwarded-For; set false if adapter is internet-facing without a proxy
LANGFUSE_EVAL_ENABLEDtrue to register LLM-as-judge evaluator configs at boot

Langfuse (canvas)

Add to .env.local (never .env — Vite bakes these at build time):

VariableDescription
VITE_API_URLAdapter URL visible from the browser (default: http://localhost:8000)
VITE_LANGFUSE_ENABLEDtrue to enable canvas tracing
VITE_LANGFUSE_PUBLIC_KEYLangfuse public key (same as LANGFUSE_PUBLIC_KEY in .env)
VITE_LANGFUSE_HOSTLangfuse host URL (default: http://localhost:3001)

Collaboration

VariableDefaultDescription
VITE_COLLAB_SERVER_URL(unset)y-websocket URL — e.g. ws://localhost:1234. Leave unset to disable collab.
VITE_COLLAB_OFFLINE_PERSISTENCEtruePersist Yjs doc to IndexedDB

CI/CD pipeline

The .github/workflows/deploy.yml pipeline has five stages:

1. adapter-tests     pytest + ruff
2. build-and-push    docker build → ghcr.io (sha tag)
3. promote-staging   push staging tag → trigger staging deploy
4. deploy-staging    smoke_test.py — 5 checks against live adapter
5. deploy-production push latest + version tag
   └── post-deploy-eval   spec-validation + debate quality gate

deploy_flows.py (run post-deploy) iterates flows/*.json and deploys each flow to the live adapter. smoke_test.py checks /health, /runtimes (all 4), /compile$ \times 4 \text{runtimes}, $/run + poll, and AgentCard discovery.