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
| Component | Version | Notes |
|---|---|---|
| Kubernetes | 1.27+ | Path A. An ingress controller (nginx/ALB) + a way to issue TLS. |
| Helm | 3.12+ | Path A. |
| PostgreSQL | 15 or 16 | Bundled by the chart/compose, or bring a managed one. |
| Redis | 7 | Bundled by the chart/compose, or bring a managed one. |
| Docker + Compose v2 | recent | Path B. |
| CPU / RAM | ~4 vCPU / 8 GB min | Grows with concurrent scans. |
Secrets you must provide (never commit these):
SECRET_ENCRYPTION_KEY— 64 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
- Set
AUTH_DISABLED=false(default in the prod overlays) so real auth is enforced. - Configure your identity source (Supabase JWKS or an SSO connection — see
docs/security-architecture.md). - 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.
- From the admin UI, configure the GitHub App (optional), policies, and CMEK.
Air-gapped notes
- No egress by default.
AI_PROVIDER=stubandPAYMENT_PROVIDER=stubmean 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.comor a GHES instance; otherwise leaveGITHUB_*unset and use manual scans. - Mirror everything. App images, the bitnami postgres/redis images, and (if
used)
AI_PROVIDER=anthropicrequires reaching Anthropic — keep itstubwhen 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
- Chart (bundled):
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_URLat it (schema already migrated inside the dump).