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.- 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
- 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)
- Run the following command to install K3s:
Copy to clipboardcurl -sfL https://get.k3s.io | sh - - Once K3s is set up, copy its kubeconfig to your home directory to get access
to it:
- Add "export KUBECONFIG=~/.kube/config" to `~/.bashrc` to make it
persistent:
Copy to clipboardecho 'export KUBECONFIG=~/.kube/config' >> ~/.bashrc - Install Helm, the Kubernetes Package Manager. You can use your OS repository
or call the following command:
- Run the following command to install K3s:
- 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.
- Create your Kubernetes namespace where
you will deploy the Element Server Suite
Community:
Copy to clipboardkubectl create namespace ess - Create a directory containing your
Element Server Suite configuration values:
Copy to clipboardmkdir ~/ess-config-values - Use the following command to get the
external-ip provisioned by Kubernetes for
Traefik :
Copy to clipboardkubectl get svc/traefik -n kube-system
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 - 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
- K3s will apply the file content
automatically. You can verify its ports using the
command :
Copy to clipboardkubectl get svc -n kube-system | grep traefik
The results will look something like:
traefik LoadBalancer 10.43.184.49 172.20.1.60 8080:32100/TCP,8443:30129/TCP 5d18h - 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`.
- 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
- 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.
- Create the following
`~/ess-config-values/values.yaml` file:
values.yaml
- 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)):
- Once that is complete, it is time to
set up your first user using the command
below:
- The final step is to test and make
sure everything works, listed below is what you
need to check:
- Log into your Element Web client website (https://client.yourdomain.tld) and log in with the user you created above.
- Verify that federation works fine using Matrix Federation Tester.
- Login with an Element X mobile client with the user you created above.
- Login with a desktop client like Element
- Preform a test video call to verify that the RTC server is working properly.
Resources and Information
- Resources used in this Project:
- Matrix - Information about the Protocol, server options, and client options
- VirtualBox - Virtual Machine Hosting Software
- Ubuntu Server - The Operating System used in this Setup
- Kubernetes Documentation - In case you get stuck trying to do something not covered here
- Element Server Suite (ESS) Community Edition - The Matrix server used
- Element Server Suite (ESS) Community Edition Github - Contains the guide that this was based from, as well as extra documentation
- Matrix Federation Tester - A toll that will test the domain you give it to see if it can find your Matrix server
- Vector.im - This is the link to the identity server used in this set up, but you could use a different one
- Matrix-Stack Documentation - Good resource if you want to add more values to the values.yaml file to modify the setup
- LetsEncrypt - Free SSL Certificate service
- Certbot - The tool used to obtain signed SSL certificates from LetsEncrypt
- Other Self Hosting Alternatives: