How to Self-Host Matrix and Element (Docker Compose)
Version 2
This is a complete guide on setting up Matrix (Synapse) and Element on a fresh Ubuntu 20.04, Debian 11, CentOS or Fedora server.
Need a Server?
Hetzner - €20 credit on us (ref) - Free Arm instance for 4 months!
Vultr - $100 credit on us (ref) - Expires after 30 days
Contents
What is Matrix?
Server Setup
Install UFW
Setup Sudo User
Install Docker
Install Matrix and Element
Create New Users
Reverse Proxy
Login
If your server is already setup feel free to skip.
What is Matrix?
Matrix is an open standard and communication protocol for real-time communication. It aims to make real-time communication work seamlessly between different service providers, just like standard Simple Mail Transfer Protocol email does now for store-and-forward email service, by allowing users with accounts at one communications service provider to communicate with users of a different service provider via online chat, voice over IP, and videotelephony. Such protocols have been around before such as XMPP but Matrix is not based on that or another communication protocol. From a technical perspective, it is an application layer communication protocol for federated real-time communication. It provides HTTP APIs and open source reference implementations for securely distributing and persisting messages in JSON format over an open federation of servers. It can integrate with standard web services via WebRTC, facilitating browser-to-browser applications. Wikipedia
Server Setup
-
Update
Ubuntu/Debian:sudo apt update && sudo apt upgrade
CentOS/Fedora:sudo dnf upgrade
-
Install automatic updates
Ubuntu/Debian:sudo apt install unattended-upgrades
CentOS/Fedora:sudo dnf install -y dnf-automatic
-
Change SSH Port:
sudo nano /etc/ssh/sshd_config
Remove the # infront of Port 22 and then change it (30000-50000 is ideal).
This is security though obsucurity which is not ideal but port 22 just gets abused by bots.
-
Restart SSH:
sudo systemctl restart sshd
-
Install fail2ban
Ubuntu/Debian:sudo apt install fail2ban
CentOS/Fedora:sudo dnf install fail2ban
Install UFW Firewall
-
Install
Ubuntu/Debian:sudo apt install ufw
CentOS/Fedora:sudo dnf install ufw
-
Replace SSH-PORT to your SSH port:
sudo ufw allow <SSH-PORT>/tcp
-
Allow HTTP/s traffic:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
- Enable Firewall:
sudo ufw enable
Setup a sudo user
adduser <USERNAME>
- Add user to sudoers
sudo adduser <USERNAME> sudo
- Login as the new user
su - <USERNAME>
Install Docker
Official Docs:
Ubuntu
Debian
CentOS
Fedora
Install Matrix and Element
-
Create docker network, this is so Matrix and Element can be on their own isolated network:
sudo docker network create --driver=bridge --subnet=10.10.10.0/24 --gateway=10.10.10.1 matrix_net
-
Create Matrix directory:
sudo mkdir matrix && cd matrix
-
Use the following template:
sudo nano docker-compose.yaml
version: '2.3'
services:
postgres:
image: postgres:14
restart: unless-stopped
networks:
default:
ipv4_address: 10.10.10.2
volumes:
- ./postgresdata:/var/lib/postgresql/data
# These will be used in homeserver.yaml later on
environment:
- POSTGRES_DB=synapse
- POSTGRES_USER=synapse
- POSTGRES_PASSWORD=STRONGPASSWORD
element:
image: vectorim/element-web:latest
restart: unless-stopped
volumes:
- ./element-config.json:/app/config.json
networks:
default:
ipv4_address: 10.10.10.3
synapse:
image: matrixdotorg/synapse:latest
restart: unless-stopped
networks:
default:
ipv4_address: 10.10.10.4
volumes:
- ./synapse:/data
networks:
default:
external:
name: matrix_net
-
Create Element Config
sudo nano element-config.json
Copy and paste example contents into your file. -
Remove
"default_server_name": "matrix.org"
(top line) fromelement-config.json
as this is deprecated -
Add our custom homeserver to the top of
element-config.json
:
"default_server_config": {
"m.homeserver": {
"base_url": "https://matrix.example.com",
"server_name": "matrix.example.com"
},
"m.identity_server": {
"base_url": "https://vector.im"
}
},
- Generate Synapse Config:
sudo docker run -it --rm \
-v "$HOME/matrix/synapse:/data" \
-e SYNAPSE_SERVER_NAME=matrix.example.com \
-e SYNAPSE_REPORT_STATS=yes \
matrixdotorg/synapse:latest generate
- Comment out sqlite database (as we have setup postgres to replace this):
sudo nano synapse/homeserver.yaml
:
#database:
# name: sqlite3
# args:
# database: /data/homeserver.db
- Add the Postgres config:
sudo nano synapse/homeserver.yaml
database:
name: psycopg2
args:
user: synapse
password: STRONGPASSWORD
database: synapse
host: postgres
cp_min: 5
cp_max: 10
- Deploy:
sudo docker compose up -d
Create New Users
- Access docker shell:
sudo docker exec -it matrix_synapse_1 bash
register_new_matrix_user -c /data/homeserver.yaml http://localhost:8008
- Follow the on screen prompts
- Enter
exit
to leave the container's shell
To allow anyone to register an account set 'enable_registration' to true in the homeserver.yaml. This is NOT recomended.
Install Reverse Proxy (Caddy)
Caddy will be used for the reverse proxy. This will handle incomming HTTPS connections and forward them to the correct docker containers. It a simple setup process and Caddy will automatically fetch and renew Let's Encrypt certificates for us!
- Follow this setup guide:
- Head to your user directory:
cd
- Create Caddy file:
sudo nano Caddyfile
Recommended Caddy Template:
- Limited Matrix paths (based on docs)
- Security Headers
- No search engine indexing
matrix.example.com {
reverse_proxy /_matrix/* 10.10.10.4:8008
reverse_proxy /_synapse/client/* 10.10.10.4:8008
header {
X-Content-Type-Options nosniff
Referrer-Policy strict-origin-when-cross-origin
Strict-Transport-Security "max-age=63072000; includeSubDomains;"
Permissions-Policy "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=(), interest-cohort=()"
X-Frame-Options SAMEORIGIN
X-XSS-Protection 1
X-Robots-Tag none
-server
}
}
element.example.com {
encode zstd gzip
reverse_proxy 10.10.10.3:80
header {
X-Content-Type-Options nosniff
Referrer-Policy strict-origin-when-cross-origin
Strict-Transport-Security "max-age=63072000; includeSubDomains;"
Permissions-Policy "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=(), interest-cohort=()"
X-Frame-Options SAMEORIGIN
X-XSS-Protection 1
X-Robots-Tag none
-server
}
}
- Enable the config:
caddy reload
Login
- Head to your element domain and login!
Don't forget to update frequently
Pull the new docker images and then restart the containers:
sudo docker compose pull && sudo docker compose up -d