This project manages Warfork dedicated servers over SSH.
The main entrypoint is cli.py, which can:
- Build and inspect a region/server-type deployment matrix
- Bootstrap a fresh host (one-time install of packages, SteamCMD, game files)
- Deploy: update game files via SteamCMD, refresh configs, restart sessions
- Stop and check status of running sessions
- Linux or macOS shell environment
- Python 3.10+ (3.11+ recommended)
- pipenv for environment and dependency management
- SSH access to all target servers
- A valid SSH private key file for remote login
Python dependencies (declared in requirements.txt, managed via pipenv):
clickparamikoscp
cli.py: Command-line entrypointwf_deploy/config.py: Loadsserver-config.jsonwf_deploy/matrix.py: Builds region x server-type deployment jobswf_deploy/remote.py: SSH/SCP actions against remote hosts (bootstrap, lifecycle, log tailing — all logic lives here)server-management/server-config.json: Server and game type definitionsserver-management/configs/*.cfg: Warfork config files per game type
Install pipenv if you don't already have it:
python3 -m pip install --user pipenvFrom the repository root, create the virtual environment and install dependencies from requirements.txt:
pipenv install -r requirements.txtThis creates a Pipfile (and Pipfile.lock) and a managed virtualenv. On subsequent setups, just run:
pipenv installTo enter the project shell:
pipenv shellOr run individual commands without activating a shell:
pipenv run python cli.py matrixIf you see ModuleNotFoundError: No module named 'scp', the virtual environment is missing dependencies. Re-run pipenv install.
By default the CLI uses:
- Config file:
server-management/server-config.json - Scripts directory:
server-management/
Important fields in server-config.json:
servers: Region key -> host + label + username +steam_branch+configurationservers.<region>.steam_branch: Pin this host tobetaorpublic. Required for any host that will be bootstrapped. SteamCMD installs one branch per/app/server, so the branch is per host, not per server type.servers.<region>.configuration: Server type key -> label + port + cvars (each region declares its own set; types may differ region to region)server_defaults.cvars: Baseline cvars merged into every jobsteam_branches: Allowed branch names (for metadata)
You must provide an SSH key for remote operations:
- Option 1: pass
--ssh-key /path/to/key - Option 2: set environment variable
WF_SSH_KEY=/path/to/key
Optional environment variables used by deploy:
RCON_PASSWORD: Injected as+set rcon_passwordOPERATOR_PASSWORD: Injected as+set g_operator_password
You can put any of these in a project-local .env file; pipenv automatically loads it for pipenv run and pipenv shell.
Top-level commands:
matrix: Preview what region/type jobs will be targetedbootstrap: Log in as root and fully install each host (wf user, packages, SteamCMD, game files, configs)deploy: Stop sessions, update game files via SteamCMD, refresh configs, restart sessionsstop: Stop every selected tmux sessionstatus: Report whether each selected session is runninglogs: Tail/home/wf/wf-*.logfrom every selected host in one terminal
General selectors shared by commands:
--config, -c: Path to config JSON--regions, -r: Comma-separated region keys, orall--types, -t: Comma-separated server-type keys, orall
All commands below assume you are using pipenv run. If you've activated the env via pipenv shell, drop the pipenv run prefix.
Show all jobs in table format:
pipenv run python cli.py matrixOnly selected regions/types:
pipenv run python cli.py matrix -r US,EU -t clan-arena,duelJSON output:
pipenv run python cli.py matrix --jsonRun this once on each new server (or any time you need to re-install or
recover one). The command logs in as root (override with --root-user)
and performs the full first-time setup in a single pass:
- Creates the
wfuser with/home/wfas a real home directory - Removes any stale
/home/wf/.steam/sdk{32,64}paths left as root-owned directories - Installs system packages and downloads SteamCMD
- Installs the Warfork game files for the host's Steam branch
- Uploads the local
configs/*.cfgfiles into/app/server/basewf/configs - Recursively chowns
/home/wfand/app/{Steam,server}towf:wf
The Steam branch comes from the host's steam_branch field in
server-config.json (set under servers.<region>). SteamCMD installs
one branch per /app/server, so the branch is pinned per host.
pipenv run python cli.py bootstrap --ssh-key ~/.ssh/id_ed25519Target specific hosts:
pipenv run python cli.py bootstrap -r US,EU --ssh-key ~/.ssh/id_ed25519Bootstrap is idempotent — re-running it is the supported way to refresh
game files or recover a misconfigured host. After it succeeds, use
deploy to roll out updates.
deploy always performs the same sequence per host: stop the matching
sessions, run SteamCMD against the host's pinned Steam branch, upload
configs, then start the sessions again. There is no action flag.
pipenv run python cli.py deploy --ssh-key ~/.ssh/id_ed25519Target a subset:
pipenv run python cli.py deploy -r US -t race --ssh-key ~/.ssh/id_ed25519Dry run (no SSH connections):
pipenv run python cli.py deploy --dry-runHigher parallelism:
pipenv run python cli.py deploy \
--parallel 8 \
--ssh-key ~/.ssh/id_ed25519To switch a host to a different Steam branch, edit its steam_branch field
in server-config.json and re-run bootstrap for that host.
Pass --local-build PATH to bootstrap or deploy to skip SteamCMD and
rsync the contents of a local build directory onto each host instead. Useful
when you have an unpublished build you want to try out without going through
a Steam branch.
pipenv run python cli.py deploy \
--local-build ~/projects/warfork-qfusion/source/build/warfork-qfusion \
--ssh-key ~/.ssh/id_ed25519The directory's contents (not the directory itself) are rsynced into
/app/server, then the SSH user runs sudo chown -R wf:wf /app/server. The
host's steam_branch is ignored when --local-build is set, so it doesn't
need to be defined for local-build runs. Re-run without --local-build (or
re-run bootstrap) to switch back to a Steam branch.
Deploy options:
--scripts-dir: local path to folder containingconfigs/--parallel, -p: max concurrent SSH connections (1-16)--rcon-password: override or pass directly--operator-password: override or pass directly--local-build: rsync game files from a local build directory instead of SteamCMD--dry-run: print actions without remote execution
Stream /home/wf/wf-*.log from each selected host into one terminal, with
each line color-coded and prefixed with the region label. Ctrl+C to stop.
pipenv run python cli.py logs --ssh-key ~/.ssh/id_ed25519Limit to a subset and show more history:
pipenv run python cli.py logs -r US,EU -n 500 --ssh-key ~/.ssh/id_ed25519The remote tail -F runs under sudo because the log files are owned by
wf. Pass --no-sudo if your SSH user can already read them.
Report whether each selected tmux session is currently running:
pipenv run python cli.py status --ssh-key ~/.ssh/id_ed25519Stop every selected tmux session (no-op for sessions that aren't running):
pipenv run python cli.py stop -r EU --ssh-key ~/.ssh/id_ed25519Preview before deploy:
pipenv run python cli.py matrix -r all -t all
pipenv run python cli.py deploy --dry-run -r all -t allRoll out one game type to one region:
pipenv run python cli.py deploy \
-r US \
-t race \
--ssh-key ~/.ssh/id_ed25519Stop all servers in EU:
pipenv run python cli.py stop -r EU --ssh-key ~/.ssh/id_ed25519- On success, deploy prints a summary and exits
0. - If any job fails, deploy lists failed targets and exits
1. - Invalid region/type keys produce a validation error showing available keys.
ModuleNotFoundErrorforclick,paramiko, orscp:- Run
pipenv installfrom the repo root, then prefix commands withpipenv run(or usepipenv shell).
- Run
pipenv: command not found:- Install with
python3 -m pip install --user pipenvand ensure~/.local/binis on yourPATH.
- Install with
- Wrong Python version picked up:
- Force a specific interpreter with
pipenv --python 3.11 install -r requirements.txt.
- Force a specific interpreter with
SSH private key is required:- Pass
--ssh-keyor setWF_SSH_KEY(a.envfile in the repo root works with pipenv).
- Pass
- Unknown region/server type:
- Check keys in
server-management/server-config.jsonand pass exact names.
- Check keys in
- Remote command failed:
- Verify host reachability, key permissions, and remote user privileges. The CLI runs all server-side logic over SSH — there is no longer a
Warfork.shto install on the host; the only on-disk remote artifacts are the game files under/app/server, the SteamCMD install under/app/Steam, and the per-instance launcher script/home/wf/wf-<port>.shwritten each timestartruns.
- Verify host reachability, key permissions, and remote user privileges. The CLI runs all server-side logic over SSH — there is no longer a
Additional server operations background is documented in:
server-management/README.md