Operations
Upgrades
Section titled “Upgrades”docker compose pull && docker compose up -dDatabase migrations run on every start and are safe to repeat, so an upgrade is a pull, not a runbook.
Choosing a release channel
Section titled “Choosing a release channel”TEAMHUD_VERSION in .env picks which server image you follow:
| Value | Moves when | Use it for |
|---|---|---|
stable (default) |
a release is promoted. None has been yet, so until then set edge or the pull fails with manifest unknown |
production |
edge |
every change we merge | trying things early; expect breakage |
sha-<12 chars> |
never | pinning a known-good build, and rolling back to one |
Pinning turns an upgrade into a deliberate edit. To roll back, set the previous sha-…
and docker compose up -d.
The other images (Postgres, auth, storage, media server) are pinned in
docker-compose.yml. Read their release notes before bumping a major version.
Backups
Section titled “Backups”backup.sh writes a dated, compressed dump of the whole database to /var/backups/teamhud
(readable by root only), plus a tarball of the storage-data and recording-media volumes
with the same timestamp, and keeps 14 days. Install the nightly timer once, as root, and
prove it runs now rather than tomorrow:
./backup.sh installsystemctl start teamhud-backup.service && ls -la /var/backups/teamhudIt covers accounts, organizations, messages, file metadata, and the uploaded files and recordings on those two volumes. It does not cover itself: the dumps sit on the same disk they protect. Copy them off the box, or turn on your provider’s machine-level backups. Files in S3-compatible storage (see below) are not in the tarball either.
Test a restore
Section titled “Test a restore”A backup nobody has restored is a hope. Restore into a scratch database, compare a few row counts against the live one, then drop it:
docker compose exec db psql -h 127.0.0.1 -U supabase_admin -d postgres -c 'CREATE DATABASE restore_drill'docker compose exec -T db pg_restore -h 127.0.0.1 -U supabase_admin -d restore_drill --no-owner --no-acl \ < /var/backups/teamhud/teamhud-<stamp>.dumpdocker compose exec db psql -h 127.0.0.1 -U supabase_admin -d postgres -c 'DROP DATABASE restore_drill'File storage
Section titled “File storage”Attachments, avatars and release installers stay on a local Docker volume by default, which is what you want when the reason you self-host is that data stays in your building.
To use S3-compatible object storage instead, set the STORAGE_* block in .env (it is
documented in .env.example). Two warnings:
- Switching does not move existing files. Copy them first, or they vanish from the app.
STORAGE_WORKSPACEis write-once. Every object path hangs off it. Choose it once.
Monitoring
Section titled “Monitoring”- Boot log:
docker compose logs server | head -50states which optional features it found and which are off. It is the fastest check after any.envchange. - Health:
./verify-stack.shre-runs the full end-to-end check at any time. - Capacity: the
thstatcollector reports real CPU, memory and bandwidth. See Sizing.
Secrets
Section titled “Secrets”gen-keys.mjs generates the database password, the login-token signing secret, the media
server keys, the encryption key, and the two service keys.
.envis a secret. Keep itchmod 600, out of git, and out of chat.- Never substitute Supabase’s published demo keys. They are signed with a public secret, so anyone who recognises one can mint their own.
- Adding a setting is one step: put it in
.envand rundocker compose up -d. Every supported variable is already wired intodocker-compose.yml.
Licensing
Section titled “Licensing”A server that has never been activated serves only the activation screen: sign in to TeamHUD Cloud and approve the code, or paste a key. A server that was activated and later lapses keeps running at Free limits. A key is what licenses a team to run on it.
- Buy a self-host licence at dashboard.teamhud.app → Billing. The key appears under Licences.
- Set
TEAMHUD_LICENSE_KEYandTEAMHUD_LICENSE_PUBLIC_KEYin.env(both are needed), thendocker compose up -d. - Check
GET /usageon your server to confirm the plan. The boot log does not report it.
The server renews the licence daily by itself.
Your own terms
Section titled “Your own terms”By default your server serves its own terms and privacy pages at /legal/…, so your users
never consent to TeamHUD’s documents while their data lives in your database. Point to your
own with TEAMHUD_TERMS_URL, TEAMHUD_PRIVACY_URL and TEAMHUD_ACCEPTABLE_USE_URL, and
bump TEAMHUD_LEGAL_VERSION whenever you change them to make everyone re-accept.
Optional: team mini-games
Section titled “Optional: team mini-games”A small game server for team mini-games. firstboot.sh installs it unless you answer no. Turn it on or off with
ENABLE_GAMES=true|false in .env, then re-run ./firstboot.sh, which adds or removes the
container and its secret. Desktops list only the games your server can actually run.