Manual installation
Set up rCTF from source for development or custom deployments.
This guide covers setting up rCTF from source without Docker, suitable for development or environments where you need full control over the stack.
Prerequisites
- Bun 1.2 or later
- PostgreSQL 15+
- Redis 7+
Project structure
rCTF is a (mostly) Bun monorepo with the following layout:
apps/
- api/ Hono REST API server
- web/ SvelteKit frontend (static build)
- admin-bot/ Puppeteer-based admin bot service
- k8s-operator/ Kubernetes challenge instancer (Go)
- docker-instancer/ Docker challenge instancer (Python)
- docs/ Documentation site
- cli/ Operational CLI
packages/
- config/ YAML/JSON config loader with Zod validation
- db/ Drizzle ORM schema and migrations
- scoring/ Pluggable scoring algorithms
- types/ Shared route definitions and validators
- util/ Small shared utilities
Setup
-
Clone and install dependencies
Terminal window git clone https://github.com/otter-sec/rctf.gitcd rctfbun i -
Create a configuration directory
rCTF loads configuration from a
rctf.d/directory. Files inside it (YAML or JSON) are loaded alphabetically and deep-merged. Create one with at the project root.mkdir rctf.drctf.d/
- 01-base.yaml create this next
Create a minimal configuration file:
rctf.d/01-base.yaml ctfName: My CTForigin: https://ctf.example.comtokenKey: <base64-encoded-32-byte-key>database:sql: postgres://user:password@localhost:5432/rctfredis: redis://localhost:6379migrate: beforestartTime: 1735689600000endTime: 1735776000000divisions:open: OpenTip (Generating a token key)
The
tokenKeymust be a base64-encoded 32-byte random key used for AES-GCM token encryption. Generate one with .openssl rand -base64 32You can also set configuration values through environment variables. See Configuration for the full reference.
-
Run database migrations
Terminal window bun run db:migrateAlternatively, set
database.migratetobeforein your config to run migrations automatically on startup. -
Start the development servers
Terminal window bun devThis starts both the API server on
http://127.0.0.1:3000and the SvelteKit dev server onhttp://127.0.0.1:5173. The dev server proxies API requests to the backend.To run them separately:
Terminal window bun run dev:api # API only on :3000bun run dev:web # Frontend only on :5173Tip (One-shot dev setup)
For a fresh local environment,
runs the migrations, resets and seeds the database, and starts the development servers. The seed includes an admin, 1000 teams, 38 challenges, and sample solves and submissions. The console prints login URLs for the admin and one team.bun run dev:mock
Production build
For production deployments, use our prebuilt Docker images rather than running a source build by hand. The image bundles the API, the compiled leaderboard worker, and the static frontend, and it runs Drizzle migrations on boot:
image: ghcr.io/otter-sec/rctf:latestSee Quick start with Docker for the full Compose setup. The manual build above is intended for development. If you run bun directly in production, you also need to manage the build, leaderboard worker, and reverse proxy that the image normally handles.
Scaling
The instanceType config option lets you split an rCTF process into frontend-only or leaderboard-only roles for horizontal scaling. See Scaling for the full table.
Running tests
Tests use PGlite (in-process PostgreSQL) and ioredis-mock, so no external services are needed:
bun run test # Run all tests from rootbun run test:server:coverage # Server tests with coveragebun run typecheck # Typecheck all workspaces