On-Prem / Self-Host Install Guide

Run Cybros entirely inside your own environment. Two supported paths:

  • A. Kubernetes (recommended for HA) — the Helm chart at deploy/helm/cybros/.
  • B. Single VM (simplest) — docker-compose.prod.yml.

Both run with DEPLOYMENT_MODE=onprem. By default Cybros makes no outbound calls and emits no telemetry — the AI and payment providers are deterministic offline stubs and the GitHub integration is opt-in.


Prerequisites

ComponentVersionNotes
Kubernetes1.27+Path A. An ingress controller (nginx/ALB) + a way to issue TLS.
Helm3.12+Path A.
PostgreSQL15 or 16Bundled by the chart/compose, or bring a managed one.
Redis7Bundled by the chart/compose, or bring a managed one.
Docker + Compose v2recentPath B.
CPU / RAM~4 vCPU / 8 GB minGrows with concurrent scans.

Secrets you must provide (never commit these):

  • SECRET_ENCRYPTION_KEY64 hex chars (32 bytes) for AES-256-GCM envelope encryption. Generate: openssl rand -hex 32. Losing it makes stored secrets unrecoverable — back it up in your secrets manager.
  • DATABASE_URL, REDIS_URL — auto-wired when using bundled datastores.
  • Optional integration keys: GITHUB_APP_*, IdP/SSO, ANTHROPIC_API_KEY, payment keys. All optional; omit to stay fully offline.

Path A — Kubernetes (Helm)

1. (Air-gapped) mirror the images

# On a connected host:
for img in cybros-api cybros-web; do
  docker pull ghcr.io/hacktiger/$img:0.1.0
  docker tag  ghcr.io/hacktiger/$img:0.1.0 registry.internal.example.com/cybros/$img:0.1.0
  docker push registry.internal.example.com/cybros/$img:0.1.0
done
# Also mirror the bitnami postgresql/redis images referenced by the subcharts.

values-onprem.yaml already points image.registry at an internal mirror and enables the bundled Postgres/Redis subcharts.

2. Fetch chart dependencies

helm dependency build deploy/helm/cybros    # pulls postgresql + redis subcharts

Air-gapped: run this on a connected host, or helm pull the bitnami subcharts and drop the .tgz files into deploy/helm/cybros/charts/ on the target.

3. Create the secrets (do NOT put them in a committed values file)

Recommended: use an ExternalSecret (External Secrets Operator / Sealed Secrets / Vault CSI) and set externalSecret.enabled=true. For a quick install you can --set-string them:

helm upgrade --install cybros deploy/helm/cybros \
  -f deploy/helm/cybros/values-onprem.yaml \
  --namespace cybros --create-namespace \
  --set-string secrets.values.SECRET_ENCRYPTION_KEY="$(openssl rand -hex 32)" \
  --set-string postgresql.auth.password="$(openssl rand -base64 24)" \
  --set-string secrets.values.DATABASE_URL="postgresql+asyncpg://cybros:<pw>@cybros-postgresql:5432/cybros" \
  --set ingress.host=cybros.internal.example.com

4. Migrations

The chart runs alembic upgrade head as a pre-install/pre-upgrade Helm hook Job ({release}-migrate). It completes before api/worker roll. Watch it:

kubectl -n cybros get jobs
kubectl -n cybros logs job/cybros-migrate

Disable with migrations.enabled=false if you run migrations out of band.

5. Verify

kubectl -n cybros get pods
kubectl -n cybros port-forward svc/cybros-api 8000:8000 &
curl -s localhost:8000/api/v1/system/deployment      # -> "mode":"onprem"
curl -s localhost:8000/health                        # -> "status":"ok"

Then browse https://<ingress.host>.


Path B — Single VM (Docker Compose)

cp .env.example .env
# edit .env: set SECRET_ENCRYPTION_KEY (openssl rand -hex 32), POSTGRES_PASSWORD,
# and any integration keys. Leave AI_PROVIDER/PAYMENT_PROVIDER as stub for offline.

docker compose -f docker-compose.prod.yml up -d --build

This brings up postgres + redis + a one-shot migrate (alembic) + api + worker (celery -B) + web, all with DEPLOYMENT_MODE=onprem, healthchecks, restart policies, and persistent volumes (cybros_pg, cybros_redis).

  • Web: http://<host>:3000 · API: http://<host>:8000 (put a TLS reverse proxy in front).
  • Check: curl localhost:8000/api/v1/system/deployment.

First-admin bootstrap

  1. Set AUTH_DISABLED=false (default in the prod overlays) so real auth is enforced.
  2. Configure your identity source (Supabase JWKS or an SSO connection — see docs/security-architecture.md).
  3. The first user to sign in on a fresh database is bootstrapped as the org owner/admin; subsequent users are provisioned via SSO/SCIM or invited by an admin.
  4. From the admin UI, configure the GitHub App (optional), policies, and CMEK.

Air-gapped notes

  • No egress by default. AI_PROVIDER=stub and PAYMENT_PROVIDER=stub mean no LLM or payment calls. No telemetry/phone-home exists in the codebase.
  • Offline scanners. The deterministic scanners run as local subprocesses in the api/worker image — no rule-pack downloads at runtime. Bake any tool updates into the image and re-mirror.
  • GitHub optional. Only enable the GitHub App if you can reach api.github.com or a GHES instance; otherwise leave GITHUB_* unset and use manual scans.
  • Mirror everything. App images, the bitnami postgres/redis images, and (if used) AI_PROVIDER=anthropic requires reaching Anthropic — keep it stub when air-gapped.

Backups

  • Postgres is the source of truth (findings, ledger, audit hash-chain, encrypted secrets). Back it up with pg_dump/managed snapshots on a schedule.
    • Chart (bundled): kubectl -n cybros exec sts/cybros-postgresql -- pg_dump -U cybros cybros | gzip > backup.sql.gz
    • Compose: docker compose -f docker-compose.prod.yml exec postgres pg_dump -U cybros cybros | gzip > backup.sql.gz
  • SECRET_ENCRYPTION_KEY — back it up separately in your secrets manager. A DB restore is useless without the key that decrypts stored secrets.
  • Redis is a broker/cache — safe to lose; no backup required.
  • Test restores periodically. Restoring = load the dump into a fresh Postgres and point DATABASE_URL at it (schema already migrated inside the dump).