ITcompressor IT — compressed and to the point
← Back to articles

Installing and configuring tuwunel with Docker

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.1 will do
  • next-way.by — my domain used for access; change it to yours
  • /opt/matrix — my server path where all Tuwunel files live
  • dff315b705ce — the CONTAINER ID of my jevolk/tuwunel:main instance 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 A record for the domain — that is beyond the scope of this guide. All nginx steps happen on the VPS with a public IP where wireguard is 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 for element web from 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

← Back to articles