Building & deploying
Game data comes from the public GSI installer
No game archive lives in the repo or CI — we host nothing that isn’t ours to redistribute. The
base game ships as the public GSI installer (tribes2gsi.exe, a Wise installer), and the build
extracts GameData from it directly — without ever running it:
- The build downloads the GSI from
GSI_URL(defaulthttps://depot.tribes2.net/legacy/tribes2gsi.exe). - A tiny static build of REWise — which extracts files
from Wise installers without executing the
.exe— pulls justMAINDIR/Tribes2/GameData/*from it. No Wine, no display, no GUI. - The 538 MB installer is fetched and removed in the same build layer; only the extracted
GameDatapersists.
REWise itself is compiled in a small rewise-build stage and pinned to a specific commit
(REWISE_REF). This makes both local and CI builds self-sufficient from a public source — no
secrets, no Release assets, no committed game files. (The installer’s silent mode does not work
headlessly, so extraction — not running it — is the only viable path.)
Everything else a mod needs (content/classic_v152.zip, content/Construction_v0.70a.exe) is
committed.
A note on the VC++ runtime licensing
The image needs Microsoft’s Visual C++ 2022 runtime DLLs (msvcp140, vcruntime140, concrt140,
…). Rather than committing those binaries, the build downloads Microsoft’s official
redistributable (VCREDIST_URL, default https://aka.ms/vs/17/release/vc_redist.x86.exe) and
unpacks it with cabextract (the redist is a WiX “Burn” bundle whose payload cabs hold the DLLs as
*.dll_x86; only the needed set is copied into the Wine system32). This keeps Microsoft as the
distributor and keeps Microsoft binaries out of this repository and its history.
These files are Microsoft “Distributable Code.” Redistribution is governed by the Visual Studio
2022 license terms (the REDIST.TXT list), which broadly permit shipping them as part of an
application subject to conditions (add significant functionality, flow-down terms, keep notices,
indemnify Microsoft, don’t open-source-contaminate them). Note two caveats for this project: the
terms are written around running on Windows (here they run under Wine on Linux, a gray
area), and you should review the current terms yourself. To pin a specific build, set
VCREDIST_SHA256. This is not legal advice.
Local builds
Build the base first; the mod images derive from it.
# base
docker build -f Dockerfile -t tribes2-server:base .
# derived mod images (FROM base)
docker build -f mods/classic/Dockerfile --build-arg BASE_IMAGE=tribes2-server:base -t tribes2-server:classic .
docker build -f mods/construction/Dockerfile --build-arg BASE_IMAGE=tribes2-server:base -t tribes2-server:construction .
Pass build args with --build-arg, e.g. --build-arg WINE_BRANCH=staging,
--build-arg PATCH_SHA256=<hash>. See Configuration → build args.
Run it:
docker run -d --name t2 -e ROOT_PASSWORD='choose-a-strong-one' \
-p 8080:8080 -p 8443:8443 -p 28000:28000/udp \
-v t2-data:/data \
tribes2-server:base
Open http://localhost:8080, log in as root, complete first-time setup.
Docker Compose
docker-compose.yml defines three services that share a common env
anchor: t2-base (default) and t2-classic / t2-construction (behind profiles).
cp .env.example .env # set ROOT_PASSWORD at minimum
docker compose build t2-base # build base FIRST
docker compose up -d t2-base # standard server (panel :8080)
docker compose --profile classic up -d --build t2-classic # Classic (panel :8081)
docker compose --profile construction up -d --build t2-construction # Construction (panel :8082)
Each service persists /data to its own named volume. Host ports are overridable via .env
(PANEL_HTTP_PORT, CLASSIC_HTTP_PORT, GAME_PORT, …).
To add your own mod service, see Creating a custom mod image → Compose.
GitHub Actions / GHCR
.github/workflows/build.yml builds and pushes to GHCR on
push to main, on v* tags, or manually (workflow_dispatch):
- Job
basebuildsDockerfile→ghcr.io/<owner>/<repo>/base:{sha,latest}. - Job
modsis a matrix (classic,construction) that builds eachFROMthe base it just pushed →ghcr.io/<owner>/<repo>/<mod>:{sha,latest}.
Both use GitHub Actions cache (type=gha) and free up runner disk first.
Game data in CI
Nothing to provision: the base job’s docker build downloads the public GSI installer and
extracts GameData with REWise (see above). Override the source with the GSI_URL repo variable
if the depot location changes, or pin its checksum with GSI_SHA256.
Repo variables / secrets
| Name | Kind | Purpose |
|---|---|---|
GSI_URL |
variable | Override the GSI installer URL (default depot.tribes2.net). |
GSI_SHA256 |
variable | Pin the GSI installer checksum. |
PATCH_URL |
variable | Override the QoL patch URL. |
PATCH_SHA256 |
variable | Pin the patch checksum. |
WINE_BRANCH |
variable | Wine branch. |
Adding your mod to CI
Add it to the matrix:
strategy:
matrix:
mod: [classic, construction, mymod] # ← your mod folder under mods/
It will publish ghcr.io/<owner>/<repo>/mymod:{sha,latest}. See
Creating a custom mod image.
Restart policy
The container expects to be long-running; restart: on-failure (Compose) relaunches it. When
root force-shuts-down the panel, the container stops and the restart policy decides whether it
comes back — a clean way to recycle.