English · Русский
An automated installer for a self-hosted Matrix infrastructure with messaging, voice calls, and video calls.
The project deploys a complete Docker Compose stack on a single Linux server:
- Matrix Synapse
- PostgreSQL
- Caddy
- Coturn
- LiveKit
- MatrixRTC JWT Service (
lk-jwt-service).
It is intended for personal, family, and private deployments. Public registration is disabled by default; accounts are created by an administrator through the user and admin commands.
- ✔ Debian 13.6
- ✔ Ubuntu 24.04 LTS
- ✔ Ubuntu 26.04 LTS
- ✔ Full installation
- ✔ Administrator creation
- ✔ User creation
- ✔ User discovery
- ✔ Direct messaging
- ✔ Push notifications
- ✔ Voice calls
- ✔ Video calls
- ✔ Matrix Federation
- ✔ Backup
- ✔ Restore
Other recent Debian and Ubuntu releases are expected to work but have not been validated.
- automated installation of the complete Matrix stack
- automatic HTTPS certificates through Caddy and Let's Encrypt
- Matrix federation over
443/tcp, without exposing port8448 - TURN/STUN through Coturn
- MatrixRTC voice and video calls through LiveKit
- PostgreSQL instead of embedded SQLite
- automatic Docker and Docker Compose installation
- UFW configuration that preserves the detected SSH port
- optional swap configuration for low-memory VPS instances
- built-in health checks for every component
- regular user and administrator account creation
- backup and restore
- guarded reconfiguration with an automatic pre-update backup.
install [--force] [--yes]
check
user USERNAME
admin USERNAME
backup
restore [ARCHIVE]
Examples:
sudo bash ./matrix-node.sh install
sudo bash ./matrix-node.sh check
sudo bash ./matrix-node.sh user USERNAME
sudo bash ./matrix-node.sh admin USERNAME
sudo bash ./matrix-node.sh backup
sudo bash ./matrix-node.sh restore [ARCHIVE]Supported and validated operating systems:
- Debian 13.6
- Ubuntu 24.04 LTS
- Ubuntu 26.04 LTS.
Recommended server configuration:
CPU: 2 vCPU
RAM: 2 GB+
Disk: 20 GB+
Public IPv4: required
Domain name: required
The domain's DNS A record must point to the public IPv4 address of the server. Check it before installation:
dig +short matrix.example.orgThe returned address must match the VPS address. The installer performs the same check before requesting a TLS certificate.
Required ports:
| Port | Protocol | Purpose |
|---|---|---|
80 |
TCP | HTTP and ACME challenge |
443 |
TCP | HTTPS, Matrix Client API, and Federation API |
3478 |
TCP/UDP | Coturn TURN/STUN |
5349 |
TCP | Coturn over TLS |
49160-49200 |
UDP | TURN relay |
7881 |
TCP | LiveKit RTC over TCP |
50000-50100 |
UDP | LiveKit RTC |
Copy matrix-node.sh to the server, then run:
chmod +x matrix-node.sh
sudo bash ./matrix-node.sh installThe installer asks for the Matrix domain and, when needed, the public IPv4 address. PostgreSQL, Coturn, and LiveKit passwords and keys are generated automatically and stored in protected configuration files.
A successful installation ends with output similar to:
Healthcheck passed.
Installation completed: https://matrix.example.org
To apply the firewall rules without an interactive confirmation prompt:
sudo bash ./matrix-node.sh install --yesThe --yes option applies only to the UFW confirmation in install mode. Other safety checks remain enabled.
The installer stops when it detects an existing stack to prevent accidental changes. To apply the current configuration to an existing deployment, use:
sudo bash ./matrix-node.sh install --forceBefore changing a running deployment, the installer automatically creates a backup.
sudo bash ./matrix-node.sh checkThe check covers:
Matrix/Synapse
PostgreSQL
Caddy/TLS
Federation
Coturn
LiveKit
lk-jwt-service
UFW
Successful result:
Healthcheck passed.
Create an administrator:
sudo bash ./matrix-node.sh admin aliceCreate a regular user:
sudo bash ./matrix-node.sh user bobThe password is not passed as an argument to the installer. It is requested interactively:
Password:
Confirm password:
Example result:
Admin created: @alice:matrix.example.org
User created: @bob:matrix.example.org
Homeserver: https://matrix.example.org
Username: alice
Password: the account password
Matrix ID: @alice:matrix.example.org
Compatible with standard Matrix clients.
Internet
|
v
Caddy :443
|
+--> Synapse :8008
|
+--> LiveKit :7880
|
+--> lk-jwt-service :8080
Coturn:
3478/tcp+udp
5349/tcp
49160-49200/udp
LiveKit RTC:
7881/tcp
50000-50100/udp
Default containers:
| Container | Default image |
|---|---|
postgres |
postgres:16-alpine |
synapse |
ghcr.io/element-hq/synapse:latest |
caddy |
caddy:2 |
coturn |
coturn/coturn:4.6.3 |
livekit |
livekit/livekit-server:latest |
lk-jwt-service |
ghcr.io/element-hq/lk-jwt-service:latest |
Image versions can be pinned with the SYNAPSE_VERSION, POSTGRES_VERSION, COTURN_VERSION, LIVEKIT_VERSION, LK_JWT_VERSION, and CADDY_VERSION environment variables.
/.well-known/matrix/server -> federation address at DOMAIN:443
/.well-known/matrix/client -> homeserver and MatrixRTC LiveKit focus
/livekit/jwt/* -> lk-jwt-service:8080
/livekit/sfu/* -> LiveKit:7880
all other requests -> Synapse:8008
PostgreSQL and the Synapse backend port are not exposed directly to the internet.
Caddy publishes /.well-known/matrix/server with this response:
{
"m.server": "matrix.example.org:443"
}Connection flow:
remote homeserver
-> https://matrix.example.org/.well-known/matrix/server
-> matrix.example.org:443
-> Caddy
-> Synapse:8008
Port 8448/tcp does not need to be exposed.
Federation can be checked with the Matrix Federation Tester. Enter the domain only, without https:// or a port number.
curl -fsS "https://matrix.example.org/.well-known/matrix/server"
curl -fsS "https://matrix.example.org/.well-known/matrix/client"
curl -i "https://matrix.example.org/_matrix/federation/v1/version"
curl -i "https://matrix.example.org/_matrix/key/v2/server"
curl -i "https://matrix.example.org/_matrix/client/versions"Create a backup:
sudo bash ./matrix-node.sh backupThe archive is created in .backups next to the script. It contains the configuration, secrets, Synapse data, Caddy, Coturn and LiveKit configuration, and a consistent PostgreSQL dump. Keep important backup copies outside the VPS.
Restore the most recent available archive:
sudo bash ./matrix-node.sh restoreRestore a specific archive:
sudo bash ./matrix-node.sh restore /path/to/matrix_backup_TIMESTAMP.tar.gzIf the current stack is running, the installer creates a safety backup before restoring the selected archive.
.backups/ backup archives
.matrix-node-state/ run logs and internal state
.secrets.env generated secrets
docker-compose.yml generated container configuration
postgres/ PostgreSQL data
synapse/ Synapse configuration and data
caddy/ Caddy configuration and data
coturn/ Coturn certificates
livekit/ LiveKit configuration
Do not commit these generated files or directories to a public repository.
The project is designed for private deployments:
- public registration is disabled by default
- users can only be created by the administrator
- no administrator account is created automatically
- user passwords are requested interactively
- PostgreSQL and the Synapse backend are not exposed publicly
- TLS terminates at Caddy
- the firewall exposes only the required ports
- secrets and configuration files are created with restricted permissions.
Never publish:
.secrets.env
private keys
TURN secret
LiveKit API secret
PostgreSQL password
Synapse signing key
backup archives
The user password is not passed as an argument to the main script. During account creation, however, it is briefly passed to the internal register_new_matrix_user tool with its -p option.
This is accepted as a practical compromise for small private deployments. Environments with stronger security requirements should replace it with stdin-based input or another mechanism that prevents the password from appearing in a process argument list.
During installation, the script may:
- install Docker, Docker Compose, and required system packages
- configure and enable UFW
- add firewall rules for the detected SSH port and Matrix services
- set
vm.swappiness - create
/swapfileand an/etc/fstabentry on low-memory systems.
Review the script before running it and keep an independent backup of important data.
Check DNS and HTTP connectivity:
dig +short matrix.example.org
curl -I http://matrix.example.orgThe A record must point to the current VPS public address, and 80/tcp must be reachable from the internet.
ls -la ./coturn/certs/
ss -ltnp | grep 5349The Coturn container must be able to read DOMAIN.crt and DOMAIN.key.
curl -fsS "https://matrix.example.org/.well-known/matrix/server"
curl -i "https://matrix.example.org/_matrix/federation/v1/version"The first request should return matrix.example.org:443; the second should return Synapse version information.
Check:
- Coturn on
3478/tcp,3478/udp, and5349/tcp - TURN relay range
49160-49200/udp - LiveKit on
7881/tcpand50000-50100/udp - the
/.well-known/matrix/clientresponse - the
/livekit/jwt/*and/livekit/sfu/*routes.
For a realistic test, place the devices on different networks, such as Wi-Fi and a mobile network.
Create a backup before any manual update:
sudo bash ./matrix-node.sh backupThe recommended way to apply the current installer configuration is:
sudo bash ./matrix-node.sh install --force
sudo bash ./matrix-node.sh checkImages tagged latest may introduce incompatible changes. For stable operation, pin container versions with the supported environment variables.
After installation, validate:
- login with the selected Matrix client
- room creation and messaging
- voice and video calls
- calls over a mobile network
- TURN fallback
- federation between different homeservers.
These scenarios depend on specific clients, networks, and carriers and are outside the built-in installer health check.
Create a backup first:
sudo bash ./matrix-node.sh backupStop the containers from the working directory:
docker compose downDeleting the working directory destroys configuration, secrets, and service data. Ensure that a tested backup is stored elsewhere before removing anything.
This project is provided as-is, without warranties of operation, compatibility, or data preservation. The administrator is responsible for the server, domain, users, updates, backups, deployment security, and compliance with applicable law.
Before production use, review the script, verify DNS and firewall rules, create a backup, and test the restore process.