How to Set Up a Matrix Server with Reverse Proxy

View Count

Published Thursday, February 12, 2026 at 08:15:00 PM EST

Authors

Table of Contents

Introduction

Before we start I have made the step-by-step guide linked in the Table of Contents so you can skip the story and jump straight to the guide if you don't want to read my adventure.

If your anything like me, the recent news about discord rolling out it's age-verification globally[1] has you a bit on edge. I am not one that particularly likes corporations having all my info, but I tolerate it for some services. The part that made this particular announcement spur me to action is the fact that they recently had a massive data breach of "a third party service provider ... [effecting] approximately 70,000 users that may have had government-ID photos exposed"[2]. Due to privacy and identity theft concerns I began to look into alternatives to discord that I could host myself, and that is when I found Matrix. As someone with a background in IT and running a home-lab (I run some home-lab alternatives, Immich (A Google Photos Alternative), Jellyfin (A Netflix Alternative), and Truenas (A Network Storage server that runs the previous apps and can replace Google Drive - I personally use HexOS to make the Truenas easier)) I set out to spin up a Matrix server.

Initial Set Up

I began my journey by looking into what exactly Matrix is. Matrix is a federated service protocol, that means its like e-mail, you can have a bunch of different providers that can all talk and send info to one another using a defined protocol. This means by its very nature it is decentralized, using the e-mail analogy, if you have an account at gmail, you can talk to people that have an account at yahoo, and if yahoo shuts down you still have your e-mail, and people can still talk to you from other e-mail providers like proton. Matrix works the same way, you have an account on a server and can talk to anyone else with a Matrix account, even if they are not on the same server.

This is nice because it allows you to host your own server and store all your personal information on it, only sending the messages to another server if you talk to a user on that server. Matrix also supports end-to-end encryption, allowing any group of people to communicate securely as log as everyone's clients support the encryption.

Now that we have a better understanding of why I chose Matrix, let get into the set-up. I started setting it up by making a fresh Hyper-V virtual machine (any VM software should work, VirtualBox is a good choice too) and installing Ubuntu Server on it. I chose to go with a new virtual machine because I did not have a dedicated kubernetes machine and I wanted to make one for any kubernetes based servers I may want to add in the future. I chose to go with the Element Server Suite (ESS) Community Edition, as it had several feature I wanted, including a web-based client built into the package. I followed the guide and got it installed, but it was not quite working. See I use an Apache server as a reverse proxy and web host (This is so that I can manage all my SSL certificates in one place, and use https with non-https services), so I needed to configure it to work.

Troubleshooting

This is where I started running into problems. See most of the documentation on ESS is for the Pro version, so it doesn't really help when using the Community Edition. The first problem I ran into was that my server was not able to be found by clients or by other servers. (You can test and see if your server can be found by other servers with this tool.) Turns out that there is specific files that your domain is checked for located at "/.well-known/matrix/". The problem with this is that they are generated inside the kubernetes, and need to be located on the main domain, not your matrix subdomain. To do this you need to modify your apache configuration for the main site to direct /.well-known/matrix url calls to your kubernetes box. Once I had done that I began running into a different issues with the client configuration. See I wanted to use a central identity server hosted by Element, Vector.im. An Identity server is a central location that can pair your e-mail and phone with your username, allowing people with your contact information to look you up on Matrix. The problem with this is that it requires modifying the /.well-known/matrix/client file, which would have been easier to do during the initial install.

Once I had finished fixing all those issues I encountered my final and largest issue, voice calls. See Matrix uses a Real-Time Communications (RTC) server to handel the voice and video calls. This requires some port-forwarding for it to work, which I had already done. It took me several hours of research before I would eventually stumble onto the problem I was have. See the RTC server uses websockets to handel the calls, not too surprising, but the problem I had was that I was not properly passing the http upgrade to websocket requests to the RTC server, instead they were getting stuck in my apache server. Once I had tweaked the Apache configuration to properly pass the upgrade requests I finally had a working Matrix server, completely behind my reverse proxy.

Conclusion

I set out to run an alternative to Discord on my own hardware, and I succeeded, despite the issues I ran into. I would highly recommend doing this if you are concerned about the growing mass of personal information companies have and are regularly having breached (a record breaking 3,332 U.S. based data breaches in 2025 alone[3]). Hopefully you can use the below guide to skip the hour os troubleshooting it took for me to get it working.

Step-By-Step Guide

Note: These steps are for debian-based linux, specifically Ubuntu Server. This guide is similar to the one found on the project's Github, however I have tweaked some of the steps to make it work with an apache reverse proxy server.
  1. Your first step is to register your domain and subdomains with your registrar (I personally use No-IP). You want to register the main domain and then five subdomains. The tld below stands for top-level domain, which is (com, org, us, io, net, etc.) These are:
    • yourdomain.tld
    • account.yourdomain.tld
    • admin.yourdomain.tld
    • chat.yourdomain.tld
    • matrix.yourdomain.tld
    • mrtc.yourdomain.tld
  2. Your second step is to set up a kubernetes box using the steps below. (Skip to step three if you already have a kubernetes box with Helm installed)
    1. Run the following command to install K3s:
    2. Once K3s is set up, copy its kubeconfig to your home directory to get access to it:
    3. Add export KUBECONFIG=~/.kube/config to ~/.bashrc to make it persistent:
    4. Install Helm, the Kubernetes Package Manager. You can use your OS repository or call the following command:
  3. Next you need to port forward the following ports to your kubernetes box:
    • TCP 30881: This port will be used for the TCP WebRTC connections of Matrix RTC Backend.
    • UDP 30882: This port will be used for the Muxed WebRTC connections of Matrix RTC Backend.
  4. Create your Kubernetes namespace where you will deploy the Element Server Suite Community:
  5. Create a directory containing your Element Server Suite configuration values:
  6. Use the following command to get the external-ip provisioned by Kubernetes for Traefik :

    The results will look something like:
    NAME      TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
    traefik   LoadBalancer   10.43.184.49   172.20.1.60   80:32100/TCP,443:30129/TCP   5d18h
  7. In such a case, you will need to set up K3S with custom ports. Create a file `/var/lib/rancher/k3s/server/manifests/traefik-config.yaml` with the following:

    traefik-config.yaml

  8. K3s will apply the file content automatically. You can verify its ports using the command :

    The results will look something like:
    traefik          LoadBalancer   10.43.184.49    172.20.1.60   8080:32100/TCP,8443:30129/TCP   5d18h
  9. If the certificates are handled in your reverse proxy, you can point to port 8080 (HTTP) only and disable TLS in ESS. Copy the file 'charts/matrix-stack/ci/fragments/quick-setup-external-cert.yaml' to tls.yaml.
  10. Configure your reverse proxy so that the DNS names you configured are routed to the external IP of Traefik on port 8080 (HTTP) and 8443 (HTTPS). (Since I am using the reverse proxy for the certificates, you will see the Apache site configuration files below contain only port 8080):

    account.yourdomain.tld.conf

    account.yourdomain.tld-le-ssl.conf

    admin.yourdomain.tld.conf

    admin.yourdomain.tld-le-ssl.conf

    chat.yourdomain.tld.conf

    chat.yourdomain.tld-le-ssl.conf

    matrix.yourdomain.tld.conf

    matrix.yourdomain.tld-le-ssl.conf

    mrtc.yourdomain.tld.conf

    mrtc.yourdomain.tld-le-ssl.conf

  11. For a quick setup using the default settings, copy the file from 'charts/matrix-stack/ci/fragments/quick-setup-hostnames.yaml' to hostnames.yaml in your ESS configuration values directory and edit the hostnames accordingly.
  12. Create the following ~/ess-config-values/values.yaml file:

    values.yaml

  13. Run the setup using the following helm command (If it times out you can add `--timeout 10m` to the end to extent the timeout time (Default is 5m) as long as you need (10m is 10 minutes)):
  14. Once that is complete it is time to set up your first user using the command below:
  15. The final step is to test and make sure everything works, listed below is what you need to check:
    1. Log into your Element Web client website (https://client.yourdomain.tld) and log in with the user you created above.
    2. Verify that federation works fine using Matrix Federation Tester.
    3. Login with an Element X mobile client with the user you created above.
    4. Login with a desktop client like Element
    5. Preform a test video call to verify that the RTC server is working properly.

Resources and Information

References