Start here

How it works

An Ankor setup has three sides:

  • a controller, which organises matches
  • game nodes, which run them
  • the players who join.

The moving parts

Controller

ankor.sh or your own

Lobbies, teams and maps; statistics built from uploaded events.

Game nodea Linux host with a node token

Agent

Starts and stops game servers and keeps their events.

CS2 installation

One copy, shared by every slot.

Slot s1game 27015 · GOTV 27020

Match

CS2 and the Ankor plugin

allocation
5b1e2c7a-8d4f-…
login token
7K3Q9XW2M8RT…
match id
0199a0b4-5c00-…
Slot s2game 27016 · GOTV 27021

Server without a match

CS2, the node's plugins and cfgs

allocation
9c04e1d2-3a7b-…
login token
Q4T8N2WX7C1R…

Players

In CS2.

Terminology

Controller

Organises matches and decides what runs where. Nodes never start a server on their own.

A controller is anything that serves the agent’s socket and its HTTP endpoints. It is typically a web service, such as ankor.sh or one you build against the protocol. The controller keys in agent.yml hold the controller’s address.

Game node

A Linux host you control, with a node token that identifies it to the controller.

Agent

The one long-running service on a node. It runs in a container, ankor-agent, beside the game servers. It dials the controller and runs the node’s game servers and CS2 installation through Docker.

Slots and game servers

A slot is room for one game server. It has an id, a game port, a GOTV port and a login token.

A game server, or server, is the CS2 process that a launch starts in a slot. Each server runs in a container of its own. The controller picks an allocation id for each launch, and the node names the server by it.

A match is a server launched with a match configuration. Ankor’s plugin runs it for one map.

Plugin

Ankor’s SwiftlyS2 plugin, in every game server. What it does depends on the launch. It talks only to the agent on its node, on port 4500. The node’s SwiftlyS2 plugins load beside it.

CS2 installation

One per node, shared by every slot. One installation, many servers explains how.

Kinds of launch

A launch sets the game mode, the player count, the cfgs and the console settings. It may also carry a match.

A match may carry a match id, the controller’s identifier for it. The match id names the match’s demos. Every match reports its phase and score to the controller over the agent’s socket. A match that sends statistics also uploads its gameplay events.

The match and its statistics decide what the server does:

Launch Ankor’s plugin Phase and score Gameplay events
Match that sends statistics Runs the roster and warmup for one map, and the knife round and demo recording when the match asks for them. Reported on the agent’s socket. Uploaded to the controller.
Match without statistics Runs the same match flow as a match that sends statistics. Reported on the agent’s socket. Not collected.
No match Stays idle. The node’s plugins and cfgs set the game. Not reported. Not collected.

A match can play on from a live round’s backup, on a new server. Launch a game explains how. A match plays a stock map, which every CS2 installation has, in the casual, competitive or wingman game mode. A launch without a match can play a map from the Steam Workshop, in any game mode.

Series

A match is one map. Its launch carries no series information, such as the number of maps, the map’s place in the series or the score so far.

A best-of-three is therefore three matches, with three match ids, launched one after another. The controller runs the veto, the side picks, the series score and the launch of each next map.

get5 and MatchZy work differently. They play a whole series on one server, which changes maps and keeps the series score. Ankor leaves the series to the controller, because a game server ends with its map:

Pros

  • Any map, any node. Each map goes to whichever node has a free slot. A node that fails between maps loses no series state, because it stores none.
  • One kind of game server. A map in a series runs like any other. The plugin has no map change, series state or end-of-series handling.
  • Formats are the controller’s. Bo2, Bo7, a map pool or a per-map side rule needs no plugin or agent release.

Cons

  • More to build. A controller that offers series implements the veto, the score and the hand-over between maps itself.
  • Players reconnect between maps, since the next map will start on another server.
  • No series in the records a node sends. Demos carry only their match id, and events only their server. The controller groups them into a series.

From launch to finished match

  1. Launch. The controller asks a node to start a server. The agent claims a free slot and starts CS2 with the plugin. If the node has no free slot, it refuses the launch, and the controller tries another node.
  2. Ready. Once the server is logged on to Steam and its map is loaded, the plugin reports that players can join. The agent then reports the slot running. The controller gives players the server’s address.
  3. Play. In a match, the plugin reports gameplay events. The agent forwards them to the controller.
  4. Finish. A match ends with its map. Any server also ends when it is stopped, or when it stays empty for the launch’s idle_shutdown_seconds. The server shuts down once its last events are stored. The agent then frees the slot and uploads the demo, if one was recorded.

The messages behind each step are in the protocol overview and the reference.

Control and history

Two kinds of traffic run between the agent and the controller:

  • Live control (launches, commands, server status) is not stored. After a reconnect the agent reports its current state again.
  • Gameplay history is stored on the node until the controller confirms it. Event uploads describes how.

Host networking

Game servers and the agent run in Docker’s host network mode. They use the node’s network stack instead of a private Docker network behind NAT.

Game traffic is a steady stream of small UDP packets. Host networking keeps Docker’s address translation and port forwarding out of that path. Players therefore connect straight to the server on the node’s address. Each plugin talks to the agent on loopback TCP port 4500.

Controller

Never connects to the node.

The node's own networkhost network mode

Agent

127.0.0.1:4500

Slot s1

UDP 27015 · GOTV 27020

Slot s2

UDP 27016 · GOTV 27021

Players

Connect to the node's public IPv4 address.

One installation, many servers

Every game server on a node reads the same CS2 installation, so CS2 is downloaded once per node. The plugin and its configuration sit in a separate overlay, mounted on top. Each ankor-cs2 image has its own overlay. A CS2 update therefore never overwrites the plugin, and the plugin never changes the game files.

The installation cannot change while a server reads it. A CS2 update therefore waits until every slot on the node is free. Update CS2 has the details. Node size versus node count weighs this for many small nodes against a few large ones.

Updates and restarts

Change When Running servers
CS2 update Only when every slot is idle. None run during it.
CS2 image (ankor-cs2) replaced Any time. Keep their image. New servers get the new one.
Agent restarted or its image replaced Any time. Keep running. The new agent takes over the running servers and their stored events. A failed replacement rolls back.
Game server crashes Without warning. Events that the agent has stored are kept. The rest are lost. A relaunch is a new server.
Host restarts Any time. End as in a crash. The agent starts again with Docker.
Connection to the controller lost Any time. Keep running. Their events wait on the node.

The node updates CS2 and its images when the controller asks. While auto_update is on, it also updates them by itself. Images lists which versions go together. Automatic updates describes what updates by itself and how to stop it.

Trust

  • By default, a node runs only the official images that the Ankor GitHub repository publishes, as official_images_only describes. update_agent and update_cs2_image refuse any other image, and the setup script refuses to run one. Only the node’s operator can turn this off, in agent.yml.
  • With it off, update_agent runs whatever image the controller sends, and that image can use the Docker socket. Connect such a node only to a controller you would give root on that host.
  • While auto_update is on, the node runs each new release or rebuild of its line from the repositories that agent.yml names.
  • A workshop server receives the Steam Web API key from its launch. Whoever runs the node can therefore read the key.
  • Game containers run with dropped capabilities and no access to the Docker socket.
  • The agent listens for plugins on loopback only and accepts only the servers it launched.

Edit this page