Knowledgebase › Matrix Synapse on a LYLIX VPS — federation, performance tuning, the database trap

Matrix Synapse on a LYLIX VPS — federation, performance tuning, the database trap

Matrix Synapse is the most-deployed Matrix homeserver. It runs as a single VPS server fine; the failure modes are usually either federation traffic surprising you or the database growing without bound. This article covers the production patterns.

Sizing

  • Solo / small family: 2 GB RAM, 2 CPU, 20 GB. Workable but tight.
  • Community of 20-100 users: 4-8 GB RAM, 4 CPU, 50-100 GB.
  • Larger: 8+ GB, dedicated DB box, media on S3.

RAM is the most-constrained resource — Synapse caches aggressively for performance.

The components

  • Synapse — the homeserver, Python.
  • PostgreSQL — primary store. Don't use SQLite for anything beyond evaluation.
  • nginx — reverse proxy + TLS.
  • Optional: workers for scaling specific functions.

Install

The official Synapse APT repository:

# Add Matrix repo
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" \
    > /etc/apt/sources.list.d/matrix-org.list

apt update
apt install matrix-synapse-py3 postgresql nginx

During install, you'll be asked for the server name — this is what's in @user:server-name. Pick carefully; it's hard to change.

PostgreSQL setup

Default Synapse uses SQLite. Migrate immediately:

# Create the DB and user
sudo -u postgres psql <<EOF
CREATE USER synapse_user WITH PASSWORD '<password>';
CREATE DATABASE synapse
    ENCODING 'UTF8'
    LC_COLLATE='C'
    LC_CTYPE='C'
    template=template0
    OWNER synapse_user;
EOF

The LC_COLLATE='C' matters — Synapse specifically requires C collation. Getting it wrong now means rebuilding the database later.

Edit Synapse config to point at PostgreSQL:

# /etc/matrix-synapse/homeserver.yaml
database:
  name: psycopg2
  args:
    user: synapse_user
    password: <password>
    database: synapse
    host: localhost
    cp_min: 5
    cp_max: 10

Restart Synapse, verify it connects.

Federation port (8448)

Matrix federation uses port 8448 by default for server-to-server traffic. You can run it on the same port as client traffic with a .well-known/matrix/server file:

{
    "m.server": "matrix.example.com:443"
}

Serve this at https://example.com/.well-known/matrix/ server. Other servers fetch it before federation and respect the port you specify.

And the client-side .well-known/matrix/client:

{
    "m.homeserver": {
        "base_url": "https://matrix.example.com"
    }
}

This means users can log in with handles like @user:example.com while the homeserver itself runs on matrix.example.com.

The database trap

Synapse's database grows continuously. Without intervention:

  • State events accumulate forever.
  • Media metadata accumulates forever.
  • Federation cache accumulates.

By month 6 of an active community, your DB can be 50+ GB. By month 12, it's a problem.

Mitigations:

  • State compression — synapse-compress- state tool reduces state event storage by 30-90%. Run monthly.
  • Media retention — configure media_retention in homeserver.yaml. Local media you set retention by user; remote media by hours of age.
  • Purge history API — for rooms you no longer need history for, the admin API can purge old events.
  • Vacuum and analyze — set autovacuum to be aggressive on the synapse DB.

nginx config

Two server blocks: one for the main host (the .well-known files), one for the actual Synapse:

server {
    listen 443 ssl;
    server_name matrix.example.com;

    location ~* ^(\/_matrix|\/_synapse\/client) {
        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;
        client_max_body_size 50M;
    }
}

Media on S3

For larger deployments, use synapse_s3_storage_provider to move media to S3:

media_storage_providers:
  - module: s3_storage_provider.S3StorageProviderBackend
    store_local: True
    store_remote: True
    config:
      bucket: matrix-media
      endpoint_url: https://s3.us-west-001.backblazeb2.com
      access_key_id: ...
      secret_access_key: ...

Existing media stays local until you migrate it; new media goes to S3.

Workers for scaling

Past a few hundred concurrent users, the monolithic Synapse process becomes the bottleneck. The worker model splits responsibilities:

  • federation_sender — sends outbound federation.
  • federation_reader — receives inbound.
  • synchrotron — handles /sync (the heaviest client request).
  • media_repository — handles media uploads/downloads.

Adding workers is a configuration exercise — main server's homeserver.yaml + per-worker config files + nginx routing. The Synapse docs cover this comprehensively.

Federation traffic surprises

  • One big room you joined (Matrix HQ, etc.) brings traffic from thousands of servers. Plan accordingly.
  • Federation can spike when a popular event happens — servers announce state to each other in bursts.
  • If federation is overwhelming your VPS, you can rate-limit specific federation peers in homeserver.yaml.

Useful admin API operations

# Get an admin access token (one-time setup), then:

# List users
curl -H "Authorization: Bearer <token>" \
    https://matrix.example.com/_synapse/admin/v2/users

# Deactivate a user
curl -X POST -H "Authorization: Bearer <token>" \
    -d '{"erase": true}' \
    https://matrix.example.com/_synapse/admin/v1/deactivate/@user:example.com

# Purge a room's history older than N
curl -X POST -H "Authorization: Bearer <token>" \
    -d '{"purge_up_to_ts": <timestamp>}' \
    https://matrix.example.com/_synapse/admin/v1/purge_history/!room_id:example.com

Backups

  • PostgreSQL — nightly pg_dump.
  • homeserver.yaml — contains keys; treat like a secret.
  • signing_key — if you lose this, federation breaks permanently. Back up separately.
  • Media — if on local, back up the media_store dir; if on S3, the bucket itself.

Alternatives

If Synapse's resource profile is too heavy for your use case, consider:

  • Dendrite — Matrix's Go-based homeserver. Lower resource use, less mature than Synapse but catching up.
  • Conduit / Conduwuit — Rust-based, single binary, very light. Great for solo use; smaller ecosystem.

Also Read

« « Back

Powered by WHMCompleteSolution