DocsSecurity & governance

Deployment & self-hosting

The reference topology, minimum configuration, horizontal scaling, backups, and forward-only migrations.

Reference topology#

The shipped docker-compose.yml is the reference deployment: caddy, web, api, runner, ssh-git, mcp, postgres, and redis, wired together entirely through the Docker network and the volumes listed in Architecture & deployment topology. There is no requirement to run it this way — every component is a plain container image and can be deployed to Kubernetes, ECS, or bare metal — but the compose file is the fastest path to a correct topology and is what docker compose up gives you out of the box.

Minimum required configuration#

At minimum, a self-hosted instance needs:

  • A PostgreSQL connection string and a Redis URL.
  • A public URL (used to build absolute links in emails, webhooks, and SAML metadata).
  • JWT signing secrets (jwt.accessSecret/jwt.refreshSecret) — long, random, and never rotated casually, since rotating them invalidates every existing session.
  • A secrets-encryption key (for CI/CD secret values at rest).
  • The internal hook secret shared between apps/api and every bare repository's Git hooks.

Scaling#

  • api is stateless per-request (all state lives in Postgres/Redis/volumes) and can be run as multiple replicas behind Caddy's load balancing.
  • runner is designed to be scaled horizontally and heterogeneously — run any number of replicas, each with its own RUNNER_NAME and RUNNER_TAGS, to add CI capacity or dedicate hardware (GPU, ARM, high-memory) to specific job types via tag matching. See CI/CD pipelines.
  • web is a stateless Next.js server and scales the same way api does.
  • postgres and redis are the two components that are not horizontally scaled by this architecture; standard PostgreSQL replication/Redis clustering approaches apply if needed at larger scale, transparently to the application layer.

Backups#

A consistent backup is: a pg_dump (or continuous WAL archiving) of the postgres_data volume, taken alongside a file-level snapshot of git_data, lfs_data, packages_data, oci_data, release_assets, and uploads_data. Because Postgres is authoritative for all relational state and the volumes are authoritative only for the byte content they were told to store by Postgres-recorded operations, restoring both from the same point in time (or restoring Postgres to a time at or after the volume snapshot) is sufficient for a consistent recovery — there is no separate index or cache layer that needs independent reconciliation.

Upgrades#

Database migrations (packages/database/prisma/migrations) are applied with prisma migrate deploy — additive, forward-only, and safe to run against a live database before the new application code is rolled out, since each migration is written to be backward-compatible with the previous release's queries until that release is fully retired. There is no supported downgrade path for a migration; roll forward with a corrective migration instead of attempting to reverse one against production data.