# Podman Quadlet: Matrix ## Overview [Matrix](https://matrix.org/) is an open, decentralised protocol for secure and interoperable communication. This cookbook runs a self-hosted Matrix homeserver using **[Tuwunel](https://matrix-construct.github.io/tuwunel/)** (a high-performance Rust-based homeserver) together with the **[Element Web](https://element.io/)** client. This cookbook runs the following services: - **matrix-homeserver**: The Tuwunel Matrix homeserver. - **matrix-web**: The Element Web browser client. - **matrix-init**: One-shot first-run initialisation (creates the admin account and discovers the admin room). - **matrix-backup**: Daily backup job triggered by a systemd timer. - **matrix-restore**: One-shot restore job that seeds the database from the latest backup archive on first boot (if needed). - **matrix-tools**: Custom OCI image (built locally from CentOS Stream 10) used by the init, backup, and restore jobs. The homeserver uses a RocksDB database stored locally and keeps media files and backup archives on a virtiofs volume. ## Prerequisites - The `traefik` cookbook must be installed and running. - A virtiofs data volume must be mounted at `/var/lib/virtiofs/data/`. - Configuration files must exist before starting (see [Configuration](#configuration)). ## Configuration ### Homeserver — `tuwunel.env` Copy the example and adapt it to your environment: ```sh sudo cp config/examples/tuwunel.env /etc/quadlets/matrix/tuwunel.env sudo chmod 600 /etc/quadlets/matrix/tuwunel.env ``` > **Important**: `TUWUNEL_SERVER_NAME` determines the Matrix identity of every user > (e.g. `@user:example.com`). **It cannot be changed after the first start.** > Prefer a root domain so users get clean handles, and delegate the actual HTTP > traffic to a subdomain (e.g. `matrix.example.com`) via `.well-known`. Key settings: | Variable | Description | |---|---| | `TUWUNEL_SERVER_NAME` | Matrix server name — sets user IDs (`@user:`). | | `TUWUNEL_PORT` | Port Tuwunel listens on (default: `6167`). | | `TUWUNEL_ALLOW_REGISTRATION` | Enable/disable new account registration. | | `TUWUNEL_REGISTRATION_TOKEN` | Token required for registration (set a strong secret). | | `TUWUNEL_GRANT_ADMIN_TO_FIRST_USER` | Automatically grants admin rights to the first registered user. | | `TUWUNEL_ALLOW_FEDERATION` | Enable Matrix federation (disabled by default for private servers). | | `MATRIX_INIT_ADMIN_USER` | Username for the initial admin account. | | `MATRIX_INIT_ADMIN_PASSWORD` | Password for the initial admin account. | | `MATRIX_BACKUP_RETENTION` | Number of daily backup archives to keep (default: `7`). | ### Element Web — `element-web/config.json` ```sh sudo mkdir -p /etc/quadlets/matrix/element-web sudo cp config/examples/element-web/config.json /etc/quadlets/matrix/element-web/config.json ``` Update `base_url` and `server_name` to match your deployment: ```json { "default_server_config": { "m.homeserver": { "base_url": "https://matrix.example.com", "server_name": "example.com" } } } ``` ## Traefik integration Copy the example Traefik dynamic configuration: ```sh sudo cp other/traefik/matrix.yaml /etc/traefik/dynamic/matrix.yaml ``` This configures two routes: | Hostname | Backend | Service | |---|---|---| | `matrix.example.com` | `http://127.0.0.1:6167` | Tuwunel homeserver | | `matrix-chat.example.com` | `http://127.0.0.1:8080` | Element Web client | Adjust the `Host()` rules and entry points to match your Traefik setup. ## Usage In a separate terminal, follow the logs: ```sh sudo make tail-logs ``` Install the Podman Quadlets and start Matrix: ```sh sudo make clean install ``` Services start in this order: 1. **matrix-tools-build.service** builds the local `matrix-tools` image. 2. **matrix-restore.service** restores the database from the latest backup (only on first boot when no database exists yet and at least one backup archive is available). 3. **matrix-homeserver.service** starts Tuwunel. 4. **matrix-web.service** starts Element Web. 5. **matrix-init.service** runs once on first boot: registers the admin user, discovers the admin room, and writes backup credentials to `/var/lib/virtiofs/data/matrix/tuwunel-backup.env`. 6. **matrix-backup.timer** schedules the daily backup job. Access the Element Web client through Traefik (e.g. `https://matrix-chat.example.com`). Restart the **matrix.target** unit: ```sh sudo systemctl restart matrix.target ``` Finally, remove the quadlets, their configuration and their data: ```sh sudo make uninstall clean ``` ## Backup and restore ### Backup Backups run daily via the `matrix-backup.timer`. The backup job: 1. Sends the `!admin server backup-database` command to the Tuwunel admin room via the Matrix API (using credentials generated by `matrix-init`). 2. Waits for Tuwunel to write an incremental RocksDB backup to the staging area. 3. Packages the backup into a restore-ready `.tar.gz` archive under `/var/lib/virtiofs/data/matrix/tuwunel/backup/`. 4. Applies the retention policy, keeping only the last `MATRIX_BACKUP_RETENTION` archives. To trigger a manual backup: ```sh sudo systemctl start matrix-backup.service ``` ### Restore The `matrix-restore.service` runs automatically before the homeserver starts if: - No RocksDB database exists at `/var/lib/quadlets/matrix/tuwunel/db/`, **and** - At least one `.tar.gz` backup archive exists under `/var/lib/virtiofs/data/matrix/tuwunel/backup/`. It extracts the latest archive into the database directory, then lets the homeserver start normally. ## Storage layout | Path | Contents | |---|---| | `/var/lib/quadlets/matrix/tuwunel/db/` | RocksDB database (local disk). | | `/var/lib/quadlets/matrix/tuwunel/backup-staging/` | RocksDB online backup staging area (cleared after each backup). | | `/var/lib/virtiofs/data/matrix/tuwunel/media/` | Uploaded media files (virtiofs). | | `/var/lib/virtiofs/data/matrix/tuwunel/backup/` | Daily backup archives (virtiofs). | | `/var/lib/virtiofs/data/matrix/tuwunel-backup.env` | Auto-generated backup credentials (written by `matrix-init`). | ## Integration tests ```sh sudo make test ```