Manual installation

Set up rCTF from source for development or custom deployments.

Edit this page View Markdown

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

  1. Clone and install dependencies

    Terminal window
    git clone https://github.com/otter-sec/rctf.git
    cd rctf
    bun i
  2. 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 mkdir rctf.d at the project root.

    • rctf.d/
      • 01-base.yaml create this next

    Create a minimal configuration file:

    rctf.d/01-base.yaml
    ctfName: My CTF
    origin: https://ctf.example.com
    tokenKey: <base64-encoded-32-byte-key>
    database:
    sql: postgres://user:password@localhost:5432/rctf
    redis: redis://localhost:6379
    migrate: before
    startTime: 1735689600000
    endTime: 1735776000000
    divisions:
    open: Open
    Tip (Generating a token key)

    The tokenKey must be a base64-encoded 32-byte random key used for AES-GCM token encryption. Generate one with openssl rand -base64 32.

    You can also set configuration values through environment variables. See Configuration for the full reference.

  3. Run database migrations

    Terminal window
    bun run db:migrate

    Alternatively, set database.migrate to before in your config to run migrations automatically on startup.

  4. Start the development servers

    Terminal window
    bun dev

    This starts both the API server on http://127.0.0.1:3000 and the SvelteKit dev server on http://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 :3000
    bun run dev:web # Frontend only on :5173
    Tip (One-shot dev setup)

    For a fresh local environment, bun run dev:mock 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.

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:latest

See 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:

Terminal window
bun run test # Run all tests from root
bun run test:server:coverage # Server tests with coverage
bun run typecheck # Typecheck all workspaces

Type to search.