Deploying into service

June 22, 2026 · View on GitHub

Read the Generic guide first regardless of which platform you ultimately use. It explains the universal requirements — binary selection, database configuration, the system user, and the systemd unit — that every other guide builds on.

Choosing a reverse proxy

Tuwunel listens on plain HTTP and requires a reverse proxy to terminate TLS. All guides assume HTTPS is already handled externally.

Caddy is the recommended choice. It handles TLS automatically via Let's Encrypt, and the full Tuwunel configuration fits in two lines. It also proxies port 8448 (Matrix federation) in the same block with no extra work.

Nginx is a solid choice if you are already running it. The configuration is more verbose but well-documented. Set client_max_body_size to match or exceed Tuwunel's max_request_size (default 20 MiB), and never use $request_uri in the proxy pass — it causes subtle breakage.

Warning

Apache and Lighttpd are not supported for Matrix federation. Both alter the X-Matrix authorization header that federation depends on. Nginx and Caddy handle it correctly.

Traefik works well in Docker environments. Note that Traefik cannot serve .well-known files by itself — you need a companion nginx container for federation discovery, or expose port 8448 directly as a Traefik entrypoint. Also check whether you are running a version between 3.6.4–3.6.6 or 2.11.32–2.11.34, which contain a bug that requires an extra workaround flag.

Root-domain delegation

By default, Tuwunel's server name is the domain that appears in Matrix user IDs (@alice:example.com), which must exactly match the host Tuwunel presents when federating. If you want to host Matrix under a subdomain (matrix.example.com) while users have addresses on the root domain (example.com), configure root-domain delegation. This serves .well-known/matrix/client and .well-known/matrix/server from the root domain pointing to the subdomain, and requires no changes to DNS beyond what is already needed for your web server.

Things to know before getting started

Pick the right binary. Prebuilt binaries for x86_64 come in -v1-, -v2-, and -v3- CPU-optimized variants. Running the wrong one produces an Illegal Instruction crash. The generic guide includes a command to check which variant your CPU supports; -v2- or better is recommended for RocksDB's CRC32 performance.

RocksDB is the only supported database. SQLite support has been removed. A RocksDB database from Conduit or a fork of conduwuit migrates in place on first boot: stop the source server, back up its data directory and media, then start Tuwunel against it. Tuwunel reconciles the schema version, room history, account data, and media automatically; no flags are required. For a Conduit source using the content-addressed media layout, set conduit_media_directory_depth and conduit_media_directory_length to match its media.directory_structure: Conduit v0.10.0 stored media flat, so use conduit_media_directory_depth = 0, while v0.10.1 and later shard it (the default). If media lived outside <database_path>/media, set conduit_source_media_path; if it lived in an S3 bucket, see importing media from a Conduit S3 bucket. SQLite databases are not supported.

Port 8448 matters for federation. Clients connect on port 443, but other Matrix homeservers connect on port 8448. Both must be reachable for a fully functional server. NAT hairpinning or split-horizon DNS may be needed for internal clients that need to reach the same domain.

Container images are minimal. Docker and Podman images contain only the binary, a minimal init (tini), and CA certificates — no shell. If you need to inspect a running container you will need to exec into it using the binary directly.

Rootless Podman requires linger. Without loginctl enable-linger, rootless containers stop when the user logs out. The Podman guide uses quadlet files and user-level systemd to handle this correctly.

NixOS has platform-specific workarounds. The community services.matrix-conduit NixOS module defaults to SQLite, requires a manual workaround for UNIX socket support, and conflicts with jemalloc when a hardened profile is enabled. The NixOS guide covers all three.