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:

  1. The build downloads the GSI from GSI_URL (default https://depot.tribes2.net/legacy/tribes2gsi.exe).
  2. A tiny static build of REWise — which extracts files from Wise installers without executing the .exe — pulls just MAINDIR/Tribes2/GameData/* from it. No Wine, no display, no GUI.
  3. The 538 MB installer is fetched and removed in the same build layer; only the extracted GameData persists.

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 base builds Dockerfileghcr.io/<owner>/<repo>/base:{sha,latest}.
  • Job mods is a matrix (classic, construction) that builds each FROM the 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.

See also


This site uses Just the Docs, a documentation theme for Jekyll.