Matrix Synapse: Decentralized Chat for Your Homelab
Self-hosted Matrix server with federation. End-to-end encryption and bridges to other platforms.
Introduction
In an era of centralized messaging platforms controlling your data and conversations, Matrix offers a compelling alternative: a decentralized, federated communication protocol that puts you in control. Matrix Synapse is the reference implementation of the Matrix homeserver, enabling you to run your own private chat infrastructure while maintaining the ability to communicate with users on other Matrix servers worldwide.
What you’ll learn:
- Deploy Matrix Synapse homeserver with Docker Compose
- Configure Element web and desktop clients
- Set up federation to communicate across Matrix servers
- Bridge Discord, Telegram, and IRC channels
- Implement SSO with Authentik or Authelia
- Optimize performance for production use
- Troubleshoot common federation and performance issues
Prerequisites:
- Docker and Docker Compose installed (v24.0+ and v2.20+)
- Reverse proxy with HTTPS (Caddy, Nginx, or Traefik)
- Domain name with DNS control (federation requires SRV records)
- 4+ GB RAM, 20+ GB storage (for moderate usage)
- PostgreSQL knowledge (recommended over SQLite)
- Basic understanding of federation concepts
Why Matrix Synapse?
The Decentralization Revolution
Traditional messaging platforms like Slack, Discord, and WhatsApp operate centralized servers that:
- Control your data and conversations
- Can shut down or change terms at any time
- Require trust in third-party providers
- Cannot communicate across platforms
Matrix solves this with decentralized federation:
- You own your data on your server
- Communicate with users on any Matrix server
- No single point of failure or control
- Open protocol anyone can implement
Key Features at a Glance
| Feature | Details |
|---|---|
| Federation | Connect with users on other Matrix servers worldwide |
| End-to-End Encryption | Olm/Megolm encryption for private conversations |
| Bridges | Connect Discord, Telegram, Slack, IRC, WhatsApp, Signal |
| Clients | Element (web/desktop/mobile), FluffyChat, SchildiChat, Nheko |
| Voice/Video | Built-in VoIP with Jitsi integration |
| Bots | Create bots with Python, JavaScript, or Go SDKs |
| Search | Full-text message search (requires PostgreSQL) |
| File Sharing | Upload images, videos, documents with configurable limits |
Matrix vs. Alternatives
| Feature | Matrix Synapse | Mattermost | Rocket.Chat |
|---|---|---|---|
| Federation | Native support | None | Limited |
| Bridges | Excellent (10+ platforms) | Limited | Moderate |
| Resource Usage | Moderate-High | Low-Moderate | Moderate |
| E2E Encryption | Native | None | Paid feature |
| Mobile Apps | Excellent | Excellent | Good |
| Setup Complexity | Advanced | Beginner | Intermediate |
Understanding Matrix Architecture
Core Components
Internet → Reverse Proxy → Matrix Synapse → PostgreSQL Database
↓ ↓
Element Web Federation (Port 8448)
↓
User Browsers Matrix Synapse: Homeserver handling message routing, federation, and storage PostgreSQL: Database storing messages, users, and room state Element: Official Matrix client (web, desktop, mobile) Reverse Proxy: Handles HTTPS and routes traffic to Synapse
Federation Explained
Federation allows your Matrix server to communicate with other Matrix servers:
Your Homeserver Other Homeserver
(matrix.example.com) (matrix.org)
↓ ↓
User @alice:example.com ←→ @bob:matrix.org How it works:
- Alice sends message to Bob on matrix.org
- Your homeserver encrypts and forwards to matrix.org
- matrix.org delivers to Bob
- Messages sync in real-time across servers
Requirements:
- Valid SSL certificate
- Port 8448 open (or delegation via .well-known)
- Proper DNS SRV records
- Server signing keys
Room Structure
Matrix rooms are distributed across all participating servers:
Room: #dev-team:example.com
├── Participants:
│ ├── @alice:example.com (your server)
│ ├── @bob:example.com (your server)
│ └── @charlie:matrix.org (remote server)
├── Room State:
│ ├── Replicated on both servers
│ ├── Eventually consistent
│ └── Survives server outages Installation: Docker Compose Method
Step 1: Prepare Directory Structure
mkdir -p /opt/matrix-synapse/{data,postgres_data,element}
cd /opt/matrix-synapse Step 2: Generate Synapse Configuration
Generate initial config file with your domain:
docker run -it --rm -v ./data:/data -e SYNAPSE_SERVER_NAME=example.com -e SYNAPSE_REPORT_STATS=no matrixdotorg/synapse:latest generate Important parameters:
SYNAPSE_SERVER_NAME: Your Matrix domain (e.g., example.com)- This generates
/data/homeserver.yamland signing keys
Step 3: Create docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:16-alpine
container_name: matrix-postgres
restart: always
environment:
POSTGRES_DB: synapse
POSTGRES_USER: synapse
POSTGRES_PASSWORD: CHANGE_THIS_SECURE_PASSWORD
POSTGRES_INITDB_ARGS: --encoding=UTF8 --locale=C
volumes:
- ./postgres_data:/var/lib/postgresql/data
networks:
- matrix
healthcheck:
test: ["CMD-SHELL", "pg_isready -U synapse"]
interval: 30s
timeout: 10s
retries: 3
synapse:
image: matrixdotorg/synapse:latest
container_name: matrix-synapse
restart: always
depends_on:
postgres:
condition: service_healthy
environment:
- SYNAPSE_CONFIG_PATH=/data/homeserver.yaml
- UID=1000
- GID=1000
volumes:
- ./data:/data
ports:
- "8008:8008" # Client API
- "8448:8448" # Federation API
networks:
- matrix
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8008/health"]
interval: 30s
timeout: 10s
retries: 3
element:
image: vectorim/element-web:latest
container_name: matrix-element
restart: always
volumes:
- ./element/config.json:/app/config.json:ro
ports:
- "8080:80"
networks:
- matrix
networks:
matrix:
driver: bridge Step 4: Configure PostgreSQL Database
Edit data/homeserver.yaml to use PostgreSQL instead of SQLite:
# Database configuration
database:
name: psycopg2
args:
user: synapse
password: CHANGE_THIS_SECURE_PASSWORD
database: synapse
host: postgres
port: 5432
cp_min: 5
cp_max: 10 Why PostgreSQL?
- SQLite doesn’t support full-text search
- PostgreSQL handles concurrent connections better
- Required for production deployments
- Enables message search functionality
Step 5: Configure Synapse Settings
Edit key sections of data/homeserver.yaml:
# Server Configuration
server_name: "example.com"
public_baseurl: "https://matrix.example.com"
# Registration
enable_registration: false
enable_registration_without_verification: false
registration_shared_secret: "GENERATE_RANDOM_STRING_HERE"
# Federation
federation_domain_whitelist: [] # Empty = allow all servers
# federation_domain_whitelist:
# - matrix.org
# - element.io
# Media Storage
max_upload_size: "50M"
media_retention:
local_media_lifetime: 90d
remote_media_lifetime: 14d
# URL Previews
url_preview_enabled: true
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'
# Turn Server (for VoIP)
turn_uris:
- "turn:turn.example.com:3478?transport=udp"
- "turn:turn.example.com:3478?transport=tcp"
turn_shared_secret: "GENERATE_RANDOM_STRING_HERE"
turn_user_lifetime: 86400000
# Rate Limiting
rc_message:
per_second: 0.2
burst_count: 10
rc_registration:
per_second: 0.17
burst_count: 3
rc_login:
address:
per_second: 0.17
burst_count: 3
account:
per_second: 0.17
burst_count: 3
failed_attempts:
per_second: 0.17
burst_count: 3
# Performance
event_cache_size: 10K
caches:
global_factor: 1.0
per_cache_factors:
get_users_in_room: 2.0 Generate random secrets:
# Generate registration secret
cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1
# Generate TURN secret
cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1 Step 6: Configure Element Client
Create element/config.json:
{
"default_server_config": {
"m.homeserver": {
"base_url": "https://matrix.example.com",
"server_name": "example.com"
},
"m.identity_server": {
"base_url": "https://vector.im"
}
},
"brand": "Element",
"integrations_ui_url": "https://scalar.vector.im/",
"integrations_rest_url": "https://scalar.vector.im/api",
"integrations_widgets_urls": [
"https://scalar.vector.im/_matrix/integrations/v1",
"https://scalar.vector.im/api",
"https://scalar-staging.vector.im/_matrix/integrations/v1",
"https://scalar-staging.vector.im/api",
"https://scalar-staging.riot.im/scalar/api"
],
"bug_report_endpoint_url": "https://element.io/bugreports/submit",
"defaultCountryCode": "US",
"showLabsSettings": true,
"features": {
"feature_new_room_decoration_ui": true,
"feature_pinning": true,
"feature_custom_status": true,
"feature_custom_tags": true,
"feature_state_counters": true
},
"default_federate": true,
"default_theme": "dark",
"roomDirectory": {
"servers": [
"example.com",
"matrix.org"
]
},
"enable_presence_by_hs_url": {
"https://matrix.example.com": false
},
"settingDefaults": {
"breadcrumbs": true
}
} Step 7: Configure DNS and Federation
A Record (Required):
matrix.example.com A YOUR_SERVER_IP SRV Records (Required for Federation):
_matrix._tcp.example.com SRV 10 0 8448 matrix.example.com
_matrix-fed._tcp.example.com SRV 10 0 8448 matrix.example.com Alternative: .well-known Delegation
If you cannot create SRV records, use .well-known delegation on your main domain:
Create https://example.com/.well-known/matrix/server:
{
"m.server": "matrix.example.com:8448"
} Create https://example.com/.well-known/matrix/client:
{
"m.homeserver": {
"base_url": "https://matrix.example.com"
}
} Serve these with proper CORS headers:
Access-Control-Allow-Origin: *
Content-Type: application/json Step 8: Configure Reverse Proxy
Caddy Configuration (Recommended):
matrix.example.com {
# Client API
reverse_proxy /_matrix/* synapse:8008
reverse_proxy /_synapse/* synapse:8008
# Element Web
reverse_proxy /* element:80
# Security headers
header {
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
Referrer-Policy "no-referrer-when-downgrade"
Strict-Transport-Security "max-age=31536000;"
}
}
# Federation endpoint (port 8448)
matrix.example.com:8448 {
reverse_proxy synapse:8448
} Nginx Configuration:
server {
server_name matrix.example.com;
# Client API
location /_matrix {
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;
}
location /_synapse {
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;
}
# Element Web
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
}
listen 443 ssl http2;
ssl_certificate /etc/letsencrypt/live/matrix.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/matrix.example.com/privkey.pem;
}
# Federation server
server {
server_name matrix.example.com;
listen 8448 ssl http2;
ssl_certificate /etc/letsencrypt/live/matrix.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/matrix.example.com/privkey.pem;
location / {
proxy_pass http://localhost:8448;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
}
} Step 9: Launch Services
# Start all services
docker compose up -d
# Monitor logs
docker compose logs -f synapse
# Check health
docker compose ps Wait for Synapse to complete startup (first launch takes 30-60 seconds).
Step 10: Create Admin User
# Register admin user
docker exec -it matrix-synapse register_new_matrix_user -c /data/homeserver.yaml http://localhost:8008
# Follow prompts:
# - Username: admin
# - Password: [secure password]
# - Make admin? yes Step 11: Verify Federation
# Test federation connectivity
curl -X GET https://matrix.example.com/_matrix/federation/v1/version
# Should return:
# {"server":{"name":"Synapse","version":"1.119.0"}}
# Use Matrix Federation Tester
# Visit: https://federationtester.matrix.org
# Enter: example.com Common federation issues:
- Port 8448 not accessible from internet
- DNS SRV records incorrect or not propagated
- SSL certificate doesn’t cover matrix.example.com
- Firewall blocking outbound federation traffic
Setting Up Bridges
Bridges connect Matrix to other platforms, allowing you to use one client for all messaging.
Discord Bridge
Use case: Access Discord servers from Matrix client
Installation:
# Add to docker-compose.yml
discord-bridge:
image: halfshot/matrix-appservice-discord:latest
container_name: matrix-discord-bridge
restart: always
volumes:
- ./discord-bridge:/data
networks:
- matrix Configuration:
# Generate config
docker run --rm -v ./discord-bridge:/data halfshot/matrix-appservice-discord:latest /usr/local/bin/node /build/tools/generateConfig.js -p 9005 -u "http://discord-bridge:9005"
# Edit discord-bridge/config.yaml:
# - Add your Discord bot token
# - Set bridge.domain to your homeserver domain
# - Configure database (SQLite or PostgreSQL) Register with Synapse:
Add to data/homeserver.yaml:
app_service_config_files:
- /data/discord-registration.yaml Copy registration file:
cp discord-bridge/discord-registration.yaml data/
docker compose restart synapse Bridge Discord Server:
- Invite Discord bot to your Discord server
- In Matrix, create room and invite Discord bridge bot
- Send:
!discord bridge YOUR_DISCORD_SERVER_ID DISCORD_CHANNEL_ID - Messages now sync bidirectionally
Telegram Bridge (mautrix-telegram)
Use case: Access Telegram chats from Matrix
# Add to docker-compose.yml
telegram-bridge:
image: dock.mau.dev/mautrix/telegram:latest
container_name: matrix-telegram-bridge
restart: always
volumes:
- ./telegram-bridge:/data
networks:
- matrix Configuration:
# Generate config
docker run --rm -v ./telegram-bridge:/data dock.mau.dev/mautrix/telegram:latest
# Edit telegram-bridge/config.yaml:
homeserver:
address: http://synapse:8008
domain: example.com
appservice:
address: http://telegram-bridge:29317
port: 29317
database: postgresql://synapse:password@postgres/telegram_bridge
telegram:
api_id: YOUR_API_ID # Get from https://my.telegram.org
api_hash: YOUR_API_HASH Register with Synapse:
cp telegram-bridge/registration.yaml data/
# Add to homeserver.yaml app_service_config_files
docker compose restart synapse Bridge Telegram Chats:
- Start chat with bridge bot on Matrix
- Send:
login - Follow phone number verification
- Your Telegram chats appear in Matrix
IRC Bridge
Use case: Connect to Libera.Chat, OFTC, or other IRC networks
irc-bridge:
image: matrixdotorg/matrix-appservice-irc:latest
container_name: matrix-irc-bridge
restart: always
volumes:
- ./irc-bridge:/data
networks:
- matrix Configuration for Libera.Chat:
# irc-bridge/config.yaml
homeserver:
url: http://synapse:8008
domain: example.com
ircService:
servers:
irc.libera.chat:
port: 6697
ssl: true
additionalAddresses:
- "irc.libera.chat"
botConfig:
enabled: true
nick: "MatrixBot"
dynamicChannels:
enabled: true
createAlias: true
published: true
joinRule: public
federate: true
matrixClients:
userTemplate: "@irc_$NICK" Usage:
Join IRC channel from Matrix:
/join #matrix:libera.chat SSO Integration with Authentik
Single Sign-On centralizes authentication across your homelab services.
Prerequisites
- Authentik installed and configured
- Admin access to both Synapse and Authentik
Step 1: Configure Authentik Provider
Log into Authentik admin panel
Navigate to Applications → Providers
Click “Create” → Select “OAuth2/OpenID Provider”
Configure:
- Name: Matrix Synapse
- Client Type: Confidential
- Redirect URIs:
https://matrix.example.com/_synapse/client/oidc/callback - Signing Key: Select your signing certificate
Save and note the Client ID and Client Secret
Step 2: Create Authentik Application
Applications → Applications → Create
Configure:
- Name: Matrix
- Slug: matrix
- Provider: Select the provider created above
Assign to users or groups who should have access
Step 3: Configure Synapse OIDC
Edit data/homeserver.yaml:
oidc_providers:
- idp_id: authentik
idp_name: "Authentik"
issuer: "https://auth.example.com/application/o/matrix/"
client_id: "YOUR_CLIENT_ID"
client_secret: "YOUR_CLIENT_SECRET"
scopes:
- "openid"
- "profile"
- "email"
user_mapping_provider:
config:
localpart_template: "{{ user.preferred_username }}"
display_name_template: "{{ user.name }}"
email_template: "{{ user.email }}"
allow_existing_users: true
enable_registration: true Restart Synapse:
docker compose restart synapse Step 4: Test SSO Login
- Navigate to Element web client
- Click “Sign In”
- Select “Continue with Authentik”
- Authenticate via Authentik
- You should be logged into Matrix
Advantages:
- Centralized user management
- MFA enforcement from Authentik
- Easy onboarding/offboarding
- Consistent authentication across services
Performance Optimization
Database Tuning
PostgreSQL Configuration:
Create postgres_data/postgresql.conf:
# Memory settings (adjust for your RAM)
shared_buffers = 1GB
effective_cache_size = 3GB
maintenance_work_mem = 256MB
work_mem = 16MB
# Checkpoint settings
checkpoint_completion_target = 0.9
wal_buffers = 16MB
min_wal_size = 1GB
max_wal_size = 4GB
# Query planner
random_page_cost = 1.1
effective_io_concurrency = 200
# Logging
log_min_duration_statement = 1000 # Log slow queries Database Maintenance:
# Run weekly vacuum
docker exec -it matrix-postgres psql -U synapse -c "VACUUM ANALYZE;"
# Reindex for performance
docker exec -it matrix-postgres psql -U synapse -c "REINDEX DATABASE synapse;" Synapse Caching
Increase cache sizes in homeserver.yaml:
caches:
global_factor: 2.0
per_cache_factors:
get_users_in_room: 5.0
get_current_state_ids: 5.0
event_cache_size: 50K Resource requirements:
global_factor: 1.0= ~500MB RAMglobal_factor: 2.0= ~1GB RAMglobal_factor: 4.0= ~2GB RAM
Media Cleanup
Old media accumulates quickly. Implement automated cleanup:
#!/bin/bash
# media-cleanup.sh
# Delete local media older than 90 days
docker exec matrix-synapse curl -X POST -H "Authorization: Bearer YOUR_ADMIN_ACCESS_TOKEN" http://localhost:8008/_synapse/admin/v1/media/example.com/delete?before_ts=$(($(date +%s) - 7776000))000
# Delete remote media older than 14 days
docker exec matrix-synapse curl -X POST -H "Authorization: Bearer YOUR_ADMIN_ACCESS_TOKEN" http://localhost:8008/_synapse/admin/v1/media/example.com/delete?before_ts=$(($(date +%s) - 1209600))000&keep_profiles=true Get admin access token:
# Login as admin and extract token
curl -X POST -d '{"type":"m.login.password", "user":"admin", "password":"yourpassword"}' https://matrix.example.com/_matrix/client/r0/login Schedule monthly:
# Crontab
0 2 1 * * /opt/matrix-synapse/media-cleanup.sh Room State Compression
Large rooms accumulate state. Enable state compression:
# homeserver.yaml
retention:
enabled: true
default_policy:
min_lifetime: 1d
max_lifetime: 365d
# Enable room auto-join cleanup
auto_join_rooms:
enabled: false Monitoring & Troubleshooting
Health Checks
# Check Synapse health
curl https://matrix.example.com/_matrix/client/versions
# Check federation
curl https://matrix.example.com:8448/_matrix/federation/v1/version
# Database connection count
docker exec matrix-postgres psql -U synapse -c "SELECT count(*) FROM pg_stat_activity WHERE datname='synapse';" Common Issues
Issue 1: Federation Not Working
Symptoms: Cannot join rooms on other servers, federation tester fails
Solutions:
# Verify port 8448 is open
nc -zv matrix.example.com 8448
# Check DNS SRV records
dig SRV _matrix._tcp.example.com
# Test .well-known delegation
curl https://example.com/.well-known/matrix/server
# Check signing keys
curl https://matrix.example.com/_matrix/key/v2/server Issue 2: High Database Usage
Symptoms: Database size grows rapidly, slow queries
Solutions:
# Find largest tables
docker exec matrix-postgres psql -U synapse -c "SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) FROM pg_stat_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 10;"
# Common culprits:
# - event_json (messages)
# - state_events (room state)
# - event_search (full-text search index)
# Purge old events from room
# Use Synapse Admin API or Synapse-Admin web UI Issue 3: Out of Memory
Symptoms: Container restarts, OOM errors in logs
Solutions:
# Reduce cache sizes in homeserver.yaml
caches:
global_factor: 0.5 # Reduce from 1.0
# Add Docker memory limits
services:
synapse:
deploy:
resources:
limits:
memory: 4G
reservations:
memory: 2G Issue 4: Slow Message Delivery
Symptoms: Messages delayed, federation lag
Solutions:
# Check event processing queue
docker exec matrix-synapse curl http://localhost:8008/_synapse/admin/v1/event_reports
# Increase worker processes (advanced)
# See Synapse worker documentation for scaling
# Verify network latency to federated servers
mtr matrix.org Logging Configuration
Enable debug logging temporarily:
# homeserver.yaml
loggers:
synapse:
level: DEBUG
synapse.federation:
level: DEBUG Restart and monitor:
docker compose restart synapse
docker compose logs -f synapse | grep -i error Advanced: Worker Configuration
For high-traffic deployments, split Synapse into multiple workers.
Benefits:
- Horizontal scaling across CPU cores
- Separate client API from federation traffic
- Improved responsiveness under load
Example worker configuration:
# Add to docker-compose.yml
synapse-generic-worker:
image: matrixdotorg/synapse:latest
container_name: synapse-worker-1
restart: always
environment:
SYNAPSE_WORKER: synapse.app.generic_worker
volumes:
- ./data:/data
- ./workers/worker1.yaml:/worker.yaml
networks:
- matrix Create workers/worker1.yaml:
worker_app: synapse.app.generic_worker
worker_name: worker1
worker_listeners:
- type: http
port: 8081
resources:
- names: [client, federation]
worker_log_config: /data/log.config Update reverse proxy to load balance across workers.
Security Hardening
Rate Limiting
Already configured in homeserver.yaml, but adjust per your needs:
rc_message:
per_second: 0.2 # 1 message per 5 seconds
burst_count: 10 # Allow bursts of 10
rc_registration:
per_second: 0.17
burst_count: 3
rc_login:
address:
per_second: 0.17
burst_count: 3 Admin API Security
Protect admin endpoints:
# Require access token for admin API
admin_contact: 'mailto:[email protected]'
# Restrict registration
enable_registration: false
registration_shared_secret: "RANDOM_SECRET" # For controlled registration Content Security
# Prevent abuse
max_upload_size: "50M"
# URL preview restrictions
url_preview_enabled: true
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'
- '169.254.0.0/16'
- '::1/128'
- 'fe80::/10'
- 'fc00::/7' Monitoring Access
Enable Prometheus metrics:
enable_metrics: true
listeners:
- port: 9000
type: metrics
bind_addresses: ['127.0.0.1'] Scrape with Prometheus for monitoring.
Backup & Recovery
Automated Backup Script
#!/bin/bash
# matrix-backup.sh
BACKUP_DIR="/opt/backups/matrix"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP_DIR"
# Backup PostgreSQL database
docker exec matrix-postgres pg_dump -U synapse synapse | gzip > "$BACKUP_DIR/synapse-db-${DATE}.sql.gz"
# Backup Synapse configuration and keys
tar czf "$BACKUP_DIR/synapse-config-${DATE}.tar.gz" /opt/matrix-synapse/data/homeserver.yaml /opt/matrix-synapse/data/*.signing.key
# Backup media (optional, can be large)
# tar czf "$BACKUP_DIR/synapse-media-${DATE}.tar.gz" # /opt/matrix-synapse/data/media_store
# Cleanup old backups (keep 30 days)
find "$BACKUP_DIR" -name "synapse-*.gz" -mtime +30 -delete
echo "Backup completed: $DATE" Schedule daily:
0 3 * * * /opt/matrix-synapse/matrix-backup.sh Disaster Recovery
# Stop services
docker compose down
# Restore database
gunzip -c /opt/backups/matrix/synapse-db-YYYYMMDD.sql.gz | docker exec -i matrix-postgres psql -U synapse synapse
# Restore configuration
tar xzf /opt/backups/matrix/synapse-config-YYYYMMDD.tar.gz -C /
# Restart services
docker compose up -d Conclusion
You now have a fully functional Matrix Synapse homeserver with:
- Federated messaging across the Matrix network
- Element web client for user access
- Bridges to Discord, Telegram, and IRC
- SSO integration for centralized authentication
- Optimized performance for production use
- Automated backups and monitoring
Next steps:
- Create rooms and invite users
- Set up additional bridges as needed
- Configure mobile Element apps
- Implement monitoring with Prometheus/Grafana
- Join the Matrix community at #synapse:matrix.org
Remember: Matrix federation means your server is part of a global network. Maintain good uptime, security, and follow the Matrix Code of Conduct.
Additional Resources
Official:
Community:
Tools:
Last Updated: December 2025 Version Covered: Synapse 1.119.0+ Word Count: 3,145 words