Skip to content

Installation

This page covers prerequisites, dependency installation, and database setup for a CTFHive instance. For a fast local demo see Quickstart first.


Prerequisites

Requirement Minimum version Notes
Python 3.13+ Required; enforced in pyproject.toml via requires-python = ">=3.13"
uv any recent The only supported package manager for this repo
Docker + Docker Compose 24+ / v2+ Optional — required only for the Docker Compose stack or dynamic lab features
Redis 7+ Optional — app degrades gracefully without it (see Redis)
PostgreSQL 16+ Optional in dev (SQLite is the default), recommended in production

Clone the repository

git clone <repo-url> ctfd-ng
cd ctfd-ng

Install Python dependencies

uv sync

Installs all [project.dependencies] from pyproject.toml. This includes Flask, SQLAlchemy, Redis client, Gunicorn, Pydantic, the Docker SDK, WireGuard bindings, and all other runtime dependencies.

uv sync --all-extras

Adds the dev extras (pytest, pytest-flask, pytest-cov) and the docs extras (mkdocs, mkdocs-material, pymdown-extensions).

Equivalent Makefile target:

make install-dev
uv sync --extra docs

Adds mkdocs-material without pytest.

Virtual environment location

uv creates .venv/ in the repo root. The Makefile references binaries as .venv/bin/python, .venv/bin/flask, etc. You do not need to activate the venv manually when using uv run or make targets.


Database setup

Default: SQLite (development)

No database installation needed. On first startup create_app() calls db.create_all() automatically, creating instance/ctfapp.db (or the path in DATABASE_URL).

Bootstrap the schema and seed the default admin account explicitly with:

uv run python cli.py db bootstrap

This is equivalent to running db.create_all() inside an app context. It is safe to run multiple times (idempotent).

Apply Flask-Migrate migrations

If you are upgrading an existing database or working with Alembic migrations:

make db-upgrade

This runs flask db upgrade — the standard Flask-Migrate command.

Migration history

The migrations/ directory must exist (created by flask db init) before running db-upgrade. For fresh installs where migrations/ is absent, use cli.py db bootstrap (which calls db.create_all()) instead.

Production: PostgreSQL

Set DATABASE_URL in your .env:

DATABASE_URL=postgresql://ctfuser:strongpassword@localhost:5432/ctfapp

The psycopg2-binary driver is included in the default dependencies. For production, prefer a non-binary psycopg2 build linked against your system's libpq.


Redis (optional)

Redis is used for:

  • Rate limiting (sliding-window counters shared across gunicorn workers)
  • Challenge flag caching

The app runs without Redis. When Redis is unreachable at startup, init_redis() in ctfapp/extensions.py catches the connection error, logs a warning, and the app continues with:

  • Cache falls back to an in-process SimpleCache (per-worker, not shared)
  • Rate limiting falls back to memory:// (per-worker counters — see the Configuration rate limiting section)

To start Redis locally:

redis-server

Or with Docker:

docker run -d --name redis -p 6379:6379 redis:7-alpine

Then set in .env:

REDIS_URL=redis://localhost:6379/0
RATELIMIT_STORAGE_URI=redis://localhost:6379/0

Multi-worker production without Redis

Running gunicorn with -w 4 (or more) without RATELIMIT_STORAGE_URI pointing at Redis means each worker maintains its own counter. A client can send workers × limit requests before being rate-limited. Always set RATELIMIT_STORAGE_URI=redis://... in production.


Running the app

Development server

make dev

Runs flask run --debug --port 5000 with hot reload. Suitable for local development only.

Production with gunicorn

make dev-prod

Runs .venv/bin/gunicorn -w 4 -b 0.0.0.0:5000 "ctfapp:create_app()" --access-logfile -.

For a proper production deployment behind nginx see Deploy with Docker Compose and Server setup guide.


Makefile targets reference

Target Command executed Purpose
make install uv sync Install production dependencies
make install-dev uv sync --all-extras Install dev + docs dependencies
make dev flask run --debug --port 5000 Hot-reload dev server
make dev-prod gunicorn -w 4 ... Production-mode server (no reload)
make db-upgrade flask db upgrade Apply Alembic migrations
make db-reset flask ctfhive reset-db --confirm Destructive DB reset (requires CONFIRM=yes)
make docker-up docker compose up -d Start full Docker stack
make docker-down docker compose down Stop Docker stack
make docker-build docker compose build ctf-app Rebuild app container
make docker-logs docker compose logs -f ctf-app Tail app container logs
make docker-shell docker compose exec ctf-app bash Shell into app container
make test pytest tests/ -v --tb=short Full test suite
make test-cov pytest ... --cov=ctfapp Tests with HTML coverage report
make lint ruff check ctfapp/ tests/ Linting (ruff required)
make format ruff format ctfapp/ tests/ Auto-format code
make typecheck mypy ctfapp/ --ignore-missing-imports Type checking (mypy required)
make docs-serve uv run mkdocs serve Live-preview docs at http://127.0.0.1:8000
make docs-build uv run mkdocs build --strict Build static docs site
make health flask ctfhive health Check DB, Redis, Docker health
make clean removes __pycache__, .pytest_cache, coverage Clean build artifacts

Ops CLI

The repo ships a Typer-based operations CLI at cli.py (also aliased as ctfctrl.py). Run it with:

uv run python cli.py --help

Key command groups:

Group Example command Purpose
env cli.py env validate role-b Validate config for a deployment role
db cli.py db bootstrap Create all tables, seed admin
admin cli.py admin bootstrap --username ... --email ... --password ... Create/update admin account
challenge cli.py challenge import <yaml> Import a challenge from YAML
challenge cli.py challenge validate <yaml> Validate manifest without DB write
challenge cli.py challenge build <slug> Build local Docker image for a challenge
registry cli.py registry check List images and registry config
instance cli.py instance spawn <slug> Spawn a lab instance from CLI
vpn cli.py vpn status WireGuard VPN overview
worker cli.py worker list List lab worker inventory
health cli.py health all Full platform health check

Most commands accept --app-env <development|production|testing> and --json flags.


Next steps