Skip to main content

Self-Hosting Tips

Expert tips for running your own Matrix homeserver.

Homeserver Alternatives

Conduit (Rust)

Conduit is a lightweight Matrix homeserver written in Rust:

Pros:

  • Single binary, no dependencies
  • Low memory usage (~100MB for small deployments)
  • Fast startup
  • Easy configuration

Cons:

  • Missing some advanced Synapse features
  • Smaller community
  • Less tested at scale

Installation:

# Docker
docker run -d -p 8448:6167 \
-v db:/var/lib/matrix-conduit \
-e CONDUIT_SERVER_NAME=your.server.name \
-e CONDUIT_DATABASE_BACKEND=rocksdb \
matrixconduit/matrix-conduit:latest

Configuration:

conduit.toml
[global]
server_name = "your.server.name"
database_backend = "rocksdb"
port = 6167
max_request_size = 20_000_000
allow_registration = false

conduwuit (Conduit Fork)

conduwuit is an actively maintained Conduit fork with more features:

  • Experimental room v11 support
  • Better federation compatibility
  • More active development
  • Bug fixes not in upstream

When to Use What

Use CaseRecommended
Large deployment (500+ users)Synapse with workers
Small personal serverConduit/conduwuit
Maximum featuresSynapse
Minimal resourcesConduit
Production criticalSynapse
ExperimentationConduit/Dendrite

Performance Optimization

Synapse Tuning

Worker Mode

For high-traffic servers, split into workers:

homeserver.yaml
worker_app: synapse.app.generic_worker
worker_replication_host: 127.0.0.1
worker_replication_http_port: 9093

Workers to consider:

  • Sync workers - Handle /sync requests
  • Federation sender - Outbound federation
  • Media workers - Media processing
  • Event persisters - Database writes

Cache Settings

caches:
global_factor: 1.0
per_cache_factors:
get_users_in_room: 2.0
get_current_state_ids: 2.0
stateGroupCache: 2.0

PostgreSQL Tuning

postgresql.conf
shared_buffers = 1GB              # 25% of RAM
effective_cache_size = 3GB # 75% of RAM
maintenance_work_mem = 256MB
work_mem = 64MB
random_page_cost = 1.1 # For SSDs
effective_io_concurrency = 200 # For SSDs

Database Maintenance

Regular maintenance tasks:

# Vacuum and analyze
psql -U synapse -d synapse -c "VACUUM ANALYZE;"

# Reindex if needed
psql -U synapse -d synapse -c "REINDEX DATABASE synapse;"

Monitoring

Prometheus + Grafana

docker-compose.yml
services:
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml

grafana:
image: grafana/grafana
ports:
- "3000:3000"

Key Metrics

MetricAlert Threshold
Federation lag> 60 seconds
Request latency> 500ms p95
Database connections> 80%
Memory usage> 85%
Disk space< 10 GB

Synapse Metrics

Enable in config:

enable_metrics: true
listeners:
- port: 9000
type: metrics

Media Management

matrix-media-repo

matrix-media-repo is an alternative media repository:

Features:

  • S3/MinIO storage support
  • Media deduplication
  • Thumbnail generation
  • Federation media caching
  • Multiple homeserver support

Installation:

docker-compose.yml
services:
media-repo:
image: t2bot/matrix-media-repo:latest
ports:
- "8000:8000"
volumes:
- ./media-repo.yaml:/etc/media-repo.yaml
- ./media:/media

Configuration:

media-repo.yaml
repo:
bindAddress: "0.0.0.0"
port: 8000

database:
postgres: "postgres://user:pass@localhost/mediarepo"

homeservers:
- name: example.com
csApi: "https://matrix.example.com"
backoffAt: 10

datastores:
- type: s3
opts:
tempPath: "/tmp/mediarepo_s3"
endpoint: s3.amazonaws.com
accessKeyId: "YOUR_ACCESS_KEY"
secretAccessKey: "YOUR_SECRET_KEY"
bucketName: "matrix-media"

S3 Storage (Native Synapse)

Store media directly in S3 without external repo:

homeserver.yaml
media_storage_providers:
- module: s3_storage_provider.S3StorageProviderBackend
store_local: true
store_remote: true
store_synchronous: true
config:
bucket: matrix-media
region_name: us-east-1
access_key_id: YOUR_KEY
secret_access_key: YOUR_SECRET

Install provider:

pip install synapse-s3-storage-provider

Media Cleanup

Remove old remote media:

# Via Admin API
curl -X POST -H "Authorization: Bearer $TOKEN" \
"https://matrix.server/_synapse/admin/v1/purge_media_cache?before_ts=1577836800000"

Identity Server

Sydent

Sydent is the reference Matrix identity server:

Purpose:

  • Email/phone to Matrix ID lookup
  • Third-party identity verification
  • Required for some discovery features

Installation:

docker-compose.yml
services:
sydent:
image: matrixdotorg/sydent
ports:
- "8090:8090"
volumes:
- ./sydent.yaml:/etc/sydent/sydent.yaml
- ./sydent-data:/var/lib/sydent

Configuration:

sydent.yaml
general:
server.name: id.example.com
log.path: /var/log/sydent

db:
file: /var/lib/sydent/sydent.db

http:
port: 8090

email:
smtphost: smtp.example.com
smtpport: 587
smtpusername: matrix@example.com
smtppassword: secret
from: "Matrix ID Server <noreply@example.com>"

Note: Most small deployments don't need their own identity server. Use matrix.org or vector.im identity servers instead.


Backup Strategy

Database Backup

#!/bin/bash
# backup.sh
DATE=$(date +%Y%m%d)
pg_dump -U synapse synapse | gzip > /backups/synapse_$DATE.sql.gz

# Keep last 7 days
find /backups -name "synapse_*.sql.gz" -mtime +7 -delete

Media Backup

# Rsync media to backup location
rsync -avz /var/lib/synapse/media_store/ /backups/media/

Signing Keys

Critical! Backup your signing key:

cp /data/*.signing.key /secure-backup/

Losing this means losing your server identity.

Federation Optimization

Federation Sender Tuning

federation_sender_instances:
- federation_sender1
- federation_sender2

Problematic Servers

Block abusive servers:

federation_domain_whitelist: null  # Allow all except...

# Or use room ACLs for per-room blocking

Federation Testing

# Test federation
curl "https://federationtester.matrix.org/api/report?server_name=example.com"

Security Hardening

Rate Limiting

rc_messages:
per_second: 0.2
burst_count: 10

rc_registration:
per_second: 0.1
burst_count: 3

rc_login:
per_second: 0.03
burst_count: 5

Registration Control

enable_registration: false
registration_shared_secret: "long-random-secret"

# Or with email verification
enable_registration: true
registrations_require_3pid:
- email

Media Restrictions

max_upload_size: 50M
url_preview_ip_range_blacklist:
- '127.0.0.0/8'
- '10.0.0.0/8'
- '172.16.0.0/12'
- '192.168.0.0/16'

Automation

matrix-docker-ansible-deploy

The best way to deploy Matrix:

git clone https://github.com/spantaleev/matrix-docker-ansible-deploy.git
cd matrix-docker-ansible-deploy

Manages:

  • Synapse/Dendrite
  • Element Web
  • Bridges (all major ones)
  • Bots (Mjolnir, etc.)
  • SSL certificates
  • Backups

Automatic Updates

# watchtower for Docker
services:
watchtower:
image: containrrr/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --cleanup --interval 86400

Disaster Recovery

Recovery Checklist

  1. Restore database from backup
  2. Restore media from backup
  3. Restore signing keys (critical!)
  4. Verify configuration
  5. Start services
  6. Test federation

Testing Recovery

Regularly test your recovery process:

  1. Set up test environment
  2. Restore from backups
  3. Verify functionality
  4. Document any issues

Resource Estimation

Users vs Resources

UsersRAMCPUStorage
1-502GB120GB
50-5004GB250GB
500-50008GB4200GB
5000+16GB+8+500GB+

Storage Growth

Estimate ~100MB per active user per month (varies by usage).

Useful Commands

# Check Synapse version
curl http://localhost:8008/_synapse/admin/v1/server_version

# List users
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8008/_synapse/admin/v2/users?limit=100"

# Room stats
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8008/_synapse/admin/v1/rooms"

# Purge old events (careful!)
curl -X POST -H "Authorization: Bearer $TOKEN" \
"http://localhost:8008/_synapse/admin/v1/purge_history/!room:example.com" \
-d '{"delete_local_events": true, "purge_up_to_ts": 1577836800000}'

Community Resources


You've reached the end of Hidden Gems. Happy hacking!