Tuwunel is a self-hosted Matrix server you can use instead of matrix-synapse, but written in Rust instead of Python.
This is a maximally simplified installation and setup guide. The essentials:
- tuwunel + element web (the web client) via Docker
- proxied through nginx (a reverse proxy on a single domain)
- wireguard as the link between the home server and the outside world, wireguard guide here
0. Variables you will need to change for yourself (or keep, if they fit)#
10.0.3.197— my server's internal IP; change it to yours, and if everything runs on one server,127.0.0.1will donext-way.by— my domain used for access; change it to yours/opt/matrix— my server path where all Tuwunel files livedff315b705ce— theCONTAINER IDof myjevolk/tuwunel:maininstance on the home server; yours will be different
1. Installing and configuring Docker#
If your Docker is an old version, you may have trouble pulling the container (an error mentioning TAR). If you have an old Docker, e.g. version 20, update it — see the guide here or the official one at https://docs.docker.com/engine/install/ubuntu/
2. Bringing up tuwunel + element web#
Create a file for docker-compose, for example tuwunel.yaml in the
/opt/matrix folder. To avoid problems, first fix the folder permissions
(change the owner to the current user):
sudo chown -R $USER:$USER /opt/
Then create the file nano /opt/matrix/tuwunel.yaml — you only need to change
TUWUNEL_SERVER_NAME and TUWUNEL_REGISTRATION_TOKEN:
services:
homeserver:
### If you already built the tuwunel image with 'docker build' or want to use a registry image,
### then you are ready to go.
image: jevolk/tuwunel:main
restart: unless-stopped
ports:
- 8008:6167
volumes:
- /opt/matrix/tuwunel:/var/lib/tuwunel
#- ./tuwunel.toml:/etc/tuwunel.toml
environment:
TUWUNEL_SERVER_NAME: next-way.by # EDIT THIS
TUWUNEL_DATABASE_PATH: /var/lib/tuwunel
TUWUNEL_PORT: 6167
TUWUNEL_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB
TUWUNEL_ALLOW_REGISTRATION: 'true'
TUWUNEL_REGISTRATION_TOKEN: 'change-it' # A registration token is required when registration is allowed.
#TUWUNEL_YES_I_AM_VERY_VERY_SURE_I_WANT_AN_OPEN_REGISTRATION_SERVER_PRONE_TO_ABUSE: 'true'
TUWUNEL_ALLOW_FEDERATION: 'true'
TUWUNEL_TRUSTED_SERVERS: '["matrix.org"]'
#TUWUNEL_LOG: warn,state_res=warn
TUWUNEL_ADDRESS: 0.0.0.0
#TUWUNEL_CONFIG: '/etc/tuwunel.toml' # Uncomment if you mapped config toml above
element-web:
image: vectorim/element-web:latest
restart: unless-stopped
ports:
- 8009:80
volumes:
- /opt/matrix/element_config.json:/app/config.json
depends_on:
- homeserver
2.1. Configuring element-web#
For element-web to work you need to create the config
nano /opt/matrix/element_config.json, where you must change the values in
m.homeserver for yourself:
{
"default_server_config": {
"m.homeserver": {
"base_url": "https://next-way.by",
"server_name": "next-way.by"
},
"m.identity_server": {
"base_url": "https://vector.im"
}
},
"disable_custom_urls": false,
"disable_guests": false,
"disable_login_language_selector": false,
"disable_3pid_login": false,
"force_verification": false,
"brand": "Element",
"integrations_ui_url": "https://scalar.vector.im/",
"integrations_rest_url": "https://scalar.vector.im/api",
"integrations_widgets_urls": [
"https://scalar.vector.im/_matrix/integrations/v1",
"https://scalar.vector.im/api",
"https://scalar-staging.vector.im/_matrix/integrations/v1",
"https://scalar-staging.vector.im/api"
],
"default_widget_container_height": 280,
"default_country_code": "GB",
"show_labs_settings": false,
"features": {},
"default_federate": true,
"default_theme": "light",
"room_directory": {
"servers": [
"matrix.org"
]
},
"enable_presence_by_hs_url": {
"https://matrix.org": false,
"https://matrix-client.matrix.org": false
},
"setting_defaults": {
"breadcrumbs": true
},
"jitsi": {
"preferred_domain": "meet.element.io"
},
"element_call": {
"url": "https://call.element.io",
"brand": "Element Call"
},
"map_style_url": "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx"
}
2.2. The actual start#
docker compose -f tuwunel.yaml up -d
# -f — points to the file
# up -d — starts in the background
We can use docker ps to check that everything started without errors.
3. nginx setup#
You will also need a VPS and an
Arecord for the domain — that is beyond the scope of this guide. All nginx steps happen on the VPS with a public IP wherewireguardis already installed to link the VPS + the home server.
First we generate Let's Encrypt certificates using certbot (find the certbot
installation at https://certbot.eff.org/instructions/):
certbot --nginx -d next-way.by
After that you will need to remove the extra stuff certbot added; the only thing to remember is the paths to the public and private keys — certbot might place them somewhere different from my guide.
Let's create the proxying config for Tuwunel and element web:
nano /etc/nginx/sites-enabled/next-way.by
upstream tuwunel {
server 10.0.3.197:8008; # IP and port where tuwunel is listening
}
server {
# Client-Server API over HTTPS (port 443)
listen 443 ssl;
listen [::]:443 ssl;
# Matrix Federation over HTTPS (port 8448)
# Only needed if you want to federate with other homeservers
# Don't forget to open port 8448 in your firewall!
listen 8448 ssl;
listen [::]:8448 ssl;
# http2 on;
server_name next-way.by;
# Nginx standard body size is 1MB, which is quite small for media uploads
# Increase this to match the max_request_size in your tuwunel.toml
client_max_body_size 100M;
location / {
proxy_pass http://10.0.3.197:8009;
# Preserve host and scheme - critical for proper Matrix operation
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto https;
}
# Forward requests to Tuwunel
location ~ ^(/_matrix|/_synapse/client|/_synapse/admin|/_synapse/mas) {
proxy_pass http://10.0.3.197:8008;
# Preserve host and scheme - critical for proper Matrix operation
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto https;
}
# TLS configuration (Let's Encrypt example using certbot)
ssl_certificate /etc/letsencrypt/live/next-way.by/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/next-way.by/privkey.pem; # managed by Certbot
}
server {
if ($host = next-way.by) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80 ;
listen [::]:80 ;
server_name next-way.by;
return 404; # managed by Certbot
}
In my case the config did not work with
http2 on;, but yours might be fine; my problem is most likely that I had changed global settings earlier. I took the rule forelement webfrom https://habr.com/ru/articles/1028012/.
Check the configuration:
nginx -t
and if everything is fine, apply it:
service nginx reload
If all is well, opening your domain shows you element-web.
4. Creating a user#
Since we run Tuwunel in Docker, we do not have direct CLI access and need to
execute the CLI from inside the docker container. On the home server run
docker ps to find the CONTAINER ID of jevolk/tuwunel:main and create a
user with admin rights:
# Check that the CLI is accessible
docker exec -it dff315b705ce tuwunel -h
# Create the user
docker exec -it dff315b705ce tuwunel --execute "users create_user your_user_name"
The reply will contain the login and password. Personally I had login issues
later, but everything was fixed by simply restarting the container:
docker restart dff315b705ce.
Afterword#
I use the FluffyChat client https://matrix.org/ecosystem/clients/fluffychat/ — it is a bit awkward, but it supports Multi Account.
After logging in you will land in a room (chat) that also contains the bot
@conduit:next-way.by (yours will have your domain), which can run CLI
commands via chat commands. To get help, type in the chat:
!admin --help