---
title: "Deploy CTFd"
description: "Capture-the-flag platform for running security competitions"
category: "Other"
url: https://railway.com/deploy/ctfd
---

# Deploy CTFd

Capture-the-flag platform for running security competitions

**[Deploy CTFd on Railway](https://railway.com/template/ctfd)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/ctfd/manifest.json

- **Creator:** A3A
- **Category:** Other
- **Total deploys:** 1

## Template content

### ctfd https://raw.githubusercontent.com/gridalpha/ctfd-railway/main/ctfd-mark.svg

- **Source:** https://github.com/gridalpha/ctfd-railway
- **Health check:** /healthcheck
- **Public domain:** Yes

### MySQL https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/mysql.svg

- **Image:** mysql:9.4
- **Start command:** `docker-entrypoint.sh mysqld --innodb-use-native-aio=0 --disable-log-bin --performance_schema=0 --innodb-buffer-pool-size=1G`

### Redis https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/redis.svg

- **Image:** redis:8.2
- **Start command:** `/bin/sh -c "rm -rf $RAILWAY_VOLUME_MOUNT_PATH/lost+found/ && exec docker-entrypoint.sh redis-server --requirepass $REDIS_PASSWORD --save 60 1 --dir $RAILWAY_VOLUME_MOUNT_PATH"`

## Buckets

- **ctfd-uploads**

## Documentation

# Deploy and Host CTFd on Railway

CTFd is the capture-the-flag framework most security competitions are built on. It gives you a challenge board grouped by category, static and regex flag checking, purchasable hints, file attachments, a live scoreboard, user or team modes, and an admin panel for writing challenges and freezing the board before the finish. University clubs, corporate red teams and conference villages all run it. Self-host CTFd when the event data — flags, submissions, accounts — should stay on infrastructure you control.

Deploy CTFd on Railway and this template wires up the production shape: a **ctfd** web service running gunicorn behind Railway's TLS edge, a managed **MySQL** database for challenges, users and submissions, a managed **Redis** instance serving as both cache and session store, and a **ctfd-uploads** bucket for attachments. Players hit the public domain, CTFd reads and writes MySQL over the private network, sessions live in Redis so the web tier keeps no local state, and every file attached to a challenge is written to the bucket and handed back through a signed, time-limited link.

![Diagram of the CTFd, MySQL and Redis services on Railway](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789282599/ctfd-architecture.webp)

## Getting Started with CTFd on Railway

Before clicking Deploy, set `PRESET_ADMIN_EMAIL` and `PRESET_ADMIN_PASSWORD` — the credentials for your organiser account. The template creates that administrator during the first boot, before CTFd serves any traffic, so the usual open setup wizard never appears on your public URL and nobody can claim the admin seat first. Open the generated domain and sign in from **Login**. In the **Admin Panel**, use **Config** to set the event name and its start and end times, then go to **Challenges** and press the plus button: give it a name, a category such as `Web` or `Crypto`, a point value, a description, and a static flag on the **Flags** tab. Attach a file on the **Files** tab — that upload confirms the bucket is wired up, since CTFd stores it there rather than on disk. Set the state to **Visible**, open the site in a private window, register a player account and submit the flag; a correct answer turns the card green and the **Scoreboard** updates at once.

![CTFd challenge board with solved Crypto and Web challenges](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789282601/ctfd-challenge-board.webp)

![Open CTFd challenge showing its attachment and flag box](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789282603/ctfd-challenge-detail.webp)

![CTFd scoreboard graphing four competitors and their scores](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789282604/ctfd-scoreboard.webp)

## About Hosting CTFd

CTFd is a Flask application that turns a set of puzzles into a scored competition. Organisers write challenges in the admin panel; players register, submit flags and climb a board everyone can watch. The event data — flags, solve times, the participant list — is sensitive until the competition ends, which is why self-hosting is the norm.

Key features:

- Standard and dynamic-scoring challenges, whose value decays as more teams solve them
- Static, case-insensitive and regex flags, with per-challenge attempt limits
- Point-costed hints, and attachments served through expiring links
- Users or teams mode, a freeze time that hides late solves, and brackets for separate fields
- A REST API, plugin and theme APIs, and full event export and import

The architecture splits across four pieces. **ctfd** runs gunicorn with gevent workers, so long-polling notifications do not block requests. **MySQL** is the system of record. **Redis** is both cache and session store, which keeps the web tier stateless and notifications consistent across workers. **ctfd-uploads** holds every challenge file, keeping large attachments off the container. Registration visibility, a registration code and challenge visibility are all set in **Config → Visibility**.

## Why Deploy CTFd on Railway

Railway removes the infrastructure work between you and an event:

- MySQL, Redis and object storage are provisioned and connected for you
- A TLS domain is issued automatically, with no certificate to renew
- The administrator is created at boot, so no setup wizard is ever exposed
- Private networking keeps the database and cache off the public internet
- Logs, metrics and one-click rollbacks are built in for event week
- Scale the web service up for the competition and back down after

## Common Use Cases

- A university or club CTF, where members write challenges and a public scoreboard tracks the field
- Internal security training, using teams mode and brackets so engineering groups compete with peers
- Conference and hiring events, where a short jeopardy board doubles as a technical screen
- A permanent practice range, with challenges added over time and the scoreboard kept private

## Dependencies for CTFd

- **ctfd** — [gridalpha/ctfd-railway](https://github.com/gridalpha/ctfd-railway), a thin layer over the official [`ctfd/ctfd`](https://hub.docker.com/r/ctfd/ctfd) image that creates the first administrator before the app listens and forces the `Secure` flag on session cookies
- **MySQL** — managed MySQL, the database for challenges, users, submissions and configuration
- **Redis** — managed Redis, CTFd's cache and server-side session store
- **ctfd-uploads** — object storage for challenge attachments

### Environment Variables Reference

| Variable | Purpose |
|---|---|
| `PRESET_ADMIN_EMAIL` | Email for the administrator account created on first boot |
| `PRESET_ADMIN_PASSWORD` | Password for that account; a valid login until you change it |
| `PRESET_ADMIN_NAME` | Administrator display name, `admin` by default |
| `SECRET_KEY` | Signs session cookies — changing it logs everyone out |
| `CTF_NAME` | Event name, written on first boot; later edited in Admin → Config |
| `CTF_USER_MODE` | `users` or `teams`, applied on first boot only |
| `WORKERS` | gunicorn worker processes, `4` by default |
| `REVERSE_PROXY` | `2,1,1,1,0`, so CTFd records the real client IP behind the edge |
| `UPLOAD_PROVIDER` | `s3`, sending challenge files to the bucket instead of local disk |
| `MAIL_*` | Optional SMTP, needed only for password resets and email verification |

### Deployment Dependencies

- Source repository: [gridalpha/ctfd-railway](https://github.com/gridalpha/ctfd-railway)
- Upstream: [CTFd/CTFd](https://github.com/CTFd/CTFd) · image [`ctfd/ctfd`](https://hub.docker.com/r/ctfd/ctfd) · docs [docs.ctfd.io](https://docs.ctfd.io)

## Hardware Requirements for Self-Hosting CTFd

| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 1 vCPU | 2–4 vCPU during a live event |
| RAM | 512 MB | 2 GB for the web service |
| Storage | Managed by Railway | Attachments live in object storage, not on a disk |
| Runtime | Python 3.11, MySQL 8+, Redis | Provided by the template |

A quiet practice instance is fine on the minimum. CTF traffic is spiky — the scoreboard gets hammered in the last hour — so raise `WORKERS` and the web service's memory before a big event, not during one.

## Self-Hosting CTFd

The quickest local run uses the official image with SQLite — fine for drafting challenges, not for an event:

```
docker run -p 8000:8000 -e SECRET_KEY=change-me ctfd/ctfd:latest
```

For anything real, point it at MySQL and Redis. Upstream ships a compose file; the essential wiring:

```
services:
  ctfd:
    image: ctfd/ctfd:latest
    ports: ["8000:8000"]
    environment:
      - SECRET_KEY=change-me
      - DATABASE_URL=mysql+pymysql://ctfd:ctfd@db/ctfd
      - REDIS_URL=redis://cache:6379
    depends_on: [db, cache]
  db:
    image: mariadb:10.11
  cache:
    image: redis:8
```

You are then left to issue certificates, decide where uploads live, back up MySQL and reach the setup wizard before anyone else. The Railway template handles all four.

## How Much Does CTFd Cost to Self-Host?

CTFd is open source under the Apache 2.0 licence, so the software is free and there is no seat or event fee for the version this template deploys. The project also sells a hosted service and an enterprise edition at ctfd.io; nothing here depends on either. On Railway you pay only for the compute, memory and storage the four services use — for a small event, a few dollars for the weekend.

## FAQ

**What is CTFd?**
CTFd is an open-source capture-the-flag platform. It hosts challenges, checks flags, awards points and runs the scoreboard for a security competition.

**What does this Railway template deploy?**
A CTFd web service, managed MySQL, managed Redis and an object storage bucket for attachments, connected over Railway's private network with a TLS domain on the web service only.

**Why does the template include MySQL and Redis?**
MySQL is CTFd's system of record for challenges, accounts and submissions. Redis is both its cache and its session store, which keeps the web tier free of local state and notifications consistent across workers.

**How do I log in to self-hosted CTFd the first time?**
Use the email and password you set in `PRESET_ADMIN_EMAIL` and `PRESET_ADMIN_PASSWORD`. That account is created on the first boot, so there is no setup wizard to race and no default password to change.

**Do I need SMTP to run CTFd on Railway?**
No. Registration, flag submission and the scoreboard all work without a mail server. Set the `MAIL_*` variables only for password resets or email verification.

**Can I scale CTFd for a large competition?**
Yes. Sessions are in Redis and attachments in object storage, so the web service holds no local state — raise `WORKERS` or add replicas and every worker sees the same sessions and files.



## Similar templates

- [Rocky Linux](https://railway.com/deploy/rocky-linux) — Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀
- [Foundry Virtual Tabletop](https://railway.com/deploy/X5tR6G) — A Self-Hosted & Modern Roleplaying Platform
- [Letta Code Remote](https://railway.com/deploy/letta-code-remote) — Run a Letta Code agent 24/7. No inbound ports, just deploy.

Open this page in a browser: https://railway.com/deploy/ctfd
