
Run several servers and update safely
One BattleSpades install can supervise many isolated servers — the ten official Match Lobby modes ship ready to launch together. This guide covers the fleet launcher, running as a service, and upgrading without losing data.
The fleet launcher
--fleet <manifest> starts one child server per enabled [[instances]] entry, each with its own config, port and log. Config paths are resolved beside the manifest. Before anything starts, every config is validated and duplicate names or game ports are rejected. Ctrl+C sends a clean shutdown to every child and only force-stops a child that exceeds shutdown_timeout_seconds (1–60, default 10).
[fleet]
shutdown_timeout_seconds = 10
[[instances]]
name = "Official TDM"
config = "configs/official-tdm.toml"
enabled = true
[[instances]]
name = "Official CTF"
config = "configs/official-ctf.toml"
enabled = true
# port = 27117 # optional: override this instance's [server] port# Portable Windows release
.\BattleSpades.exe --fleet fleet.toml
# Source checkout
py -3.12 run_server.py --fleet fleet.tomlThe ten official profiles
The shipped fleet.toml launches these profiles from configs/. Each uses the recovered retail map rotation and clock, backfills at most six bots with the crash-isolated process worker, and has its own game port, Steam ports and log file. AoSPlay and Steam publishing are disabled in all of them.
| Profile | Mode | Start map | Game UDP | Steam query / updater UDP | Clock | Bots |
|---|---|---|---|---|---|---|
Official TDMconfigs/official-tdm.toml | tdm | MayanJungle | 27015 | 27016 / 8766 | 15 min | 6 max (process) |
Official CTFconfigs/official-ctf.toml | ctf | TokyoNeon | 27017 | 27018 / 8767 | 30 min | 6 max (process) |
Official Classic CTFconfigs/official-cctf.toml | cctf | Classic | 27019 | 27020 / 8768 | 90 min | 6 max (process) |
Official Zombieconfigs/official-zombie.toml | zom | SpookyMansion | 27021 | 27022 / 8769 | 10 min | 6 max (process) |
Official VIPconfigs/official-vip.toml | vip | CityOfChicago | 27023 | 27024 / 8770 | 15 min | 6 max (process) |
Official Multi-Hillconfigs/official-multi-hill.toml | mh | MayanJungle | 27025 | 27026 / 8771 | 25 min | 6 max (process) |
Official Territory Controlconfigs/official-territory-control.toml | tc | CityOfChicago | 27027 | 27028 / 8772 | 25 min | 6 max (process) |
Official Diamond Mineconfigs/official-diamond-mine.toml | dia | ArcticBase | 27029 | 27030 / 8773 | 15 min | 6 max (process) |
Official Demolitionconfigs/official-demolition.toml | dem | Atlantis | 27031 | 27032 / 8774 | 15 min | 6 max (process) |
Official Occupationconfigs/official-occupation.toml | oc | AncientEgypt | 27033 | 27034 / 8775 | 15 min | 6 max (process) |
To publish a fleet, give each profile its own public identity: enable [revival] per profile, register every game port, and run each with its own token (the fleet passes the parent's environment to every child, so separate launches or containers are simpler when each needs a different AOS_MASTER_WRITE_TOKEN). With Steam listing, forward each game and query port plus the Steam UDP ports 8766–8775 the profiles use.
Write your own profile
A profile is an ordinary config file; anything missing uses the built-in default (not the values in the big sample config.toml), so set the keys you care about explicitly.
[server]
name = "My Zombie Server"
port = 27041
max_players = 24
[game]
default_mode = "zom"
default_map = "SpookyMansion"
[lobby]
match_length_minutes = 10
map_rotation = ["SpookyMansion", "MayanJungle", "CastleWars"]
[bots]
enabled = true
population_mode = "backfill"
fill_target = 8
max_bots = 8
worker = "process"
[admin]
password = "another-long-unique-secret"
bans_path = "bans-zombie.json"
[revival]
enabled = false
[logging]
file = "my-zombie.log"
console = falseYou can also run instances as completely separate processes: BattleSpades --config configs/my-zombie.toml (and --port to override the port once).
Keep it running as a service
The server stops cleanly on Ctrl+C, SIGINT and SIGTERM, so any service manager works. On Linux, systemd restarts it after a crash and starts it at boot.
[Unit]
Description=BattleSpades Ace of Spades server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=battlespades
WorkingDirectory=/opt/battlespades
ExecStart=/opt/battlespades/BattleSpades --config /opt/battlespades/config.toml
Environment=AOS_MASTER_WRITE_TOKEN=aos_srv_...
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now battlespades
journalctl -u battlespades -fOn Windows, the simplest option is a Task Scheduler task that runs BattleSpades.exe “At startup” with “Run whether user is logged on or not”, the server folder as “Start in”, and “If the task fails, restart every 1 minute”. For a source checkout run python run_server.py from its folder instead. --control-stdin lets a launcher stop the server by writing shutdown to its standard input.
Updating without losing data
Keep these across every upgrade — they are your data, not part of the program:
config.toml(and anyconfigs/*.toml/fleet.tomlyou changed)bans.json(or yourbans_path)state/round-results.sqlite3— AoSPlay round results that were not yet uploaded- custom maps in
maps/(with their.txt/.jsonsidecars), prefabs, and your plugins logs/if you need history
- 01Test first
Extract or build the new version in a separate folder and run it with
--check, ideally on a spare port (--port 27099) for a quick join test. - 02Announce
Warn players with
/say; a restart disconnects everyone. - 03Stop
Stop the old server (Ctrl+C or your service manager).
- 04Carry data over
Copy the files above into the new folder. Compare your
config.tomlwith the new sample for new keys; missing keys simply use defaults. - 05Start
Start the new version and watch the log for warnings.
# stop the server first (compiled modules are locked while it runs)
git pull
python -m pip install -r requirements.txt
python setup.py build_ext --inplace
python run_server.py --checkThe release packager refuses to overwrite an existing release folder on purpose, so a new portable version always lands next to the old one. Containers update by switching the image tag; see Docker.
Common questions
How many servers can one machine run?+
It depends on CPU cores and memory. Each server simulates at 60 Hz and needs roughly 80–200 MB after loading a map, plus bots. Start small, watch tick stats in the logs, and add instances while they stay healthy.
Can two instances use the same port?+
No. The fleet launcher refuses duplicate game ports, and the operating system would refuse the second socket anyway.
Will an update reset my bans?+
Only if you forget to copy bans.json. Bans are stored in that file, keyed by IP address.


