Running a successful CTF

A start-to-finish guide to organizing, running, and wrapping up a capture-the-flag competition.

Edit this page View Markdown

Running a CTF means coordinating organizers and challenge authors while keeping the infrastructure stable and players informed. These guides cover the work from early planning through teardown, including mistakes that tend to repeat.

Prerequisites

Set the event’s structure early, including organizer roles, deadlines, CTFtime, rules, sponsors, and player communication.

Challenge design

Plan a balanced challenge set, package services safely, and test everything before the event begins.

Setting up rCTF

Take a fresh server through rCTF installation, TLS and firewall setup, and the first admin account.

Deploying challenges

Deploy shared or per-team services safely, connect admin bots, and keep challenge metadata in sync.

During the CTF

Monitor the event, cover organizer shifts, and respond when challenges or infrastructure break.

After the CTF

Publish the final results, distribute prizes, collect writeups and feedback, archive the event, and dismantle its infrastructure.

Common mistakes

Learn from recurring mistakes in event planning, challenge design, infrastructure, and on-site operations.

Prerequisites

Organizational structure, timeline, CTFtime registration, sponsorships, rules, and communication setup for your CTF.

Edit this page View Markdown

Before building challenges or deploying infrastructure, decide who is responsible for each part of the event, set a schedule, and establish how participants will get information and support.

Organizational structure

A small team can run a strong event, but every important job needs a clear owner.

  • Organizers own the schedule, final decisions, sponsor relationships, merchandise, and prizes.
  • Challenge authors build challenges and playtest one another’s work.
  • Infrastructure leads deploy the platform and challenges, monitor them during the event, and respond to security or capacity problems. For a long event, rotate this responsibility so someone with access is always available.
  • Support team members answer participant tickets and bring challenge or infrastructure problems to the right person.
Tip (Team size)

Five to ten organizers can usually cover a 24 to 48 hour CTF with 20 to 30 challenges. People can hold more than one role, but plan shift coverage before assuming a small team is enough.

Timeline

Start at least two or three months before the event. One workable schedule is:

  • Three months out, choose the date, outline the challenges, contact sponsors, and start the CTFtime registration.
  • Two months out, begin writing challenges.
  • Two weeks out, freeze new challenge ideas and focus on playtesting, revisions, platform setup, and infrastructure testing.
  • One week out, finalize every challenge, open registration, announce the event, and prepare Discord for participants.

CTFtime

CTFtime is where most teams discover upcoming CTFs and track their results. Check its calendar before choosing a date, then list the event early enough for the registration to be approved.

Note (Regarding conflicts)

Avoid overlapping major events, especially qualifiers for onsite finals and competitions with large prizes.

Listing a CTF takes three steps:

  1. Create a team. The same team can compete in and organize events.
  2. Register the event series, such as “My CTF.” The CTFtime team reviews this manually, so submit it well before the event.
  3. Add an edition for the specific competition, such as “My CTF 2026.”

After the event, upload the final scoreboard so teams receive CTFtime ranking points. Competitors also vote on the event’s weight, which can increase as the event establishes a history.

Note

CTFtime also publishes resources for event organizers.

Homepage

The homepage is the first thing participants see. It should cover event timing, rules, prizes, sponsors, and anything else worth surfacing up front.

Rules

Publish the rules before registration opens. A reasonable starting point is:

  • There is no team limit.*
  • Do not attack or interfere with other teams, the competition infrastructure, or other competitors.
  • Do not share flags, solutions, or hints with other teams.
  • Do not discuss challenges with other teams until the CTF is over.
  • Do not attempt to brute-force flags or flag submission endpoints.
  • Do not use automated tools or scanners against competition infrastructure.
  • Each player may only be a member of one team. Do not register multiple accounts.
  • Tiebreakers are resolved by the timestamp of the last solve that brought the team to their final score.
  • Violating any of the above rules may result in disqualification at the discretion of the organizers.
Note

*Team size limits are difficult to enforce. rCTF gives each team one shared token, so the platform cannot count individual members.

Sponsorships and prizes

Prizes are optional, though many events reward the top three teams or strong post-event writeups. Sponsors often cover those costs.

When looking for sponsors:

  • Start two or three months ahead. Approval can take time.
  • Prepare a short proposal with the event audience, expected participation, sponsor benefits, and a few funding levels.
  • Begin with companies and organizations that already support security events or have worked with your team.

Possible starting points include:

Common sponsor benefits include:

  • Logo placement on the CTF homepage and merchandise, if applicable
  • Shoutouts on social media and announcement channels
  • A challenge written by the sponsor or built around one of its tools, when that fits the event
  • A talk or booth at an onsite event

Communication

Discord setup

Most CTFs use Discord as the main communication channel for announcements, support, and post-event discussion.

A typical Discord server configuration for a CTF includes the following channels:

Channel Purpose
#rules Discord and CTF-specific rules
#announcements Registration status, CTF start/end times, challenge updates, and feedback requests
#first-bloods Recognition of teams who achieved the first solve on a challenge
#support Support ticket creation for issues such as broken challenges (e.g., via Tickets v2)
#general General discussion
#looking-for-team Team formation for participants seeking teammates
#web/#rev/#pwn/… Category-specific discussion channels, if desired

Keep support in one place instead of answering direct messages. A ticket channel gives the team a shared record of platform problems, broken challenges, incorrect flags, and hint requests.

Hints

Caution (No-hint policy)

Do not give private hints to individual teams. Even a well-intended nudge can look unfair in a competition.

When a participant reports a challenge problem, confirm that the live service and reference solution still work. Do not reveal anything about the intended solution.

If you need to publish a hint or clarification, follow the hint policy.

Challenge design

Guidelines for planning, authoring, and testing CTF challenges, including difficulty distribution, flag formats, and quality assurance.

Edit this page View Markdown

Set the event format before authors begin. For a typical online jeopardy CTF:

  • Run for 24 or 48 hours, usually over a weekend.

  • Use recognizable CTF categories.

  • Aim for two to six challenges per category and avoid leaving one category noticeably thinner than the others.

  • Include a real range of difficulty. One or two easy, two or three medium, and one or two hard challenges per category is a useful starting point.

Challenge authoring

Good challenges are what make a CTF worth playing. Keep these guidelines in mind during development.

General principles

  • Write original challenges. Reused problems are often searchable or recognizable.

  • Give solvers enough information to make reasoned progress. If the only way forward is to guess what the author had in mind or try arbitrary inputs, the challenge is unclear rather than difficult. An unsolved challenge is usually a reason to revisit the design.

  • Make sure the intended solution fits the length of the event.

  • Do not use fake clues or red herrings.

  • Give each challenge a technical idea worth learning or practicing.

Challenge components

Each challenge should include the following:

Component Comments Necessity
Title A memorable name. Prefer something distinctive over keygen, pwnme, warmup, or crackme. Required
Description Context and necessary information for the challenge. Descriptions are often themed and playful. Required
Hints Usually released during the event when an unsolved challenge needs clarification. See Hint policy. Optional, uncommon
Category The category that best matches the main technique. Required
Author The challenge author. Meta challenges, such as survey or sanity checks, should be authored by “Team” or some other umbrella name. Optional, common
Tags Extra labels for bounties, challenge series, or secondary categories. Optional, common
Difficulty A rough rating. Because difficulty is subjective, many events omit it. Optional, uncommon
Flag The solution in the established format. Required
Attachments Downloadable files, packaged cleanly and given stable names instead of names such as dist(4).tar.gz. Optional, common
Remote Connection details for a hosted challenge. Use rCTF’s [!CONNECTION] callout. Optional, common
Instancer Per-team instance configuration. Keep it private during the event and publish it with the challenge afterward. Optional, uncommon
Solution A working solve script or writeup for playtesting, support, and post-event release. Optional, recommended

Dockerization

Package remote challenges as containers so the same build can be tested, deployed, and reproduced after the event.

Best practices

  • Keep one challenge per container so each challenge stays self-contained. If a challenge requires multiple services (e.g., a web app and a database), use Docker Compose.
  • Reach for minimal base images like alpine or debian-slim to reduce build times and attack surface.
  • Build artifacts separately with multi-stage builds, compiling binaries in one stage and copying only the final artifact into the runtime image.
  • Run challenge services as non-root users inside the container to limit the impact of container escapes.
  • Use a read-only filesystem where possible by mounting the root filesystem as read-only (readOnly: true or --read-only) and using a tmpfs for writable directories.

Security defaults

When deploying challenges, apply the following security settings:

  • cap_drop: ALL drops all Linux capabilities.
  • no-new-privileges: true prevents privilege escalation.
  • Memory and PID limits prevent resource exhaustion (e.g., fork bombs).
  • Network isolation uses internal networks for multi-container challenges where services should not be directly reachable.

Example Dockerfile

Dockerfile
FROM python:3.12-slim
RUN useradd -m ctf
WORKDIR /home/ctf
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py flag.txt ./
RUN chmod 444 flag.txt
USER ctf
EXPOSE 5000
CMD ["python", "app.py"]

Using with rCTF instancer

With the rCTF Docker instancer, describe the challenge services in instancerConfig. The instancer creates the containers and network for each team, then removes them when the instance expires.

Pre-event checklist

Before the event, verify the following for each challenge:

  • The challenge is solvable with the provided materials. Where it fits, ship a single solve.py that outputs the flag.
  • The challenge has been playtested internally. Two playtesters per challenge is the safer bet.
  • The flag is correct and matches the expected format.
  • The files are correctly packaged and downloadable. Double-check that the files are the right versions, since authors sometimes update source code without re-packaging the distribution zip.
  • The remote services are accessible and stable.
  • The Docker containers build and run correctly.
  • The solution or writeup is documented internally and accessible to the support team.
Tip (Recommended reading)

The CTF Design Guideline has more advice on writing and reviewing challenges.

Setting up a CTF platform

Complete guide to deploying rCTF, configuring Nginx with TLS, and creating admin accounts.

Edit this page View Markdown

This walkthrough installs rCTF on an Ubuntu VPS, puts Nginx in front of it, and enables TLS through Cloudflare or Certbot.

Prerequisites

You will need:

  • A domain name, such as example.com
  • An Ubuntu 24.04 VPS with at least 2 CPU cores and 4 GiB of memory

The TLS steps cover either a proxied Cloudflare hostname or a certificate issued with Certbot.

Note

Replace example.com and 123.123.123.123 throughout the guide with your domain and VPS address.

Installing rCTF

  1. SSH into your VPS

    Connect to the new server:

    Terminal window
    ssh root@123.123.123.123

    Run the remaining commands on the VPS as root.

  2. Run the installation script

    Run the installer:

    Terminal window
    curl -L https://get.rctf.osec.io | sh

    Answer y when asked whether to start rCTF. The installer puts the deployment and configuration under /opt/rctf/.

  3. Configure your instance

    Edit the files in /opt/rctf/rctf.d/. At minimum, review the following settings in the configuration reference:

    • CTF name, description, image, public origin, homepage, and start and end times
    • Email provider, if registration should verify addresses. Some providers must approve the account before it can send mail, so configure this early.
    • proxy.cloudflare if behind Cloudflare
    • An object storage provider, if uploads should not be stored on the VPS
  4. Restart rCTF after configuration changes

    Recreate the service after changing configuration:

    Terminal window
    cd /opt/rctf && docker compose up -d --force-recreate

Setting up the web server

rCTF listens only on the VPS loopback address. Nginx terminates TLS and forwards public traffic to it. Cloudflare can sit in front of Nginx for proxying and DDoS protection.

  1. Configure Cloudflare DNS

    Under DNS > Records, create a proxied A record for 123.123.123.123. Then set SSL/TLS encryption to Full (strict).

  2. Create origin certificate

    Under SSL/TLS > Origin Server, create a certificate for ctf.example.com. Save the certificate as /etc/ssl/cf_fullchain.pem and its private key as /etc/ssl/cf_privkey.pem.

  3. Configure UFW firewall

    Allow SSH and Cloudflare traffic, then enable UFW:

    Terminal window
    ufw allow 22
    for cfip in `curl -sw '\n' https://www.cloudflare.com/ips-v{4,6}`; do ufw allow proto tcp from $cfip comment 'Cloudflare IP'; done
    ufw enable # answer `y` when prompted regarding enabling firewall
  4. Install and configure Nginx

    Install Nginx with apt update && apt install -y nginx, then create /etc/nginx/sites-available/rctf.conf. Update the marked hostname.

    /etc/nginx/sites-available/rctf.conf
    # Redirect HTTP -> HTTPS
    server {
    listen 80;
    server_name ctf.example.com; # TODO: update me to CTF platform domain name
    return 301 https://$host$request_uri;
    }
    map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
    }
    server {
    listen 443 ssl;
    server_name ctf.example.com; # TODO: update me to CTF platform domain name
    ssl_certificate /etc/ssl/cf_fullchain.pem;
    ssl_certificate_key /etc/ssl/cf_privkey.pem;
    # NOTE: It is *very* important to only whitelist Cloudflare IPs for the webserver (which we'll do in the following steps),
    # as this otherwise lets you spoof the IP.
    set_real_ip_from 0.0.0.0/0;
    real_ip_header CF-Connecting-IP;
    location / {
    # Uncomment the following if you want to only allow yourself to access the platform for now.
    #allow [your IP];
    #deny all;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_pass http://127.0.0.1:8080/;
    proxy_buffering off;
    }
    }
  5. Enable the configuration

    Create a symlink so Nginx loads the configuration, then apply it:

    Terminal window
    ln -s /etc/nginx/sites-available/rctf.conf /etc/nginx/sites-enabled/rctf.conf
    nginx -t && nginx -s reload

    Use the same command after later Nginx changes. rCTF should now be available at https://ctf.example.com.

Creating an admin account

  1. Register a new account

    Register a new account in rCTF.

  2. Update permissions via database

    On the VPS, open PostgreSQL and grant the account admin permissions:

    Terminal window
    docker exec -it rctf-postgres-1 bash
    Inside the postgres container
    psql -U rctf
    UPDATE users SET perms = 3 WHERE email = 'my.email@example.com';
  3. Verify admin access

    Open https://ctf.example.com/admin/challs and confirm that the challenge editor loads.

    Warning

    If the editor is missing, check that the SQL update matched the account’s email and completed successfully.

Deploying challenges

Guide to deploying CTF challenges including shared remotes, instanced remotes, kCTF, Docker, and admin bots.

Edit this page View Markdown

Hosted challenges use either a shared remote, which every team connects to, or an instanced remote, which gives each team a separate environment.

Tip

Every hosted challenge should include a local setup, such as a Docker Compose file. Authors need it to reproduce deployment problems and test the reference solution against the same build.

Admin bots should run separately from challenge services. This keeps browser traffic outside the challenge network and lets bot workers scale independently.

Shared remote

In a shared remote, every team connects to the same service. Use one when solves do not alter shared state, or when each connection runs inside its own sandbox. A cryptographic oracle is a common example.

kCTF runs shared challenges on GCP with autoscaling, health checks, automatic restarts, and per-connection sandboxing. A dedicated VPS running Docker is simpler, but you must provide the isolation and resource limits yourself.

Warning (Wrap shared remotes in a jail)

Any shared remote where the intended solve gives a competitor code execution must run the vulnerable process inside a per-connection sandbox. Concretely:

  • pwn.red/jail runs each connection in a separate nsjail with a read-only root filesystem, no network, dropped capabilities, and CPU, memory, and time limits. It is a practical default for shared pwn and RCE services.
  • nsjail is the lower-level building block both kCTF and pwn.red/jail wrap. kCTF runs every challenge inside an nsjail by default. If you’re not on kCTF and not using pwn.red/jail, configure nsjail yourself.

Use a jail whenever the intended solution provides code execution. Without per-connection isolation, one solver can replace the binary, leave a backdoor in /tmp/, or stop the service for everyone.

kCTF

kCTF has its own documentation, so this section focuses on the things that aren’t immediately obvious, especially for first-time users:

  • Set the challenge domain with --domain-name when you run kctf cluster create. To use *.example.com, use Google nameservers for the domain. To use *.subdomain.example.com, add NS records that delegate the subdomain to Google.
  • By default, the created nodes are spot (preemptible) instances. This means the nodes rotate every 24 hours, with brief downtime each time. To avoid this, resize the cluster without the spot instance flag (e.g., kctf cluster resize --min-nodes 1 --max-nodes 3 --num-nodes 1 --machine-type n2-standard-4), at slightly higher cost.
  • If a health check fails, the challenge restarts automatically, which can look like a connectivity issue from the outside. Run kctf chal status to see what’s actually going on.
  • Watch your GCP project’s quotas. The dashboard shows which limits have been hit or are getting close. Deploying a lot of challenges can fail because of insufficient IP addresses or other quota constraints. Setting up kCTF early is a practical way to verify your quotas are sufficient, and it may improve your account’s standing for automatic quota increase approvals.
  • Some challenges deployed to kCTF need extra changes because of the security hardening. Standing up kCTF early lets you catch and fix these before the competition starts. The Kubernetes Pods documentation covers the configurable options for kCTF challenge instances (for example, additional volumes, since the file system is read-only by default).

Docker

For a smaller setup, run Docker on one or more dedicated hosts. Do not put intentionally vulnerable challenges on the server that holds the rCTF database or participant data. Challenge traffic and a container escape should not be able to take down or expose the platform.

Docker Compose is enough for a small set of shared services, with each challenge on its own port. A container alone is not a safe boundary for an RCE challenge, especially if it has extra privileges. Put the vulnerable process inside pwn.red/jail or nsjail and set storage and process limits.

Caution

Block access to cloud metadata services such as http://169.254.169.254. They can expose credentials for the cloud account.

VM

Another option is to give each challenge instance its own VM or VPS. This costs more, but a compromised challenge is less likely to affect the rest of the event through disk exhaustion, a container escape, or a host failure. Block access to the cloud metadata service on these hosts as well. Choose the level of isolation based on the risks and resource needs of each challenge.

Instanced remote

An instanced remote gives each team its own environment. Use it when a solve changes persistent state, writes files, or gives broad code execution that cannot be safely reset after every connection.

rCTF provides two instancer backends:

  • Docker instancer is a bundled Python FastAPI service that runs on a standalone Docker host alongside Traefik and Redis. It’s lightweight and a good fit for small-to-medium events.
  • Kubernetes instancer runs each team instance in its own namespace with network policies and Traefik routing. It includes a Terraform deployment for GKE and is intended for events that need several challenge nodes.

The instancer overview explains the shared instancerConfig fields, participant controls, and admin UI.

Existing systems such as Klodd can still integrate with rCTF.

Warning

Instances can be deployed before the CTF begins. Be careful when deploying challenges in advance if their names are predictable.

Admin bot

The rCTF admin bot runs trusted TypeScript handlers in Chrome or Firefox. It validates participant input, queues browser visits, and returns logs for each job. Run the worker separately from the challenge so browser capacity can scale without exposing the worker service publicly.

Keeping challenges in sync

Deploy source, attachments, images, and instancerConfig from the same revision. Updating them separately makes it easy to publish a binary that no longer matches the remote service or reference solution.

Tip (Use Konata + CI as the single source of truth)

Commit each challenge’s kona.yml beside its source and deploy it with Konata from CI. The Konata GitHub Action can build and push the image, regenerate attachments, update rCTF, and roll out the Kubernetes deployment from one commit.

Generate attachments from the challenge build and reference the output through attachments.files. Konata archives that output on each sync, while rCTF skips the upload when its contents have not changed.

The SekaiCTF 2026 challenge repository shows this workflow with Konata, Kubernetes, instanced challenges, and dynamic scoring.

During the CTF

Operational procedures for monitoring, incident response, and issue resolution during an active CTF competition.

Edit this page View Markdown

During the event, the team needs to watch the platform, respond to failures, and make consistent decisions about broken challenges and hints.

Monitoring

Watch the services participants depend on, and make sure alerts reach someone who can act:

  • For the rCTF API, watch error rates, latency, CPU, and memory.
  • For PostgreSQL, watch connection usage, slow queries, and disk space. A steadily rising connection count can point to a leak.
  • For Redis, watch memory and connected clients. If Redis fails, leaderboard caching and rate limiting are affected.
  • For challenge containers, watch CPU, memory, network traffic, restarts, and disk use.
  • For the reverse proxy, watch request volume, 5xx responses, and latency. If Cloudflare is in front, include its traffic and DDoS dashboards.
Tip

Use an external uptime monitor such as UptimeRobot or Hetrix Tools so a total outage still produces an alert.

Staffing and shift coverage

For events longer than 12 hours, rotate shifts and keep someone with infrastructure access on call at all times. When a challenge author is offline, the support team should still have the reference solution and enough notes to investigate tickets.

Incident response

Decide who can change infrastructure, challenges, and the event schedule before an incident happens.

Challenge outages

If a challenge becomes unavailable, the response depends on the deployment method:

  • For Docker containers, check container status with docker ps and logs with docker logs <container>. Restart with docker compose up -d --force-recreate <service> if needed.
  • For instancer-managed challenges, verify the instancer service is running and reachable. Check the instancer logs for errors. If individual instances are failing, the issue may be with the challenge image itself rather than the instancer.
  • For static challenges (no remote), verify that challenge files are accessible through the upload provider. If using S3/GCS, check bucket permissions and CDN status.

After the fix is tested, announce that the challenge is available again and say whether its files or behavior changed.

Platform issues

If the rCTF platform itself becomes unresponsive, work through the following steps:

  1. Check API server logs for crash traces, out-of-memory errors, or unhandled exceptions. If using Docker, run docker logs rctf-rctf-1.
  2. Verify database connectivity by ensuring PostgreSQL is running and accepting connections. Check for connection pool exhaustion or long-running queries with docker exec -it rctf-postgres-1 psql -U rctf -c "SELECT count(*) FROM pg_stat_activity;".
  3. Verify Redis connectivity by ensuring Redis is running. Run docker exec -it rctf-redis-1 redis-cli --pass "$RCTF_REDIS_PASSWORD" ping.
  4. Restart the rCTF service if the issue is not immediately identifiable. Run cd /opt/rctf && docker compose restart rctf.
  5. Check resource usage to verify the host has sufficient CPU, memory, and disk space. High leaderboard update frequency or large team counts can increase resource usage.

If an outage materially reduces playing time, extend the event and announce the new end time clearly.

Security incidents

For security incidents:

  • For suspected flag sharing, preserve submission times, IP records, and the shared values before deciding on a penalty.
  • During a denial-of-service attack, take the affected challenge offline if necessary, preserve logs, and add rate limits or blocks before restoring it.
  • If a flag is leaked, rotate it in rCTF and every affected service immediately, then announce any file or service changes participants need to know about.

Challenge issues

Broken challenges

If a challenge is reported as broken, verify it first. Have the author run the intended solution against the live deployment. If it’s actually broken, work through the following steps:

  1. Post an announcement acknowledging the issue and that a fix is in progress.
  2. Apply the fix to the challenge.
  3. Test the fix before redeploying.
  4. Announce when the challenge is back online.
Warning (Mid-competition modifications)

Be very careful when modifying challenges during the competition, since changes can invalidate progress made by participants. If any team has already solved the challenge, consider it solvable and avoid further modifications unless the challenge is fundamentally broken.

Unintended solutions

Accept unintended solutions unless they rely on attacking shared infrastructure or another team. Finding a valid path the author missed is part of solving the challenge.

If a deployment mistake exposes the flag directly, fix the original only when necessary for fairness or stability. A separate patched variant can preserve the intended idea, but its release may reveal what was wrong with the first version.

Attachment updates

If challenge attachments need updates (e.g., typo fixes or missing files), update the attachment and post a clear announcement spelling out what changed. Avoid changes that would invalidate existing progress.

Hint policy

Do not give private hints. If an unsolved challenge needs help, release the same information to everyone:

  1. Around the halfway point of the competition, ask interested participants to open support tickets describing their current progress on unsolved challenges.
  2. The challenge author reviews the submitted progress and decides whether a hint is warranted and what to provide.
  3. Any approved hints must go out publicly in the #announcements channel so all participants have equal access.
Caution (Post-solve hints)

Once a challenge has been solved, don’t release any more hints for it. Doing so would be unfair to teams that solved it without help.

Tip (Timing considerations)

Account for time zones when scheduling a hint. A release during one region’s night gives teams elsewhere more useful playing time with it.

Note (Beginner-friendly challenges)

Beginner challenges can allow more guidance when their purpose is teaching rather than ranking. State that policy in advance and apply it consistently.

Announcements

Keep communication with participants clear and timely throughout the competition. Always announce the following:

  • Competition start and end reminders
  • Challenge releases (if challenges are released in waves)
  • Challenge fixes or updates
  • Delays or extensions of the competition
  • Hint releases
  • Infrastructure issues and resolutions
  • Survey release
  • Writeups release

Keep announcements concise and informative. For time-related announcements, Discord’s timestamp formatting handles relative times across time zones.

After the CTF

Post-CTF tasks including uploading scoreboards to CTFtime, distributing prizes, collecting feedback, and tearing down infrastructure.

Edit this page View Markdown

After the event, submit the scoreboard, contact prize winners, collect feedback and writeups, publish an archive, and remove infrastructure that no longer needs to run.

Uploading scoreboard to CTFtime

Submitting the final scoreboard to CTFtime is what gives participating teams their ranking points. The procedure below exports the scoreboard in the format CTFtime expects.

  1. Update admin permissions

    The CTFtime export requires leaderboardRead (4). A full admin with permissions 63 already has it. Otherwise, grant the permission as described under Administration › Permissions:

    UPDATE users SET perms = 7 WHERE email = '[...]';
  2. Retrieve authentication token

    Log in to the CTF platform as the admin account and run the following snippet in the browser’s developer console:

    Browser console
    localStorage.getItem('token')

    Copy the returned authentication token, removing the surrounding quotation marks.

  3. Export the scoreboard

    Use the following command to retrieve the scoreboard export, replacing TOKEN with the authentication token from the previous step:

    Terminal window
    curl https://ctf.example.com/api/v1/integrations/ctftime/leaderboard \
    -H "Authorization: Bearer TOKEN"
  4. Submit to CTFtime

    Submit the command output through the CTFtime event dashboard.

    Note

    Point distribution can take around an hour for established events. For first-time events, points are awarded only after the one-week weight voting period ends.

Prize distribution

Contact winners through the email in the admin teams view, not through an unverified Discord account. The same view can filter teams by division for divisional prizes.

Feedback collection

A short post-event form can show what to keep and what to change. Ask about:

Category Topics to address
Infrastructure Platform stability, challenge availability, response times
Challenges Difficulty balance, category distribution, quality of problem design
Organization Communication clarity, support responsiveness, overall experience
Tip

Read the responses before planning the next edition, while the incidents and challenge discussions are still fresh.

Writeup collection

Writeups make the challenges useful after the scoreboard closes. To encourage them:

  • Announce a deadline for writeup submissions (typically one to two weeks after the event).
  • Offer incentives such as prizes for outstanding writeups or recognition in official channels.
  • Compile and share links to community writeups in the Discord server or on the event website.
  • Release official writeups for challenges that received few or no community solutions.

Archive the platform

Before decommissioning the platform, use the static export tool to preserve challenges, profiles, solves, and the final leaderboard. Host the resulting read-only site wherever you plan to keep permanent links and writeups.

Infrastructure teardown

Scale expensive infrastructure down soon after the event. If the budget allows, keep challenge services available for a few days so participants can finish writeups and verify solutions.

Cloud credits are not a reason to leave vulnerable or forgotten services running. Keep them only while someone is still monitoring them and no sensitive data or credentials remain exposed.

Warning (Teardown checklist)

Decommission the following resources so you don’t get hit with unexpected costs:

  • Publish the static archive (see Archiving) before deleting anything else.
  • Take down the CTF platform, and back up the database and configuration before deletion to simplify setup for future events.
  • Remove challenge infrastructure: kCTF clusters and any VPS instances used for shared remotes, plus the Docker instancer or Kubernetes instancer deployment (including the GKE cluster, Traefik, and the ACME wildcard secret).
  • Remove the admin bot worker(s) and any queue/scraper sidecars if you deployed an autoscaled fleet.
  • For cloud services, delete static IP addresses, DNS records, load balancers, and any remaining storage buckets or artifacts (including the S3/GCS upload bucket if you migrated off the local provider).
  • Rotate or revoke any API keys, tokens, or credentials used during the event. This covers rCTF’s tokenKey, the instancer authToken, the admin bot service token, captcha secrets, registry push credentials, and any deploy tokens, such as Konata’s RCTF_TOKEN.

Type to search.