Host a server

Run your own match.

A PeakRunner server is one small program: it runs the match, checks every move and shot, and talks to players over encrypted UDP. Download it, answer a few questions, and share the invite link. No domain name or certificate needed.

Overview

Quick start

  1. Download PeakRunner Server for your computer from the downloads (macOS on Apple Silicon, Windows x64 or Linux x64) and unpack it.
  2. Run peakrunner-server from a terminal (on Windows, double-click peakrunner-server.exe). It asks for a name, the mode and maps, how many players, bots, an optional password, the UDP port (7777), whether to announce to the public list, and whether to open the port on your router automatically (UPnP). Press Enter to accept each default.
  3. It saves your answers to server.json in your user settings folder and starts. Later runs start straight away; run with --setup to change them.
  4. Share the invite link it prints (peakrunner://join/…#sha256=…): players paste it into Direct server address or invite link in the game. If you announced, your server also appears under Community servers here and in the game's server list.
Server name [PeakRunner Community]: Garage CTF
Mode [1]:
Maps (numbers like 1,3, or all) [all]:
Max players (2-28) [16]: 12
UDP port [7777]:
Announce to the public server list? (yes/no) [yes]:

Players outside your network need UDP to reach your computer: the server tries UPnP if you let it, and says plainly whether the directory could reach it; if not, forward the UDP port on your router. An unlisted server contacts nothing; run it with --publicip to also print an invite link with your public address. The password, if any, is kept in server.json, readable only by your user account; PEAKRUNNER_MATCH_PASSWORD overrides it.

Requirements

You needDetails
A computermacOS on Apple Silicon, Windows x64 or Linux x64 (Debian 12 / glibc 2.36 or newer). Docker is an option on Linux. One CPU core and 256 MB of memory per server is plenty, even for 28 players (a full local test used under a millisecond of simulation per tick); the official servers idle at about 35 MB.
A public addressA public IPv4 address, or a home router that forwards the port to your machine.
An open UDP port7777 by default; any port works. Run several servers on one machine with one port each (for example 7777, 7778, …).
BandwidthAbout 120 kbit/s upload per player in a busy match (each player gets 20 compact updates a second): roughly 1 Mbit/s for 8 players, 3.5 Mbit/s for 28. Download is small.

Run it with Docker

For an always-on Linux host, the same program runs in Docker, configured only by environment variables (it never asks questions without a terminal). This is how the official servers run, with their own certificate; for a Docker server without one, mount a writable folder at PEAKRUNNER_SERVER_DIR to keep its generated key. A server that plays every map in Capture the Flag and Capture & Hold, then Team Deathmatch, and lists itself publicly:

services:
  match:
    image: peakrunner/server:<version>      # published with the source release
    command: [peakrunner-server]
    user: "10001:10001"
    environment:
      PEAKRUNNER_MATCH_NAME: Example Ridge
      PEAKRUNNER_LOCATION: Dallas, US
      PEAKRUNNER_MAX_PLAYERS: "16"
      PEAKRUNNER_MATCH_ROTATION: '[{"playlist":"ctf_cnh"},{"playlist":"team_deathmatch"}]'
      PEAKRUNNER_DIRECTORY_URL: https://dir.peakrunner.net/announce
      PEAKRUNNER_ADVERTISE_URL: quic://play.example.net:7777
    ports:
      - "7777:7777/udp"
    volumes:
      - ./certs:/run/peakrunner:ro             # fullchain.pem and privkey.pem
    restart: unless-stopped
    read_only: true
    cap_drop: [ALL]
    security_opt: [no-new-privileges:true]
    mem_limit: 1g                            # Ashveil, the heaviest map, holds about 60 MB
    cpus: 1.0
    healthcheck:
      test: [CMD, peakrunner-server, --healthcheck]
      interval: 15s

Start it with docker compose up -d, then open the port in your firewall, for example sudo ufw allow 7777/udp.

Only publish the UDP game port. Inside the container the server also answers a small health endpoint on TCP 8080 for the health check. Run with a server.json (outside Docker), it keeps that page on this computer only (127.0.0.1, TCP, at the game port's number), so there's nothing extra to firewall.

Settings

The setup questions cover the common ones. Every setting can also be set with an environment variable, which overrides server.json.

VariableMeaning
PEAKRUNNER_MATCH_NAMEServer name shown in lists, up to 64 characters. Names starting with "Springdale" (including the default) are kept for the official servers, so set your own.
PEAKRUNNER_MATCH_ROTATIONThe maps and modes to play, as JSON (see below). Without it the server plays one map in CTF.
PEAKRUNNER_ROTATION_ORDERin_order (default) plays the rotation as listed; random plays the same maps and modes in a new random order each time through. In server.json: "rotation_order": "random".
PEAKRUNNER_MATCH_MAPThe map when no rotation is set (default old-holler, Old Holler).
PEAKRUNNER_LOCATIONWhere the server is, shown in server lists so players can pick a nearby one: for example Dallas, US. Up to 40 letters, digits, spaces, commas, periods or hyphens.
PEAKRUNNER_MAX_PLAYERSHow many players the match holds, 2–28 (default 8). Teams are kept even.
PEAKRUNNER_BOTSFill the match with bots up to this many players (even number, at most the capacity; default 0). Each person who joins takes a bot's place, and a bot returns when they leave. Bots count as players in the server lists, marked as bots.
PEAKRUNNER_TIME_OF_DAY, PEAKRUNNER_WEATHEREach round's sky: default (the map's own look; Team Deathmatch rolls its own), random (again each round), or one of map, dawn, day, dusk, night and clear, overcast, rain, storm, snow, fog. Only the look changes. In server.json: time_of_day and weather.
PEAKRUNNER_MATCH_PASSWORDOptional. Players must enter it to join. Keep it out of files you share; an .env file next to the Compose file works well.
PEAKRUNNER_MAX_SPECTATORSSpectator places, 0–8 (default 8). Spectators take no player slot; a full match seats newcomers as spectators and puts them in when a slot opens.
PEAKRUNNER_SESSIONS_PER_IPPlayers from one address, 1–28 (default 4). Raise it for a LAN party behind one router.
PEAKRUNNER_SERVER_DIRWhere server.json and the generated key live (or --config-dir).
PEAKRUNNER_UDP_BINDAddress and port to listen on inside the container (default 0.0.0.0:7777).
PEAKRUNNER_TLS_CERT, PEAKRUNNER_TLS_KEYOptional certificate chain and key (see Your own certificate). Without them the server uses its generated key.
PEAKRUNNER_DIRECTORY_URLWhere to announce: https://dir.peakrunner.net/announce. Leave unset to stay unlisted.
PEAKRUNNER_ADVERTISE_URLThe address players use: quic://your-hostname-or-public-ip:port, with the port as seen from the internet. The setup questions fill this in for you.

Maps and rotation

The rotation is a JSON list. Each entry is either a playlist, which expands to every map that supports that mode, or one map in a mode. Entries play in order and then repeat; an empty server goes back to the start. Up to 32 entries, expanding to at most 64 matches.

[{"playlist":"ctf"},
 {"map":"reefbreak","mode":"team_deathmatch"},
 {"map":"highgoal","mode":"football"}]
PlaylistPlays
ctf_cnhEvery CTF map in CTF, then every Capture & Hold map in Capture & Hold
ctfEvery CTF map
capture_and_holdEvery map with Capture & Hold towers
team_deathmatchEvery map except the stadiums
rabbit, team_rabbitEvery map except the stadiums, in Rabbit or Team Rabbit
footballThe two stadiums
duelA duel server: a lobby, and one on one duels side by side on every map except the stadiums (below)
Map keyMapModes
old-hollerOld HollerCTF, C&H, TDM
tower-complexTower ComplexCTF, C&H, TDM
cairnholdCairnholdCTF, C&H, TDM
frostlineFrostlineCTF, C&H, TDM
dustreachDustreachCTF, C&H, TDM
ozarktic-blastOzarktic BlastCTF, C&H, TDM
reefbreakReefbreakCTF, C&H, TDM
ashveilAshveilCTF, C&H, TDM
longfieldLongfieldFootball
highgoalHighgoalFootball
tenfoldTenfold TowerA duel server's lobby (not in rotations; below)

Mode keys are ctf, capture_and_hold, team_deathmatch, rabbit, team_rabbit, football and duel, plus the modes of any mods you run. Some map keys are older working names; they stay so existing rotations keep working. The server refuses to start if the rotation names a map or mode it can't play, and says why.

A duel server

A rotation of only duel entries ([{"playlist":"duel"}], "Duels" in the setup, or Duel on the game's Host screen) makes a duel server. Everyone joins its lobby, Tenfold Tower, where the only weapon is a blade and nothing counts. From the duel board (the map-vote key, V) players challenge each other, or a bot, and watch duels being played. Each accepted challenge plays in an arena of its own, on a map drawn from the rotation, so many duels run at once: one life a round, first to 3 rounds, light armor's default kit. The capacity counts everyone: the lobby, the duelists and those away watching. The lobby is Tenfold Tower, and server bots spar with blades on its floors (two to a floor, from the ground up); each person who joins takes a bot's place.

Your own maps and mods

The server plays the maps that come with it. To run more, or to change how the game plays, put them in the content folder beside server.json (the setup creates it: on Linux ~/.config/PeakRunner/server/content, on a Mac ~/Library/Application Support/PeakRunner/server/content, on Windows %APPDATA%\PeakRunner\server\content):

  1. peakrunner-server --check reads that folder exactly as the server will and names every problem, rules scripts included.
  2. peakrunner-server --setup lists the mods it found, asks which to run, and offers their modes and your maps with the rest.
  3. Start it. The game's Find match shows your mods and how much joining downloads; players who lack something are asked, then get it straight from your server and keep it for next time.

Players only ever receive data. Rules scripts stay on your server. How to make mods →

Get listed

Answer yes to announcing (or set PEAKRUNNER_DIRECTORY_URL and PEAKRUNNER_ADVERTISE_URL), and the server announces itself every 20 seconds. Before listing it, the directory checks that:

Listings show the player count and capacity, and disappear about a minute after the announcements stop. Each address can list up to 4 servers. Your server then shows under Community servers on this site and in every player's Find match list, with its map, mode and player count. Servers with a certificate from a public authority are listed like the official ones.

Behind a home router, forward the UDP port to the machine running the server (or let UPnP do it). The server reports whether the directory could reach it.

Your own certificate (optional)

You don't need one: the generated key is checked just as strictly. A certificate from a public authority for a hostname is useful if you want players to join by name (quic://play.example.net:7777) or already run one. Players' games then check it against the usual public root authorities and the hostname, exactly as a browser does.

Any ACME client works. With certbot and a DNS provider plugin (no web server needed), for example Cloudflare:

sudo certbot certonly --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  -d play.example.net

The server reads the full chain and the private key in PEM form: by default /run/peakrunner/fullchain.pem and /run/peakrunner/privkey.pem. The key must be readable by the server's user and nobody else.

The certificate is loaded when the server starts, so restart it after each renewal (a certbot deploy hook can do this). A restart briefly disconnects players; they can rejoin straight away.

Operate it

LAN and friends

For a LAN party or a private VPN such as Tailscale there is a lighter LAN server. It needs no certificate or hostname, but it only listens on private addresses, and its traffic is not encrypted, so it refuses to listen on a public address.

peakrunner-lan-server --bind 192.168.1.20 --port 7781 --name "Basement" --map reefbreak --players 12

Players join by typing the address and port (for example 192.168.1.20:7781) as the direct server address. Set PEAKRUNNER_MATCH_PASSWORD to require a password. The LAN server plays one map: CTF, or Football on a stadium.

For quick games you don't need a server at all: Start match in the game runs a local match with bots.