Skip to content

Running the Matrix hub ​

deploy/hub is the shared Matrix homeserver that the human queue uses to reach the owner's phone: Tuwunel behind Caddy, with federation off and public registration closed. matrix-bus is the client that talks to it, and queue-mirror mirrors approvals and questions into its #queue room.

It is a compose stack and a few owner-side scripts, not a package. This page follows deploy/hub/README.md, which also records why each setting is what it is.

What runs ​

ServiceCompose profileStatus
tuwunel on 127.0.0.1:8008defaultruns
caddy, TLS for SERVER_NAMEremoteruns on a VPS
core, the broker servicecorebuilt by CI, not started; it exists for stage 2

Stage 1 runs Tuwunel and Caddy only. The core appservice registration is still rendered, so Tuwunel knows its namespaces, but nothing uses them yet.

Prerequisites ​

  • Docker with the compose plugin.
  • Node 22 and a repo-root pnpm install, only for the owner-side scripts. A VPS needs Docker alone.
  • openssl, which setup.sh uses to generate tokens.
  • envsubst (part of GNU gettext), which setup.sh uses to render the config templates.
  • For the remote profile: a DNS name that points at the host, and ports 80 and 443 open.

Local run ​

sh
cd deploy/hub
./scripts/setup.sh
docker compose up -d --build
curl localhost:8008/_matrix/client/versions
pnpm run bootstrap   # owner account, the ac-edge1 mirror user and #queue

setup.sh writes every missing key to .env, generates the tokens, and renders the config templates into data/run. It prints what it rendered:

rendered for tp301.local: tuwunel.toml titan-core.yaml titan-edge1.yaml

SERVER_NAME defaults to tp301.local. Rerunning the script keeps the existing .env keys.

Remote run ​

On a fresh VPS with an empty data/:

sh
cd deploy/hub
printf 'SERVER_NAME=chat.example.org\nPUBLIC_BASE_URL=https://chat.example.org\n' > .env
./scripts/setup.sh && chmod 600 .env
docker compose run --rm tuwunel --execute "users create_user owner"   # note the password, then Ctrl-C
docker compose --profile remote up -d --build

Setting PUBLIC_BASE_URL also enables online backups and the client well-known file. The last line starts Tuwunel and Caddy.

Where state lives ​

Everything is under deploy/hub, and all of it is gitignored.

PathHolds
.envevery token and the owner password; keep it mode 600 and never commit it
data/dbthe Tuwunel database
data/server_namethe server name the database was created for
data/runrendered config and appservice registrations; regenerated by setup.sh
data/backuponline database backups, the latest 3, when PUBLIC_BASE_URL is set
data/caddy/dataTLS certificates

Logs are the containers' own: docker compose logs -f tuwunel or caddy.

Back up data/backup, .env and data/caddy/data. A restore must keep the same SERVER_NAME.

Upgrade ​

The compose file tracks ghcr.io/matrix-construct/tuwunel:latest. Take a backup, then:

sh
docker compose pull
docker compose --profile remote up -d

Tuwunel has a 30-minute stop grace period. Let a stop finish; do not kill the container. After a template change, run ./scripts/setup.sh again and restart Tuwunel.

How it fails ​

What you seeWhy
data/db was created for <old>, not <new>; wipe data/db or restore SERVER_NAME, exit 1the server name changed after the database was created; Tuwunel bakes the name into every id
The "SERVER_NAME" variable is not set. Defaulting to a blank string.setup.sh has not run, so .env and data/run do not exist
curl localhost:8008/… refuses the connectionTuwunel is still starting, or the container exited; read its logs
Caddy serves no certificate for SERVER_NAMEthe DNS name does not reach the host, or ports 80 and 443 are closed; Caddy binds both
pnpm run bootstrap fails to connectTuwunel is not up on HS_URL (default http://localhost:8008)

Never copy a laptop's data/ to the VPS: the server name would not match.

What CI checks ​

The hub-compose job in .github/workflows/ci.yml runs setup.sh with a sample .env, validates the compose file with and without the remote and core profiles, and builds the core image. It starts no container.

MIT licensed