What Synapse asks of the machine
| Resource | What drives it | Synapse documentation |
|---|---|---|
| RAM | Caches of recent room data and metadata | At least 1 GB free to join large public rooms such as #matrix:matrix.org; SYNAPSE_CACHE_FACTOR (default 0.5) trades memory for speed |
| Database | Events and room state, including rooms joined over federation | PostgreSQL; SQLite only for testing, and slow in large rooms |
| Media disk | Uploads, thumbnails, cached remote media, URL previews | Kept until media_retention purges it; unset means never |
| File handles | A connection to every server in a busy room | Raise the limit to at least 4096 if Synapse runs out |
| Python | Installing from PyPI | Python 3.10 to 3.13 |
There is no per-user CPU figure in the docs, and load does not follow headcount. It follows rooms. The admin FAQ notes that busy rooms involve hundreds of servers, and the first time your homeserver speaks in one it opens connections to all of them at once. Ten users in private rooms cost little; one user who joins several large public rooms can cost more than the other nine together.
The server name comes first
server_name sets the domain part of every ID on the server, @alice:example.com and #ops:example.com, and it tells other homeservers where to find yours. Synapse cannot change it after installation. For a test box the machine's hostname is fine. For production, use the bare domain, as you would for email, and let delegation send traffic to matrix.example.com.
Installation also generates a signing key. Other homeservers use it to verify your server's events. The docs say not to lose it: back it up with the database, because other homeservers keep your old key cached.
Sizing the Cloud VPS
A Cloud VPS here is a KVM guest on OpenStack hosts in New York. Each flavor fixes vCPUs, memory and SSD, and caps disk operations, disk throughput and port speed; a larger flavor raises those caps with the memory. Synapse and PostgreSQL share one guest, so both draw on the same disk-operations cap, and a growing homeserver can reach that cap before it runs out of cores.
- Memory: leave room for PostgreSQL's shared_buffers and for Synapse's caches. The 1 GB floor in the docs is for joining large rooms; plan above it.
- Disk: the media store sits on the same SSD as the database. Remote media is cached locally, so joining media-heavy rooms fills the disk without any local user uploading anything.
- Traffic: only egress counts against the flavor's monthly allowance. Pulling remote media and incoming federation is ingress and never metered. Once the allowance is spent, outbound traffic is shaped to 10 Mbit/s until a new billing period begins or you buy extra traffic, and the guest stays up.
- Growth: support resizes the guest to a larger flavor, restarting it once and keeping its addresses and data. Flavors only grow, because a disk cannot shrink, so start at the size you need now.
Installing Synapse with PostgreSQL
- Order the VPS with Ubuntu or Debian and paste your SSH key; you log in as root over SSH. The matrix.org packages are built for amd64.
- Point DNS for
matrix.example.comat the VPS. Ifserver_namewill be the bare domain, you also need control of whathttps://example.comserves, for the delegation file. - Add the matrix.org repository and install
matrix-synapse-py3with PostgreSQL. The installer asks for the server name. Do not use the Synapse package in Ubuntu's own repository: the Synapse docs call it old, with known security vulnerabilities. - Create the database user and a UTF8 database with locale C from template0. Without that encoding PostgreSQL cannot store UTF8 strings.
- Put your settings in a file under
/etc/matrix-synapse/conf.d/rather than editinghomeserver.yaml, so package upgrades do not ask to replace your configuration. Restartmatrix-synapse.
sudo apt install -y lsb-release wget apt-transport-https
sudo wget -O /usr/share/keyrings/matrix-org-archive-keyring.gpg https://packages.matrix.org/debian/matrix-org-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/matrix-org-archive-keyring.gpg] https://packages.matrix.org/debian/ $(lsb_release -cs) main" |
sudo tee /etc/apt/sources.list.d/matrix-org.list
sudo apt update
sudo apt install matrix-synapse-py3 postgresql libpq5
sudo -u postgres createuser --pwprompt synapse_user
sudo -u postgres createdb --encoding=UTF8 --locale=C --template=template0 --owner=synapse_user synapse
# /etc/matrix-synapse/conf.d/local.yaml
database:
name: psycopg2
args:
user: synapse_user
password: "the password given to createuser"
dbname: synapse
host: localhost
cp_min: 5
cp_max: 10
public_baseurl: "https://matrix.example.com"
enable_registration: false
registration_shared_secret: "a long random string from openssl rand -hex 32"
max_upload_size: 50M
media_retention:
remote_media_lifetime: 14d
This file holds secrets: sudo chown root:matrix-synapse /etc/matrix-synapse/conf.d/local.yaml && sudo chmod 640 /etc/matrix-synapse/conf.d/local.yaml.
Moving an existing SQLite install later is possible with synapse_port_db, in two passes: one against a copy, then a final one with Synapse stopped. Starting on PostgreSQL avoids it.
TLS, federation and delegation
Out of the box Synapse speaks plain HTTP on localhost:8008. Put nginx (or Caddy, Apache, HAProxy) in front to terminate TLS, forward only /_matrix and /_synapse/client, and pass X-Forwarded-For and X-Forwarded-Proto. Set x_forwarded: true on the 8008 listener so Synapse logs real client addresses. Get the certificate first, for example with certbot for matrix.example.com.
server {
listen 443 ssl;
listen [::]:443 ssl;
listen 8448 ssl default_server;
listen [::]:8448 ssl default_server;
server_name matrix.example.com;
ssl_certificate /etc/letsencrypt/live/matrix.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/matrix.example.com/privkey.pem;
location ~ ^(/_matrix|/_synapse/client) {
# no path after the port, or nginx rewrites the URI
proxy_pass http://localhost:8008;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host:$server_port;
client_max_body_size 50M;
proxy_http_version 1.1;
}
}
Other homeservers connect to server_name on port 8448 unless told otherwise. Two ways to satisfy them:
- Port 8448 directly:
server_nameis the host's own DNS name and the proxy listens on 8448 with a certificate for it. No delegation needed. - Delegation:
https://example.com/.well-known/matrix/serverreturns{"m.server": "matrix.example.com:443"}, served on port 443. Without a port in that file, 8448 is assumed. Ifexample.comitself routes to Synapse,serve_server_wellknown: truemakes Synapse serve the file.
Clients look for /.well-known/matrix/client with m.homeserver.base_url, served with Access-Control-Allow-Origin: * for browser clients; it should match public_baseurl. Check the result with the Matrix federation tester. Two known traps: the proxy must not normalise URIs (Apache needs nocanon), and other homeservers do not yet follow 308 redirects, so an apex-to-www 308 can break federation; the docs suggest a 301 instead.
Accounts, registration and mail
enable_registration defaults to false. Create accounts from the command line: with registration_shared_secret set in local.yaml, restart Synapse, then run register_new_matrix_user -c /etc/matrix-synapse/conf.d/local.yaml http://localhost:8008. Guard that secret: anyone who has it can create users, admins included, even with registration off.
If you open sign-ups, the docs recommend a captcha, a required email or phone (registrations_require_3pid), or registration_requires_token, with tokens issued through the admin API. Registration without verification is, in the docs' words, a known vector for spam and abuse.
Without email, password resets and email notifications are disabled. Fill in smtp_host, smtp_port and notif_from. New VPS guests block outbound connections to port 25 but leave 465 and 587 open, so relay through a mail provider on 587.
Keeping the disk and database in check
- Media: local uploads live under
local_content, remote copies underremote_content/matrix.organd its siblings.media_retentionpurges media not accessed for a set time:local_media_lifetimefor your users' files,remote_media_lifetimefor the cache. Quarantined media is never purged. - URL previews: off by default. Turning them on requires
url_preview_ip_range_blacklist, at least loopback and RFC 1918 ranges, or any room member can make Synapse fetch internal URLs. - Database: the admin FAQ's query on
state_groups_statelists the rooms holding the most state. Tune shared_buffers, effective_cache_size and work_mem to the guest's free memory. - Backups: the PostgreSQL database, the signing key, the configuration and the media store. Before a PostgreSQL major upgrade, stop Synapse and take a backup.
- Calls: reliable VoIP through the homeserver needs a TURN server, which Synapse does not include.
When to leave the VPS
The VPS page lists the signs: the processor busy all day on shared vCPU time, a database needing more disk operations than the largest flavor's cap, egress beyond the allowance month after month. A homeserver joined to many large federated rooms can show all three. The next step is a dedicated server, every core and disk to itself and an unmetered port, in New York or four other cities. PostgreSQL can also move to separate hardware while Synapse stays put. For group video beside the chat, a self-hosted Jitsi Meet server is its own sizing problem.
Questions
How much memory does a Synapse homeserver need?
The Synapse docs set one floor: at least 1 GB of free RAM to join large public rooms. Beyond that it depends on the rooms joined, because Synapse caches recent room data in memory. SYNAPSE_CACHE_FACTOR, default 0.5, lowers or raises that cache.
Is SQLite good enough for a small server?
Only for testing. The installation docs say SQLite should not be used in production and performs poorly, especially in large rooms. PostgreSQL also lets the database move to separate hardware later.
Must port 8448 be open?
Only if other homeservers reach you there, which is the default. With a .well-known/matrix/server file on your domain naming port 443, federation runs over 443 and 8448 can stay closed.
Can the server name be changed after setup?
No. It is the domain in every user ID and room alias, and Synapse cannot change it after installation. Delegation lets the name stay example.com while Synapse runs on another host or port.
Can it run in Docker instead?
Yes. The official image is matrixdotorg/synapse, also published as ghcr.io/element-hq/synapse. It defaults to SQLite and includes no TURN server, so connect it to PostgreSQL for production. The VPS's one-click Docker app installs Docker Engine and the Compose plugin.
Why do invited users on other servers fail to join?
Usually the reverse proxy. Errors such as Invalid signature or 401 Unauthorized mean other servers could not reach yours correctly, often because the proxy rewrote the URI or a 308 redirect sits in the path.