How to Set Up a Matrix Server with Apache Reverse Proxy

View Count
Published Thursday, February 12, 2026 at 08:15:00 PM EST <- Back

Authors

Table of Contents

Introduction

Before we start, I have linked the step-by-step guide here 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 of this particular announcement that spurred me to action was the recent 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. That is when I found Matrix. As someone with a background in IT who runs a home-lab [I run some home-lab alternatives, such as Immich (A Google Photos Alternative), Jellyfin (A Netflix Alternative), and Truenas (A Network Storage server that runs the previous apps while also replace Google Drive - I personally use HexOS to make Truenas easier)] I set out to spin up a Matrix server.

Initial Set Up

I began my journey by looking into exactly what Matrix is. Matrix is a federated service protocol, meaning its like e-mail, making it possible to have a bunch of different providers that can all talk and send info to one another using a defined protocol. This means that by its very nature it is decentralized. Using the e-mail analogy, a gmail account allows you to e-mail a yahoo account. If yahoo shuts down, you still have your e-mail and people can still e-mail you from other providers. Matrix works the same way. You have an account on a server that allows you to communicate with anyone else that has 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 message a user from 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's get into the set-up. I began by making a fresh Hyper-V virtual machine (any VM software should work, VirtualBox is a good choice too). I then installed Ubuntu Server on that machine. I chose to go with a new virtual machine because I did not have a dedicated kubernetes machine. I chose this to allow all my kubernetes programs to run on one machine. I chose to go with the Element Server Suite (ESS) Community Edition, as it had several features I wanted, including a web-based client built into the package. I installed it by following the guide, but it wasn't working quite yet. I use an Apache server as a reverse proxy and web host (allowing me to manage all my SSL certificates in one place and use https with non-https services). Because of this, I needed to configure Apache to work with Matrix.

Troubleshooting

This is where I started running into problems. Most of the documentation on ESS is for the Pro version and doesn't really help when using the Community Edition. My first problem 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 are specific files that your domain is checked for located at "/.well-known/matrix/". The problem is that they are generated inside the kubernetes, but they need to be located on the main domain, rather than your matrix subdomain. This requires you to modify your apache configuration for your main site to direct "/.well-known/matrix" url calls to your kubernetes box. Once I had done that, I began running into a different issue with the client configuration.

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. This requires modifying the "/.well-known/matrix/client" file. This would have been easier to do during the initial install, but with some research into the kubernetes commands I was able to change it after I had installed Matrix.

Once I had finished fixing those issues I encountered my final and largest issue, voice calls. Matrix uses a Real-Time Communications (RTC) server to handle 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 solution for the problem I was having. Turns out, the RTC server uses websockets to handle the calls. Not too surprising, but the problem I had was that I was not properly passing the http-upgrade-to-websocket requests into the RTC server. They were getting stuck in my apache server until I tweaked the Apache configuration to properly pass the upgrade requests. Now, 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, succeeding despite the issues I ran into. I would highly recommend doing this if you are concerned about the growing amount of personal information that companies are amassing and regularly losing to breaches (a record breaking 3,332 U.S. based data breaches in 2025 alone[3]). Hopefully, you can use the below guide to skip the hours of 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. The 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. The 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 `~/ess-config-values/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