Eigenen Synapse Server für das Matrix Protokoll betreiben inklusive Element Call

Mal wieder ein Beitrag der vor allem mir als Gedankenstütze dienen soll. Und zwar geht es diesmal um Synapse, dem Referenzserver für das Matrix Protokoll.

Wer hier gelandet ist wird sicher schon wissen was Synapse bzw. das Matrix Protokoll ist. Über diesen Server kann man vor allem chatten aber auch Video- und Audioanrufe abwickeln und noch vieles mehr. Das Matrix Protokoll ist extrem mächtig. Das ist nicht immer ein Vorteil, denn es zu implementieren ist somit auch extrem schwer. Das führt auch dazu das es bisher eigentlich kaum eine ernst zunehmende Alternative zu Synapse oder den Element Clients gibt wenn man den vollen Funktionsumfang nutzen möchte.

Aber warum mach ich mir die Arbeit. Naja im Alltag nutze ich überwiegend Signal, was meiner Meinung nach ein super Dienst ist, auch wenn hier manche Leute jetzt wieder Schnappatmung kriegen. Aber dieser Dienst ist halt zentralisiert, an eine Mobilfunknummer gebunden, liegt im Machtbereich von Donald Trump und falls die Chatkontrolle kommt dann hat man auch schon angedroht sich aus dem europäischen Markt zurück zu ziehen.

Mein eigener Synapse Server ist also mein Plan B in Sachen Kommunikation, ausserdem ist so eine Chatplattform auch ganz praktisch in Bezug auf Smarthome Benachrichtigungen, Datenschutz und Co. Des Weiteren ist Matrix als dezentrales System gedacht, von daher ist es geradezu traurig bis gefährlich wie viele User dieses Ökosystems Ihr Zuhause auf der Hauptinstanz matrix.org haben.

Leider ist das Setup eines Synapse Servers inkl. dem Backend für Element Call nicht trivial, deswegen habe ich das hier mal zusammengefasst. Aber nein das hier ist jetzt keine Anleitung wo ich jedes Fitzelchen kommentiere etc. Teilweise muss man auch das eigene Hirn noch einschalten. Ausserdem ist das ganze an mein spezifisches Setup (Docker mit nativ installiertem nginx als reverse Proxy) angepasst. So oder so ist es ggf. eine Inspiration für euer eigenes Setup. Auch ich musste mir die Infos an etlichen Stellen zusammensuchen. Ich werde hier die einzelnen Config Dateien in den Beitrag einbetten so das Ihr nachschauen könnt was Ihr ändern müsst.

Eigentlich wollte ich Sie auf Codeberg laden aber der Dienst kämpft wohl gerade mit Problemen.

Am Ende solltet Ihr einen Synapse Server inkl. Element Web und Element Call am laufen haben.

Vorraussetzungen:

  1. ein aus dem Internet erreichbarer Linux Server mit Docker, Docker compose und nginx. Falls Ihr eine Firewall einsetzt denkt daran diese entsprechend einzurichten und die notwendigen Ports freizugeben
  2. Zwei Domains, ich nutze im Beispiel matrix.example.eu und matrixrtc.example.eu, diese müsste Ihr natürlich überall wo Sie vorkommen ersetzen
  3. TLS Zertifikate für die beiden Domains

Ordnerstruktur:

Legt euch zuerst eine passende Ordnerstruktur an. Bei mir liegen die einzelnen Containerdefinitionen und Ihre Config Dateien in eigenen Ordnern unter /opt. Für meinen Matrix Server sieht das dann so aus:

opt
└── matrix
    ├── elementweb
    ├── livekit
    ├── postgres
    ├── synapse
    └── docker-compose.yaml

Die docker-compose.yaml müsst ihr jetzt noch nicht erstellen.

Homeserver.yaml generieren:

Haben wir die Ordnerstruktur angelegt lassen wir uns von Synapse eine homeserver.yaml inkl. Keys, Secrets etc erstellen. Das macht Ihr mit folgendem Befehl, die homeserver.yaml findet Ihr danach im Ordner Synapse. Falls Ihr eine andere Ordnerstruktur habt müsst ihr das natürlich anpassen.

docker run -it --rm \
  -v /opt/matrix/synapse:/data \
  -e SYNAPSE_SERVER_NAME=matrix.example.eu \
  -e SYNAPSE_REPORT_STATS=no \
  matrixdotorg/synapse:latest generate

Homeserver.yaml bearbeiten:

Jetzt schaut Ihr euch die homeserver.yaml an und führt die Änderungen durch die Ihr ein meinem Beispiel seht. Bitte nicht einfach Copy und Paste machen, dann sind eure Keys und Secrets weg. Schaut euch alle Zeilen an, wenn Ihr euch bei etwas nicht sicher seit dann schaut in die Synapse Doku. Wie gesagt, selber denken ist wichtig. Setzt auf jeden Fall ein sicheres Passwort für die Postgres Datenbank, Ihr werdet es auch in der Docker Compose Datei brauchen.

# Configuration file for Synapse.
#
# This is a YAML file: see [1] for a quick introduction. Note in particular
# that *indentation is important*: all the elements of a list or dictionary
# should have the same indentation.
#
# [1] https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html
#
# For more information on how to configure Synapse, including a complete accounting of
# each option, go to docs/usage/configuration/config_documentation.md or
# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html
server_name: "matrix.example.eu"
pid_file: /data/homeserver.pid
listeners:
  - port: 8008
    resources:
    - compress: false
      names:
      - client
      - federation
    tls: false
    type: http
    x_forwarded: true
database:
  name: psycopg2
  args:
    user: synapse
    password: mySuperSecretPassword
    database: synapse
    host: db
    port: 5432
    cp_min: 5
    cp_max: 10
log_config: "/data/matrix.example.eu.log.config"
media_store_path: /data/media_store
max_upload_size: 50M
enable_registration: false
enable_registration_without_verification: false
registration_shared_secret: "AutoGenerated"
# Retention policy
retention:
  enabled: true
  default_policy:
    min_lifetime: 1d
    max_lifetime: 365d
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'
report_stats: false
macaroon_secret_key: "AutoGenerated"
form_secret: "AutoGenerated"
signing_key_path: "/data/matrix.example.eu.signing.key"
trusted_key_servers:
  - server_name: "matrix.org"
experimental_features:
  # MSC3266: Room summary API. Used for knocking over federation
  msc3266_enabled: true
  # MSC4222: needed for syncv2 state_after. This allows clients to
  # correctly track the state of the room.
  msc4222_enabled: true
  # MSC4140: Delayed events are required for proper call participation signalling. If disabled it is very likely that you end up with stuck calls in Matrix rooms
  msc4140_enabled: true

# The maximum allowed duration by which sent events can be delayed, as
# per MSC4140.
max_event_delay_duration: 24h

rc_message:
  # This needs to match at least e2ee key sharing frequency plus a bit of headroom
  # Note key sharing events are bursty
  per_second: 0.5
  burst_count: 30
  # This needs to match at least the heart-beat frequency plus a bit of headroom
  # Currently the heart-beat is every 5 seconds which translates into a rate of 0.2s
rc_delayed_event_mgmt:
  per_second: 1
  burst_count: 20

Docker Compose bearbeiten:

Als nächstes nehmen wir uns die Docker Compose Datei vor. Auch darin ist eigentlich alles kommentiert was Ihr ändern müsst. Das wichtigeste ist das Datenbank Passwort sowie Key und Secret für Livekit. Wir Ihr diese generiert steht auch in den Kommentaren der Datei.

services:
  synapse:
    image: matrixdotorg/synapse:latest
    container_name: synapse
    restart: unless-stopped
    volumes:
      - ./synapse:/data
    ports:
      - "127.0.0.1:8008:8008"   # Bind to loopback
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    container_name: synapse-db
    restart: unless-stopped
    environment:
      POSTGRES_USER: synapse
      POSTGRES_PASSWORD: CHANGEME #also in synapse/homeserver.yaml
      POSTGRES_DB: synapse
      POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C"
    volumes:
      - ./postgres:/var/lib/postgresql/data
    ports:
      - "127.0.0.1:5432:5432"

  auth-service:
    image: ghcr.io/element-hq/lk-jwt-service:latest
    container_name: element-call-jwt
    hostname: auth-server
    environment:
      - LIVEKIT_JWT_PORT=8080
      - LIVEKIT_URL=https://matrixrtc.example.eu/livekit/sfu #CHANGEME
      - LIVEKIT_KEY=CHANGEME # use tr -dc 'a-zA-Z0-9' </dev/urandom | head -c 64 to generate key / also change in livekit/config.yaml
      - LIVEKIT_SECRET=CHANGEME # use tr -dc 'a-zA-Z0-9' </dev/urandom | head -c 64 to generate key / also change in livekit/config.yaml
      - LIVEKIT_FULL_ACCESS_HOMESERVERS=matrix.example.eu
    restart: unless-stopped
    ports:
      - 127.0.0.1:8070:8080 #Change 8070 to whichever port you want JWT to be available on locally

  livekit:
    image: livekit/livekit-server:latest
    container_name: element-call-livekit
    command: --config /etc/livekit.yaml
    ports:
      - 127.0.0.1:7880:7880/tcp
      - 7881:7881/tcp
      - 50100-50200:50100-50200/udp
    restart: unless-stopped
    volumes:
      - ./livekit/config.yaml:/etc/livekit.yaml:ro

  element-web:
    image: vectorim/element-web:latest
    restart: unless-stopped
    ports:
      - "127.0.0.1:8009:80"
    healthcheck:
      test: ["CMD-SHELL", "wget -q -O /dev/null http://localhost:80/version || exit 1"]
      start_period: "5s"
      interval: "15s"
      timeout: "5s"
    volumes:
      - ./element-web/config.json:/app/config.json
    depends_on:
      - synapse

Livekit konfiguration:

Nun ist die config.yaml im Unterordner livekit dran. Hier müsst Ihr eigentlich nur die externe IP eures Server sowie Key und Secret anpassen die Ihr gerade generiert habt. Übrigens wie ich letztens noch gelesen habe (https://sspaeth.de/2026/04/matrix-voip-and-livekit/) werdet ihr in den allermeisten Fällen keinen Turn Server brauchen. Also weder den in Livekit noch sowas wie CoTurn.

port: 7880
bind_addresses:
  - "0.0.0.0"
rtc:
  tcp_port: 7881
  port_range_start: 50100
  port_range_end: 50200
  use_external_ip: true
  node_ip: Externe IP eures Servers
room:
  auto_create: false
logging:
  level: info
turn:
  enabled: false
  domain: localhost
  cert_file: ""
  key_file: ""
  tls_port: 5349
  udp_port: 443
  external_tls: true
keys:
  LIVEKIT_KEY: LIVEKIT_SECRET # Values from your docker compose, mind the space!

Element Web konfiguration:

Die letzte Config Datei, diesmal aber im json Format, die Ihr anpassen müsst ist die für Element Web, falls Ihr das verwenden wollt. Auch hier müsst Ihr vorallem die URL’s anpassen etc.

{
    "default_server_config": {
        "m.homeserver": {
            "base_url": "https://matrix.example.eu",
            "server_name": "matrix.example.eu"
        },
        "m.identity_server": {
            "base_url": "https://vector.im"
        }
    },
    "disable_custom_urls": false,
    "disable_guests": false,
    "disable_login_language_selector": false,
    "disable_3pid_login": false,
    "force_verification": false,
    "brand": "Element",
    "default_widget_container_height": 280,
    "default_country_code": "DE",
    "show_labs_settings": false,
    "features": {
        "feature_video_rooms": true,
        "feature_group_calls": true,
        "feature_element_call_video_rooms": true,
        "feature_oidc_native_flow": true
    },
    "default_federate": true,
    "default_theme": "light",
    "room_directory": {
        "servers": ["https://matrix.example.eu"]
    },
    "setting_defaults": {
        "breadcrumbs": true
    },
    "element_call": {
        "url": "https://matrixrtc.example.eu"
    },
    "map_style_url": "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx"
}

Nginx konfigurieren:

Damit euer Synapse Server von aussen erreichbar ist, föderieren kann etc. müsst Ihr natürlich auch noch einen passenden reverse Proxy, in meinem Fall einen nginx einrichten. Dazu könnt Ihr folgende Config nehmen, natürlich müsst ihr die ganzen Domains sowie die Pfade zu den TLS Zertifikaten anspassen.

# HTTPS redirect
server {
    listen 80;
    listen [::]:80;
    server_name matrix.example.eu;

    location / {
        return 301 https://$host$request_uri;
    }
}

# Client API
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name matrix.example.eu;

    ssl_certificate     /etc/letsencrypt/live/matrix.example.eu/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/matrix.example.eu/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;


    client_max_body_size 50M;

    # Well-known for Client Configuration
    location /.well-known/matrix/client {
        return 200 '{"m.homeserver": {"base_url": "https://matrix.example.eu"}, "m.identity_server": {"base_url": "https://vector.im"}, "org.matrix.msc4143.rtc_foci": [{"type": "livekit", "livekit_service_url": "https://matrixrtc.example.eu/livekit/jwt"}]}';
        default_type application/json;
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods 'GET, OPTIONS';
    }

    # Well-known for federation
    location /.well-known/matrix/server {
        return 200 '{"m.server":"matrix.example.eu:8448"}';
        default_type application/json;
    }

    location / {
        proxy_pass http://localhost:8009;
        proxy_set_header X-Forwarded-For $remote_addr;
    }

    # Forward to dockerized Synapse
    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;
        proxy_http_version 1.1;

        proxy_read_timeout    600;
        proxy_connect_timeout 600;
        proxy_send_timeout    600;
    }
}

# Federation Port 8448
server {
    listen 8448 ssl http2;
    listen [::]:8448 ssl http2;
    server_name matrix.example.eu;

    ssl_certificate     /etc/letsencrypt/live/matrix.example.eu/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/matrix.example.eu/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    client_max_body_size 50M;


    location / {
        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;
        proxy_http_version 1.1;
    }
}
# HTTPS redirect
server {
    listen 80;
    listen [::]:80;
    server_name matrixrtc.example.eu;

    location / {
        return 301 https://$host$request_uri;
    }
}

# HTTPS Client API
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name matrixrtc.example.eu;

    ssl_certificate     /etc/letsencrypt/live/matrixrtc.example.eu/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/matrixrtc.example.eu/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

location ^~ /livekit/jwt/ {
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;

      # MatrixRTC Authorization Service running at port 8080
      proxy_pass http://localhost:8070/;
    }

    location ^~ /livekit/sfu/ {
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;

      proxy_send_timeout 120;
      proxy_read_timeout 120;
      proxy_buffering off;

      proxy_set_header Accept-Encoding gzip;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";


      # LiveKit SFU websocket connection running at port 7880
      proxy_pass http://localhost:7880/;
    }
}

Jetzt noch die Config mittels symlink eurem nginx bekant machen und diesen reloaden.

Synapse Server starten:

Danach konnt Ihr wieder in den Ordner /opt/matrix wechseln und den Synapse Server starten:

docker compose up

Ich habe hier das detachen erst einmal weg gelassen, so könnt Ihr die Ausgaben beobachten und wisst auch wann die Datenbankinitialisierung vorbei ist.

Benutzer anlegen:

Nun müsst Ihr natürlich noch User anlegen, da wir die Registrierung deaktiviert haben, machen wir das über die Kommandozeile. Den ersten User machen wir auch gleich zum Admin, bei den anderen Usern tauscht ihr das –admin gegen ein –no-admin.

docker exec -it synapse register_new_matrix_user \
  http://localhost:8008 \
  -c /data/homeserver.yaml \
  -u meinErsterUser \
  -p SuperSicheresPasswort \
  --admin

Nun sollte euer eigener Synapse Server laufen und auch mit anderen Server föderieren können. Falls Ihr konstruktive Anmerkungen habt, eine Fehler in der Config seht etc. dann schreibt einfach einen Kommentar. Wie gesagt das ganze aufzusetzen ist extrem komplex und den einen ultimativen Guide gibt es nicht (und der hier ist es sicher auch nicht).

Aber ich hoffe ich kann auf die Weise den einen oder anderen dazu bewegen sich auch so ein System aufzusetzen und damit dazu beizutragen das Matrix Ökosystem ein bisschen resilienter zu machen.

Björns Techblog
Björns Techblog
@blog@blog.sengotta.net
354 Beiträge
73 Folgende
Fediverse-Reaktionen

Schreibe einen Kommentar