Self-hosting Matrix in 2025
After a longer hiatus, I decided yesterday to tackle setting up my own Matrix server again. The last time I tried this, it was quite a battle with cryptic error messages, Docker permission problems, and federation headaches. This time should be different – and indeed: A lot has changed!
TL;DR: It finally works smoothlyThe key takeaways upfront:
- ✅ Docker installation runs cleanly through
- ✅
.well-known
federation is recognized immediately - ✅ Element X with Sliding Sync works out-of-the-box
- ✅ Traefik integration without hickups
- ✅ IPv4 & IPv6 federation active from the start
In short: What used to cost hours or days now works in under an hour. But let me explain step by step...
The technical starting pointFor my setup I use:
- Synapse as Matrix homeserver
- PostgreSQL as database
- Traefik as reverse proxy with Let's Encrypt
- Docker Compose for orchestration
The target architecture:
example.com
as Matrix domain (for @username:example.com)matrix.example.com
as technical server endpoint- Well-Known delegation for clean separation
What used to be problematicDuring my last Matrix installation attempts a few years ago, these were the usual stumbling blocks:
Database permissions
# This was standard:
psql: FATAL: password authentication failed for user "synapse"
permission denied for table xyz
Federation debugging
# Federation tester always resulted in:
"CanReachViaHTTPS": false,
"DNSCheck": false
Well-Known endpoints
The .well-known/matrix/client
and .well-known/matrix/server
configuration was always a trial-and-error process with cryptic error messages.
Element X and Sliding
SyncSliding Sync didn't even exist yet, and when it did, it came with separate proxy containers and complicated configuration.
The 2025 installation: Surprisingly smooth
1. Docker Compose setup
My compose.yml
has become refreshingly compact:
services:
synapse:
image: matrixdotorg/synapse:latest
restart: unless-stopped
environment:
SYNAPSE_SERVER_NAME: example.com
SYNAPSE_REPORT_STATS: "no"
volumes:
- ./data:/data
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
# Client API (über websecure/443 mit CrowdSec-Schutz)
- "traefik.http.routers.matrix-client.entrypoints=websecure"
- "traefik.http.routers.matrix-client.rule=(Host(`matrix.example.com`))"
- "traefik.http.routers.matrix-client.tls=true"
- "traefik.http.routers.matrix-client.tls.certresolver=http_resolver"
- "traefik.http.routers.matrix-client.service=matrix-client"
- "traefik.http.routers.matrix-client.middlewares=default@file"
- "traefik.http.services.matrix-client.loadbalancer.server.port=8008"
# Federation API (über Port 443, OHNE CrowdSec!)
- "traefik.http.routers.matrix-federation.entrypoints=matrix-federation"
- "traefik.http.routers.matrix-federation.rule=(Host(`matrix.example.com`))"
- "traefik.http.routers.matrix-federation.tls=true"
- "traefik.http.routers.matrix-federation.tls.certresolver=http_resolver"
- "traefik.http.routers.matrix-federation.service=matrix-federation"
- "traefik.http.services.matrix-federation.loadbalancer.server.port=8008"
postgres:
image: postgres:15
restart: unless-stopped
environment:
POSTGRES_DB: synapse
POSTGRES_USER: synapse
POSTGRES_PASSWORD: secure_random_password
That's it. No complicated volume mounts, no permission fixes, no custom Dockerfiles.
2. Homeserver configuration
The homeserver.yaml
has become significantly more user-friendly. The most important changes for 2025:
# Element X Support eingebaut
experimental_features:
msc3575_enabled: true
unstable_features:
org.matrix.simplified_msc3575: true
# Public Base URL für saubere Links
public_baseurl: "https://matrix.example.com"
# Well-Known Federation
server_name: "example.com"
# Federation über Port 8008
listeners:
- port: 8008
tls: false
type: http
x_forwarded: true
resources:
- names: [client, federation]
compress: false
That's it! No separate Sliding Sync proxy, no complicated MSC feature activation.
3. Well-Known delegation
The .well-known/matrix/client
on example.com
:
{
"m.homeserver": {
"base_url": "https://matrix.example.com"
},
"m.identity_server": {
"base_url": "https://vector.im"
}
}
And .well-known/matrix/server
:
{
"m.server": "matrix.example.com:443"
}
Result: Federation tester shows green immediately!
Element X: The game changer
The biggest difference from before is Element X. Installation was trivial:
- Install app
- Enter server:
example.com
- Login:
@username:example.com
- Sliding Sync works automatically
No separate proxy containers, no experimental API endpoints, no beta features. It just works.
The performance is noticeably better than Element Classic:
- Instant synchronization between devices
- Rooms load significantly faster
- Offline support is more robust
Federation: Finally hassle-free
Everything green, IPv4 and IPv6 work, federation runs immediately.
Performance and resource consumption
The current setup is surprisingly efficient:
CONTAINER CPU % MEM USAGE / LIMIT MEM %
matrix-synapse-1 0.52% 222.5MiB / 23.41GiB 0.93%
matrix-postgres-1 0.10% 117.9MiB / 23.41GiB 0.49%
Absolutely fine for a single-user server – under 1% CPU load and less than 350MB RAM total. The PostgreSQL integration runs stable, no memory leaks or performance issues.
What I learned
1. The Matrix ecosystem has matured
The development of recent years shows: Matrix has evolved from an interesting experiment to a production-ready platform. Installation has become significantly more user-friendly.
2. Element X makes the difference
Sliding Sync isn't just a technical feature – it fundamentally changes the user experience. Matrix finally feels as responsive as modern messengers.
3. Well-Known delegation is brilliant
The ability to use @username:example.com
as Matrix ID while running the technical server on matrix.example.com
is perfect for branding and flexibility.
4. Docker Compose is completely sufficient
No Kubernetes, no complex orchestration tools needed. Docker Compose with Traefik is absolutely adequate for self-hosting setups.
Lessons learned and tips
Do's ✅
- Use PostgreSQL instead of SQLite – even for single-user setups
- Enable Sliding Sync directly in Synapse instead of separate proxies
- Use Well-Known delegation for clean domain separation
- Test federation immediately with the Matrix Federation Tester
Don'ts ❌
- No complex identity servers for private setups
- No Element Web if you only use native apps
- No excessive resource limits – Matrix has become more efficient
- Don't optimize too early – the defaults work well
Conclusion: Matrix is finally self-hosting-ready
After years of “almost, but not quite” experiences, I can say: Matrix self-hosting is finally seamlessly possible in 2025.
Development has reached the point where installation is no longer the most difficult part. Instead, you can focus on what Matrix is designed for: decentralized, secure communication without vendor lock-in.
Who want to reach me directly and encrypted? @matthias:klein.ruhr 🔐
🏡 Linktree | Follow me also on: GoToSocial |
Lemmy