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:

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:

The target architecture:

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:

  1. Install app
  2. Enter server: example.com
  3. Login: @username:example.com
  4. 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:

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 ✅

Don'ts ❌

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