Matrix is an open, decentralised protocol for chat, voice and video, with end-to-end encryption. Anyone can run a homeserver, and homeservers federate: users on your server can talk to users on matrix.org or any other Matrix server, much like email. Synapse is the reference homeserver implementation. Running your own gives you control over your team’s or community’s data, identities and retention.
Before you start
- Your Matrix ID will be
@user:example.com. The part after:(the server name) is permanent and can’t be changed later. You can host the server on a subdomain (matrix.example.com) while users get IDs on your main domain via delegation. - Resources: a small private server (a handful of users, few large rooms) runs on 2 vCPU and 2–4 GB RAM. Joining big public rooms over federation uses much more CPU, RAM and disk.
- Use PostgreSQL (SQLite is only for testing).
Alternatives: Conduit/conduwuit (lightweight, Rust) and Dendrite. Synapse is the most feature-complete.
Docker Compose
# /opt/matrix/compose.yaml
services:
synapse:
image: matrixdotorg/synapse:latest
restart: unless-stopped
volumes:
- ./synapse:/data
ports:
- "127.0.0.1:8008:8008"
depends_on: [db]
db:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_USER: synapse
POSTGRES_PASSWORD: change-me-db
POSTGRES_INITDB_ARGS: "--encoding=UTF8 --lc-collate=C --lc-ctype=C"
volumes:
- ./postgres:/var/lib/postgresql/data
Generate the config for your server name:
cd /opt/matrix
docker run -it --rm -v $PWD/synapse:/data -e SYNAPSE_SERVER_NAME=example.com -e SYNAPSE_REPORT_STATS=no matrixdotorg/synapse:latest generate
Edit synapse/homeserver.yaml to use PostgreSQL (note the C collation above, which Synapse requires):
database:
name: psycopg2
args:
user: synapse
password: change-me-db
database: synapse
host: db
cp_min: 5
cp_max: 10
enable_registration: false
public_baseurl: https://matrix.example.com/
Start it with docker compose up -d, and check docker compose logs -f synapse.
Reverse proxy and federation
Clients and other servers connect over HTTPS. Federation uses port 8448 by default, or 443 with delegation. With Nginx:
server {
server_name matrix.example.com;
listen 443 ssl;
listen 8448 ssl; # federation (optional with delegation)
client_max_body_size 50M;
location ~ ^(/_matrix|/_synapse/client) {
proxy_pass http://127.0.0.1:8008;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
}
# ssl_certificate … (certbot)
}
Don’t proxy /_synapse/admin publicly. Use it via an SSH tunnel or an allow-list.
Delegation: IDs on example.com, server on matrix.example.com
Serve two small JSON files on https://example.com/.well-known/matrix/:
/.well-known/matrix/server → {"m.server": "matrix.example.com:443"}
/.well-known/matrix/client → {"m.homeserver": {"base_url": "https://matrix.example.com"}}
(Serve client with Access-Control-Allow-Origin: *.) Then federation works over port 443, and port 8448 isn’t needed.
Check your setup with the Matrix Federation Tester (federationtester.matrix.org).
Create users
With registration disabled, create accounts from the server:
docker compose exec synapse register_new_matrix_user -c /data/homeserver.yaml http://localhost:8008
Answer the prompts, and make yourself an admin. For teams, connect SSO (OIDC with Keycloak or Authentik), or use registration tokens instead of open registration. Open registration attracts spam bots.
Clients
Element (web, desktop, iOS, Android) is the flagship client. Log in with your homeserver URL. You can host Element Web yourself (static files behind Nginx). Other clients: FluffyChat, Cinny, Nheko.
Set up key backup (recovery key or passphrase) in the client, so encrypted message history survives device loss.
Voice and video: TURN
1:1 calls behind NAT need a TURN server (coturn) configured in Synapse (turn_uris, turn_shared_secret). Group calls use Element Call or Jitsi. Open TURN’s ports (UDP 3478 plus a relay port range) in the firewall.
Maintenance
- Disk: media and room state grow over time. Configure
media_retention, purge old remote media via the admin API, and avoid joining huge public rooms if resources are tight. - Backups: PostgreSQL dumps, plus
synapse/(config, signing key, media store). Losing the signing key breaks federation trust for your server name. See backup strategy. - Updates: Synapse releases often. Read the upgrade notes, and update regularly. See updating self-hosted apps.
Quick reference
| Item | Value |
|---|---|
| Client and federation API | /_matrix via HTTPS |
| Synapse port (internal) | 8008 |
| Federation port | 8448, or 443 with .well-known delegation |
| Create a user | register_new_matrix_user -c /data/homeserver.yaml … |
| Test federation | federationtester.matrix.org |
Related: Mattermost and Rocket.Chat.