From fa059f762f72f813ee06b1234404ce14ceedd74f Mon Sep 17 00:00:00 2001 From: Andrew Hunter Date: Thu, 5 Mar 2026 13:47:20 -0500 Subject: [PATCH 1/4] :memo:(docs) Add testmatrix example to troubleshooting. --- docs/troubleshooting.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 3a8c4b118..52d672a66 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -244,3 +244,32 @@ and is not easy for non-developers to understand. [unbound-tuning]: https://unbound.docs.nlnetlabs.nl/en/latest/topics/core/performance.html [unbound-arch]: https://wiki.archlinux.org/title/Unbound + +#### MatrixRTC testing tools +[testmatrix](https://pypi.org/project/testmatrix/) is a command line tool for testing various aspects of a matrix server and guiding debugging. + +``` +$ ./test_matrix.py -u -t example.com +Testing server example.com + Federation url: https://matrix.example.com:443 +✔ Server well-known exists +✔ Client well-known has proper CORS header + Client url: https://matrix.example.com + Adding livekit service URL: https://matrixrtc.example.com/livekit/jwt +✔ Server version: Tuwunel (1.5.0) +✔ Federation API endpoints seem to work fine +✔ Client API endpoints seem to work fine + Server oauth metadata endpoint failed (spec 1.15) + QR code login is disabled (MSC 4108) + Public room directory is disabled +✔ MatrixRTC SFU configured + Adding livekit service URL: https://matrixrtc.example.com/livekit/jwt +✔ JWTauth healthz responds +✔ jwt /get_token without auth returns 405, good. +✔ /get_token succeeded. Use the below information to test your livekit SFU on https://livekit.io/connection-test +[...] +𐄂 MatrixRTC configured but delayed events turned off (MSC4140). BAD! +✔ Room summaries (MSC3266) support in matrix compat v1.15 +✔ Room summaries stable support works +𐄂 Direct open registration might not be forbidden (returned 401)! +``` From 95adde8a731424673414d7e3546ef8a74cfc0220 Mon Sep 17 00:00:00 2001 From: Andrew Hunter Date: Fri, 6 Mar 2026 10:42:52 -0500 Subject: [PATCH 2/4] :memo:(docs) Add podman quadlet and keycloak ODIC SSO examples --- docs/SUMMARY.md | 5 +- docs/configuration/openid-connect.md | 40 ++++++++++++++ docs/deploying/containers.md | 3 + docs/deploying/podman-systemd.md | 79 ++++++++++++++++++++++++--- docs/deploying/redhat.md | 4 ++ docs/deploying/reverse-proxy-caddy.md | 23 ++++++++ quadlet/tuwunel-db.volume | 2 + quadlet/tuwunel.container | 21 +++++++ quadlet/tuwunel.env | 15 +++++ 9 files changed, 184 insertions(+), 8 deletions(-) create mode 100644 docs/configuration/openid-connect.md create mode 100644 docs/deploying/containers.md create mode 100644 quadlet/tuwunel-db.volume create mode 100644 quadlet/tuwunel.container create mode 100644 quadlet/tuwunel.env diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 1093e03c8..9cd4a21e9 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -3,13 +3,16 @@ - [Introduction](introduction.md) - [Configuration](configuration.md) - [Examples](configuration/examples.md) + - [OpenID Connect](configuration/openid-connect.md) - [Deploying](deploying.md) - [Generic](deploying/generic.md) - [Reverse Proxy - Caddy](deploying/reverse-proxy-caddy.md) - [Reverse Proxy - Nginx](deploying/reverse-proxy-nginx.md) - [Reverse Proxy - Traefik](deploying/reverse-proxy-traefik.md) - [NixOS](deploying/nixos.md) - - [Docker](deploying/docker.md) + - [Containers](deploying/containers.md) + - [Docker](deploying/docker.md) + - [Podman](deploying/podman-systemd.md) - [Kubernetes](deploying/kubernetes.md) - [Arch Linux](deploying/arch-linux.md) - [Debian](deploying/debian.md) diff --git a/docs/configuration/openid-connect.md b/docs/configuration/openid-connect.md new file mode 100644 index 000000000..9d310162e --- /dev/null +++ b/docs/configuration/openid-connect.md @@ -0,0 +1,40 @@ +# OpenID Connect / OIDC + +## Keycloak + +Keycloak is a self-hostable OpenID Connect provider. + +> Ensure your matrix .well-known values are being served correctly before beginning. Such as with [matrixtest](../troubleshooting.md#matrixrtc-testing-tools) + +### Keycloak configuration + +1. Create client on your keycloak server: +- Ensure `Client Authentication` is toggled `on` +- Root Url = `https://` +- Valid Redirect Urls = `https:///_matrix/client/unstable/login/sso/callback/` +- Web Origins = `https://` +2. Navigate to the Client Credentials tab, note the value of `client secret` +3. Note the `realm` you are creating the client in. + +### Tuwunel configuration + +Add the following identity provider section to you tuwunel.toml config file. Replace the `< placeholders>` with the values noted in your keycloak `client`. + +#### tuwunel.toml +```toml + +[[global.identity_provider]] +brand = 'keycloak' +client_id = '' +client_secret = '' +issuer_url = 'https:///realms/' +``` +#### Environment variables +Example Environment variables that can be added to a `docker-compose.yaml` or podman `tuwunel.env` if preferred: + +```env +TUWUNEL_IDENTITY_PROVIDER__0__BRAND="keycloak" +TUWUNEL_IDENTITY_PROVIDER__0__CLIENT_ID="" +TUWUNEL_IDENTITY_PROVIDER__0__CLIENT_SECRET="" +TUWUNEL_IDENTITY_PROVIDER__0__ISSUER_URL="https:///realm/" +``` diff --git a/docs/deploying/containers.md b/docs/deploying/containers.md new file mode 100644 index 000000000..2766d26d9 --- /dev/null +++ b/docs/deploying/containers.md @@ -0,0 +1,3 @@ +# Containers +Tuwunel can be deployed in containers such as [Docker](docker.md) or [Podman](podman-systemd.md). + diff --git a/docs/deploying/podman-systemd.md b/docs/deploying/podman-systemd.md index 8cd4becd9..77c83a587 100644 --- a/docs/deploying/podman-systemd.md +++ b/docs/deploying/podman-systemd.md @@ -1,16 +1,81 @@ -# tuwunel in Podman systemd +# Podman, Quadlets, and systemd + +For a rootless setup, we can use quadlets and systemd to manage the container lifecycle. + +> If this is the first container managed with quadlets for your user, ensure that linger is enabled so your containers are not killed after logging out. +> +> `sudo loginctl enable-linger ` + +## Installation +1. Copy quadlet files to `~/.config/containers/systemd/tuwunel` + +### tuwunel.container + +
+tuwunel container quadlet -Copy [tuwunel.container](tuwunel.container) to ~/.config/containers/systemd/tuwunel.container. -Reload daemon: ``` -systemctl --user daemon-reload +{{#include ../../quadlet/tuwunel.container}} ``` -Start the service: + +
+ +### tuwunel-db.volume + +
+tuwunel database volume quadlet + +``` +{{#include ../../quadlet/tuwunel-db.volume}} +``` + +
+ +### tuwunel.env + +
+tuwunel environment variable quadlet + +```env +{{#include ../../quadlet/tuwunel.env}} +``` + +
+ + + + ``` -systemctl --user start tuwunel +mkdir -p ~/.config/containers/systemd/tuwunel +cp docs/deploying/qualet/* ~/.config/containers/systemd/tuwunel ``` +2a. Modify tuwunel.env to desired values. +2b. Modify [tuwenel.toml](generic.md#creating-the-tuwunel-configuration-file) to desired values. +This can be saved in your user home directory if desired. + +3. Reload daemon to generate our systemd unit files: +``` +systemctl --user daemon-reload +``` +4. Start tuwunel: +``` +systemctl --user start tuwunel-pod +``` +## Logging To check the logs, run: ``` -journalctl -eu tuwunel.container --user +systemctl --user status tuwunel +``` +or + +``` +podman logs tuwunel-homeserver ``` +#### Troubleshooting systemd unit file generation + +Look for errors in the output: + + +`/usr/lib/systemd/system-generators/podman-system-generator --user --dryrun` + diff --git a/docs/deploying/redhat.md b/docs/deploying/redhat.md index 8abe23f72..7658b86d1 100644 --- a/docs/deploying/redhat.md +++ b/docs/deploying/redhat.md @@ -1 +1,5 @@ {{#include ../../rpm/README.md}} + +## Podman / Quadlets + +Podman and Quadlets are well supported on Redhat-based distributions. See [Podman and systemd](podman-systemd.md) for examples. diff --git a/docs/deploying/reverse-proxy-caddy.md b/docs/deploying/reverse-proxy-caddy.md index dbee256d3..205bec879 100644 --- a/docs/deploying/reverse-proxy-caddy.md +++ b/docs/deploying/reverse-proxy-caddy.md @@ -42,6 +42,29 @@ After starting Caddy, verify it's working by checking: curl https://your.server.name/_tuwunel/server_version curl https://your.server.name:8448/_tuwunel/server_version ``` +## Caddy and .well-known + +Caddy can serve `.well-known/matrix/client` and `.well-known/matrix/server` instead of `tuwunel`. This can be done by using the `respond` directive in your caddyfile. + +Useful if you want to delegate a domain such as `example.com` -> `matrix.example.com`. + +> [!info] Note the use of \` (backtic) in the respond directive to escape JSON that contains \" (double quotes). + +```caddyfile +your.server.name, your.server.name:8848 { + + @matrix path /.well-known/matrix/* + #Recommended CORS headers (https://spec.matrix.org/v1.17/client-server-api/#well-known-uris) + header @matrix { + Access-Control-Allow-Origin: * + Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Headers: X-Requested-With, Content-Type, Authorization + } + respond /.well-known/matrix/client `{"m.homeserver": {"base_url":"https://"}, "org.matrix.msc4143.rtc_foci": [{"type": "livekit", "livekit_service_url": "https://"}]} ` + respond /.well-known/matrix/server `{"m.server": ":443"}` +} +``` + --- diff --git a/quadlet/tuwunel-db.volume b/quadlet/tuwunel-db.volume new file mode 100644 index 000000000..091b38f45 --- /dev/null +++ b/quadlet/tuwunel-db.volume @@ -0,0 +1,2 @@ +[Volume] +VolumeName=tuwunel-db diff --git a/quadlet/tuwunel.container b/quadlet/tuwunel.container new file mode 100644 index 000000000..e6c2145cd --- /dev/null +++ b/quadlet/tuwunel.container @@ -0,0 +1,21 @@ +# tuwenel.container + +[Unit] +Description=Tuwunel Matrix Homeserver + +[Container] +ContainerName=tuwunel-homeserver +Image=ghcr.io/matrix-construct/tuwunel:latest +PublishPort=8008:8008 +Volume=tuwunel-db:/var/lib/tuwunel/ + +#Example location in ~/tuwunel/confing/ +Volume=%h/tuwunel/config/tuwunel.toml:/etc/tuwunel.toml +EnvironmentFile=tuwunel.env + +[Service] +# Uncomment when your system is properly configured, restart=always can mask start up errors. +#Restart=always + +[Install] +WantedBy=default.target diff --git a/quadlet/tuwunel.env b/quadlet/tuwunel.env new file mode 100644 index 000000000..37977f161 --- /dev/null +++ b/quadlet/tuwunel.env @@ -0,0 +1,15 @@ +TUWUNEL_SERVER_NAME="your.server.tld" +TUWUNEL_PORT=8008 +TUWUNEL_MAX_REQUEST_SIZE=20000000 +TUWUNEL_ALLOW_REGISTRATION=true +TUWUNEL_REGISTRATION_TOKEN= +TUWUNEL_ALLOW_FEDERATION=true +TUWUNEL_TRUSTED_SERVERS=["matrix.org"] +TUWUNEL_LOG=info + +#Listen on this host for IPv4 and v6 +TUWUNEL_ADDRESS=["0.0.0.0", "::"] + +#Tell Tuwunel to use the user config file +TUWUNEL_CONFIG=/etc/tuwunel.toml + From 026cb69b6c06e3e2aa10a1dc80f8572ec772383b Mon Sep 17 00:00:00 2001 From: Andrew Hunter Date: Fri, 6 Mar 2026 11:20:14 -0500 Subject: [PATCH 3/4] Fix some incorrect commands refering to -pod. --- docs/deploying/podman-systemd.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/deploying/podman-systemd.md b/docs/deploying/podman-systemd.md index 77c83a587..1d611afbd 100644 --- a/docs/deploying/podman-systemd.md +++ b/docs/deploying/podman-systemd.md @@ -47,12 +47,11 @@ For a rootless setup, we can use quadlets and systemd to manage the container li ``` mkdir -p ~/.config/containers/systemd/tuwunel -cp docs/deploying/qualet/* ~/.config/containers/systemd/tuwunel ``` -2a. Modify tuwunel.env to desired values. -2b. Modify [tuwenel.toml](generic.md#creating-the-tuwunel-configuration-file) to desired values. -This can be saved in your user home directory if desired. +2. + - Modify tuwunel.env to desired values. + - Modify [tuwenel.toml](generic.md#creating-the-tuwunel-configuration-file) to desired values. This can be saved in your user home directory if desired. 3. Reload daemon to generate our systemd unit files: ``` @@ -60,7 +59,7 @@ systemctl --user daemon-reload ``` 4. Start tuwunel: ``` -systemctl --user start tuwunel-pod +systemctl --user start tuwunel ``` ## Logging To check the logs, run: From 3f22c9e5eaff0266b03419aaa8f216030dc10ee9 Mon Sep 17 00:00:00 2001 From: Andrew Hunter Date: Sun, 8 Mar 2026 11:54:45 -0400 Subject: [PATCH 4/4] Refactor troubleshooting section and fix link. --- docs/maintenance.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/maintenance.md b/docs/maintenance.md index 35579c7bf..135f10fb4 100644 --- a/docs/maintenance.md +++ b/docs/maintenance.md @@ -42,7 +42,7 @@ running compaction is not recommended, or compaction via a timer, due to creating unnecessary I/O amplification. RocksDB is built with io_uring support via liburing for improved read performance. -RocksDB troubleshooting can be found [in the RocksDB section of troubleshooting](troubleshooting.md). +RocksDB troubleshooting can be found [in the RocksDB section of troubleshooting](troubleshooting.md#rocksdb--database-issues). ### Compression @@ -77,15 +77,14 @@ would like to store nearly none at all, see the `rocksdb_max_log_files` config option. ## Backups +### Online +Currently only RocksDB supports online backups. -Currently only RocksDB supports online backups. If you'd like to backup your -database online without any downtime, see the `!admin server` command for the -backup commands and the `database_backup_path` config options in the example -config. Please note that the format of the database backup is not the exact -same. This is unfortunately a bad design choice by Facebook as we are using the -database backup engine API from RocksDB, however the data is still there and can -still be joined together. +If you'd like to backup your database online without any downtime, see the `!admin server` command for the backup commands and the `database_backup_path` config options in the example config. +Please note that the format of the database backup is not the exact same. This is unfortunately a bad design choice by Facebook as we are using the database backup engine API from RocksDB, however the data is still there and can still be joined together. + +#### Restoring online backup To restore a backup from an online RocksDB backup: - shutdown Tuwunel @@ -101,6 +100,7 @@ if you have multiple) to your new directory old one with the new one you crafted - start up Tuwunel again and it should open as normal +### Offline If you'd like to do an offline backup, shutdown Tuwunel and copy your `database_path` directory elsewhere. This can be restored with no modifications needed.