Matrix ist ein dezentrales, offenes Protokoll für sichere, Echtzeit-Kommunikation. Es ermöglicht dir, deinen eigenen Chat-Server zu betreiben, der vollständig unter deiner Kontrolle steht und mit anderen Matrix-Servern kommunizieren kann (Federation). In diesem ausführlichen Tutorial zeige ich dir, wie du einen vollständigen Matrix-Server mit Docker Compose aufsetzt, inklusive Synapse (der Referenz-Server), Element (Web-Client), Traefik (Reverse-Proxy), TURN-Server für Voice/Video und optional WhatsApp-Bridge.
Was ist Matrix?
Matrix ist ein offenes Protokoll für dezentrale, Echtzeit-Kommunikation. Die wichtigsten Vorteile:
- Dezentral: Jeder kann seinen eigenen Server betreiben
- Federiert: Server können miteinander kommunizieren (wie E-Mail)
- Ende-zu-Ende verschlüsselt: Sichere Kommunikation
- Open Source: Vollständig transparent und anpassbar
- Multi-Client: Verschiedene Clients für verschiedene Plattformen
- Rich Features: Text, Voice, Video, Dateien, etc.
Architektur-Übersicht
Dieses Setup umfasst folgende Komponenten:
- Synapse: Der Matrix-Homeserver (Hauptkomponente)
- PostgreSQL: Datenbank für Synapse
- Element Web: Web-basierter Chat-Client
- Traefik: Reverse-Proxy mit automatischem SSL
- Coturn: TURN-Server für Voice/Video-Calls
- LiveKit: Moderne WebRTC-Infrastruktur für Video-Calls
- Synapse Admin: Web-Interface zur Server-Verwaltung
- Mautrix WhatsApp (optional): Bridge zu WhatsApp
Voraussetzungen
Bevor wir mit der Installation beginnen, stelle sicher, dass du folgende Voraussetzungen erfüllst:
Systemanforderungen
- Docker Version 20.10 oder höher - Download
- Docker Compose Version 2.0 oder höher - Download
- Mindestens 2GB RAM (4GB+ empfohlen)
- Mindestens 10GB freier Speicherplatz
- Eine Domain mit DNS-Zugriff (für SSL-Zertifikate)
Installation prüfen
Überprüfe, ob Docker und Docker Compose installiert sind:
docker --version
docker compose version
DNS-Konfiguration
Du benötigst eine Domain mit folgenden DNS-Einträgen:
matrix.deine-domain.de→ Deine Server-IP (für Synapse)chat.deine-domain.de→ Deine Server-IP (für Element)rtc.deine-domain.de→ Deine Server-IP (für LiveKit, optional)jwt.deine-domain.de→ Deine Server-IP (für JWT-Service, optional)
Wichtig: Stelle sicher, dass diese DNS-Einträge aktiv sind, bevor du mit dem Setup beginnst, da Let’s Encrypt die Domain-Validierung durchführt.
Schritt 1: Projekt-Verzeichnis erstellen
Erstelle ein neues Verzeichnis für dein Matrix-Setup:
mkdir matrix-server
cd matrix-server
Schritt 2: Umgebungsvariablen konfigurieren
Erstelle eine .env Datei mit allen notwendigen Konfigurationswerten:
nano .env
Füge folgende Variablen ein und passe sie an deine Umgebung an:
# Synapse Konfiguration
SYNAPSE_SERVER_NAME=matrix.deine-domain.de
SYNAPSE_REPORT_STATS=true
# PostgreSQL
POSTGRES_PASSWORD=dein_sicheres_datenbank_passwort
# AWS S3 für Media Storage (optional, aber empfohlen)
AWS_ACCESS_KEY_ID=dein_aws_access_key
AWS_SECRET_ACCESS_KEY=dein_aws_secret_key
AWS_DEFAULT_REGION=eu-central-1
AWS_S3_ADDRESSING_STYLE=path
# TURN Server Konfiguration
TURN_DOMAIN=rtc.deine-domain.de
TURN_REALM=rtc.deine-domain.de
TURN_STATIC_AUTH_SECRET=generiere_ein_langes_zufaelliges_secret
EXTERNAL_IP=deine_oeffentliche_ip_adresse
RELAY_IP=deine_oeffentliche_ip_adresse
# LiveKit Konfiguration
LIVEKIT_API_KEY=generiere_einen_api_key
LIVEKIT_API_SECRET=generiere_ein_langes_secret
# Mautrix WhatsApp Bridge (optional)
MW_POSTGRES_PASSWORD=dein_whatsapp_bridge_datenbank_passwort
Wichtige Konfigurationsoptionen im Detail
SYNAPSE_SERVER_NAME:
- Dies ist der Domain-Name deines Matrix-Servers
- Muss mit deiner DNS-Konfiguration übereinstimmen
- Beispiel:
matrix.example.com
SYNAPSE_REPORT_STATS:
true: Sende anonyme Statistiken an matrix.orgfalse: Keine Statistiken senden
POSTGRES_PASSWORD:
- Starkes Passwort für die PostgreSQL-Datenbank
- Verwende einen Passwort-Generator für Production
TURN_STATIC_AUTH_SECRET:
- Generiere ein zufälliges Secret für TURN-Authentifizierung
- Mindestens 32 Zeichen lang
- Generieren mit:
openssl rand -hex 32
LIVEKIT_API_KEY und LIVEKIT_API_SECRET:
- Für Video-Calls benötigt
- Generiere mit:
openssl rand -hex 16(für Key) undopenssl rand -hex 32(für Secret)
Sicherheitshinweis: Bewahre deine .env Datei sicher auf und teile sie niemals öffentlich!
Schritt 3: Verzeichnisstruktur erstellen
Erstelle die notwendigen Verzeichnisse für Konfigurationen und Daten:
mkdir -p synapse element/config.json traefik well-known/matrix/server whatsapp certs
Schritt 4: Traefik ACME-Datei erstellen
Erstelle die Datei für Let’s Encrypt-Zertifikate:
touch traefik/acme.json
chmod 600 traefik/acme.json
Die Berechtigung 600 ist wichtig, damit nur der Besitzer die Datei lesen/schreiben kann.
Schritt 5: Synapse-Konfiguration generieren
Synapse benötigt eine initiale Konfigurationsdatei. Wir generieren sie mit dem offiziellen Synapse-Image:
docker run -it --rm \
-v $(pwd)/synapse:/data \
-e SYNAPSE_SERVER_NAME=matrix.deine-domain.de \
-e SYNAPSE_REPORT_STATS=true \
matrixdotorg/synapse:latest generate
Wichtig: Ersetze matrix.deine-domain.de mit deinem tatsächlichen Domain-Namen!
Dies erstellt die Datei synapse/homeserver.yaml. Wir werden diese später anpassen.
Schritt 6: Synapse-Konfiguration anpassen
Öffne die generierte synapse/homeserver.yaml und passe folgende Einstellungen an:
nano synapse/homeserver.yaml
Wichtige Konfigurationen:
Datenbank-Verbindung (bereits konfiguriert durch Umgebungsvariablen):
database:
name: psycopg2
args:
user: synapse
password: ${POSTGRES_PASSWORD}
database: synapse
host: db
cp_min: 5
cp_max: 10
Media Storage (für S3, optional):
media_store_path: "/data/media_store"
s3:
enabled: true
bucket_name: "dein-s3-bucket-name"
region_name: "eu-central-1"
access_key_id: "${AWS_ACCESS_KEY_ID}"
secret_access_key: "${AWS_SECRET_ACCESS_KEY}"
addressing_style: "path"
TURN-Server Konfiguration:
turn_uris:
- "turn:rtc.deine-domain.de:3478?transport=udp"
- "turn:rtc.deine-domain.de:3478?transport=tcp"
- "turns:rtc.deine-domain.de:5349?transport=udp"
- "turns:rtc.deine-domain.de:5349?transport=tcp"
turn_shared_secret: "${TURN_STATIC_AUTH_SECRET}"
turn_user_lifetime: 86400000
turn_allow_guests: true
LiveKit Integration (für moderne Video-Calls):
livekit:
url: "wss://rtc.deine-domain.de/livekit/sfu"
api_key: "${LIVEKIT_API_KEY}"
api_secret: "${LIVEKIT_API_SECRET}"
Federation (wichtig für Kommunikation mit anderen Servern):
federation_domain_whitelist: []
# Leer lassen = alle Domains erlauben
# Oder spezifische Domains auflisten
Registrierung (für neue Benutzer):
enable_registration: true
enable_registration_without_verification: false
registration_shared_secret: "generiere_ein_langes_secret_hier"
Schritt 7: Element-Konfiguration erstellen
Erstelle die Konfigurationsdatei für Element Web:
nano element/config.json
Füge folgende Konfiguration ein:
{
"default_server_config": {
"m.homeserver": {
"base_url": "https://matrix.deine-domain.de",
"server_name": "matrix.deine-domain.de"
},
"m.identity_server": {
"base_url": "https://vector.im"
}
},
"default_server_name": "matrix.deine-domain.de",
"brand": "Dein Chat",
"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/_matrix/integrations/v2",
"https://scalar-staging.vector.im/_matrix/integrations/v1",
"https://scalar-staging.vector.im/_matrix/integrations/v2",
"https://scalar-staging.riot.im/scalar/api"
],
"bug_report_endpoint_url": "https://element.io/bugreports/submit",
"defaultCountryCode": "DE",
"showLabsSettings": true,
"features": {
"feature_new_spinner": true,
"feature_pinning": true,
"feature_custom_status": true,
"feature_custom_tags": true,
"feature_state_resolver": true,
"feature_mjolnir": true,
"feature_dnd": true,
"feature_bridge_state": true,
"feature_presence_in_room_list": true,
"feature_cross_signing": true,
"feature_new_device_manager": true,
"feature_video_rooms": true,
"feature_element_call": true,
"feature_livekit": true
},
"default_federate": true,
"default_theme": "light",
"roomDirectory": {
"servers": [
"matrix.deine-domain.de",
"matrix.org"
]
},
"settingDefaults": {
"breadcrumbs": true
},
"jitsi": {
"preferredDomain": "meet.jit.si"
},
"livekit": {
"url": "wss://rtc.deine-domain.de/livekit/sfu",
"jwt_service_url": "https://jwt.deine-domain.de"
}
}
Wichtig: Ersetze alle deine-domain.de Einträge mit deiner tatsächlichen Domain!
Schritt 8: Well-Known Konfiguration
Erstelle die Well-Known-Dateien für Matrix-Discovery:
mkdir -p well-known/matrix
Erstelle well-known/matrix/server:
nano well-known/matrix/server
Füge folgendes ein (ersetze mit deiner Domain):
{
"m.server": "matrix.deine-domain.de:443"
}
Erstelle well-known/matrix/client:
nano well-known/matrix/client
{
"m.homeserver": {
"base_url": "https://matrix.deine-domain.de"
},
"m.identity_server": {
"base_url": "https://vector.im"
}
}
Schritt 9: Docker Compose Datei erstellen
Erstelle die docker-compose.yml Datei:
nano docker-compose.yml
Füge die vollständige docker-compose.yml ein (siehe unten im Dokument).
Schritt 10: Container starten
Starte alle Container:
docker compose up -d
Der -d Flag startet die Container im Hintergrund. Du kannst den Status überprüfen mit:
docker compose ps
Die Container sollten jetzt starten. Der erste Start kann einige Minuten dauern, da:
- PostgreSQL die Datenbank initialisiert
- Synapse die Datenbank-Schema erstellt
- Traefik SSL-Zertifikate von Let’s Encrypt anfordert
Schritt 11: Logs überwachen
Überwache die Logs, um sicherzustellen, dass alles korrekt startet:
# Alle Container
docker compose logs -f
# Nur Synapse
docker compose logs -f synapse
# Nur Traefik (für SSL-Status)
docker compose logs -f traefik
Wichtige Log-Meldungen
Synapse:
Server started, listening on port 8008→ Server läuftDatabase is ready→ Datenbankverbindung erfolgreichFederation is ready→ Federation aktiviert
Traefik:
Certificate obtained from ACME→ SSL-Zertifikat erfolgreichServer configuration reloaded→ Konfiguration geladen
Schritt 12: Ersten Benutzer erstellen
Nachdem Synapse gestartet ist, erstelle den ersten Admin-Benutzer:
docker compose exec synapse register_new_matrix_user -c /data/homeserver.yaml -a -u admin -p dein_admin_passwort
Wichtig:
-amacht den Benutzer zum Administrator- Verwende ein starkes Passwort!
- Speichere die Credentials sicher!
Schritt 13: Auf den Server zugreifen
Nach erfolgreichem Start kannst du auf folgende URLs zugreifen:
- Element Web Client:
https://chat.deine-domain.de - Synapse API:
https://matrix.deine-domain.de - Synapse Admin:
http://deine-server-ip:8081 - Traefik Dashboard:
http://deine-server-ip:8080(falls aktiviert)
Schritt 14: Erste Anmeldung
- Öffne
https://chat.deine-domain.dein deinem Browser - Klicke auf “Sign In”
- Wähle “Edit” neben dem Server-Namen
- Gib
https://matrix.deine-domain.deein - Melde dich mit deinem Admin-Account an
Erweiterte Konfiguration
Media Storage mit S3
Für Production-Umgebungen wird empfohlen, Media-Dateien in S3 zu speichern:
- Erstelle einen S3-Bucket bei AWS (oder kompatiblem Service)
- Konfiguriere die AWS-Credentials in der
.envDatei - Aktiviere S3 in der
homeserver.yaml(siehe Schritt 6)
WhatsApp-Bridge einrichten
Die Mautrix WhatsApp Bridge ermöglicht es, WhatsApp-Nachrichten über Matrix zu empfangen/senden:
- Starte die Bridge:
docker compose up -d mautrix-whatsapp - Öffne
http://deine-server-ip:29318im Browser - Folge der Anleitung zum QR-Code-Scan
- Die Bridge wird automatisch als AppService in Synapse registriert
Backup-Strategie
Datenbank-Backup:
docker compose exec db pg_dump -U synapse synapse > backup_$(date +%Y%m%d).sql
Synapse-Daten-Backup:
tar -czf synapse_backup_$(date +%Y%m%d).tar.gz synapse/
Automatische Backups (mit Cron):
# Füge zu crontab hinzu (crontab -e)
0 2 * * * cd /pfad/zum/matrix-server && docker compose exec -T db pg_dump -U synapse synapse > backups/db_$(date +\%Y\%m\%d).sql
Häufige Probleme und Lösungen
Problem: SSL-Zertifikat wird nicht erstellt
Symptome: Traefik-Logs zeigen ACME-Fehler
Lösungen:
- Überprüfe, ob die DNS-Einträge korrekt sind:
dig matrix.deine-domain.de - Stelle sicher, dass Port 80 und 443 von außen erreichbar sind
- Überprüfe die Firewall-Einstellungen
- Warte einige Minuten, Let’s Encrypt hat Rate-Limits
Problem: Synapse startet nicht
Symptome: Container stoppt sofort oder Logs zeigen Fehler
Lösungen:
- Überprüfe die Logs:
docker compose logs synapse - Stelle sicher, dass PostgreSQL läuft:
docker compose ps db - Überprüfe die
homeserver.yamlauf Syntax-Fehler - Stelle sicher, dass die Datenbank-Credentials korrekt sind
Problem: Federation funktioniert nicht
Symptome: Kann nicht mit anderen Matrix-Servern kommunizieren
Lösungen:
- Überprüfe, ob Port 8448 von außen erreichbar ist
- Teste mit:
https://federationtester.matrix.org/#matrix.deine-domain.de - Stelle sicher, dass die Well-Known-Dateien korrekt sind
- Überprüfe die Traefik-Labels für Federation
Problem: TURN-Server funktioniert nicht
Symptome: Voice/Video-Calls funktionieren nicht
Lösungen:
- Überprüfe die TURN-Konfiguration in
homeserver.yaml - Stelle sicher, dass die Ports 3478 und 5349 geöffnet sind
- Überprüfe die
TURN_STATIC_AUTH_SECRETin der.env - Teste mit:
https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/
Problem: Element lädt nicht
Symptome: Weißer Bildschirm oder Fehler beim Laden
Lösungen:
- Überprüfe die
config.jsonauf Syntax-Fehler - Stelle sicher, dass die Domain in der Konfiguration korrekt ist
- Überprüfe die Browser-Konsole auf Fehler
- Stelle sicher, dass Traefik die Route korrekt konfiguriert hat
Performance-Optimierung
Datenbank-Optimierung
Füge in homeserver.yaml hinzu:
database:
args:
cp_min: 5
cp_max: 10
cp_reconnect: true
Synapse-Optimierung
Für größere Installationen:
media_store_path: "/data/media_store"
max_upload_size: "50M"
max_image_pixels: "32M"
Ressourcen-Limits
Füge in docker-compose.yml Ressourcen-Limits hinzu:
synapse:
deploy:
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '1'
memory: 1G
Sicherheits-Best-Practices
-
Firewall konfigurieren:
- Öffne nur Ports 80, 443, 8448
- Blockiere alle anderen Ports
-
Regelmäßige Updates:
docker compose pull docker compose up -d -
Starke Passwörter:
- Verwende Passwort-Manager
- Aktiviere 2FA für Admin-Accounts
-
Backups:
- Tägliche Datenbank-Backups
- Wöchentliche Voll-Backups
- Teste die Wiederherstellung regelmäßig
-
Monitoring:
- Überwache Container-Logs
- Setze Alerts für kritische Fehler
- Überwache Ressourcen-Nutzung
Nächste Schritte
Nach erfolgreicher Installation kannst du:
- Weitere Benutzer einladen: Erstelle Accounts für deine Community
- Räume erstellen: Organisiere deine Kommunikation in Räumen
- Bridges einrichten: Verbinde mit anderen Chat-Services
- Bots hinzufügen: Automatisiere Aufgaben mit Matrix-Bots
- Custom Branding: Passe Element an dein Branding an
Fazit
Mit diesem Setup hast du einen vollständig funktionsfähigen Matrix-Server mit:
- ✅ Sichere, verschlüsselte Kommunikation
- ✅ Voice- und Video-Calls
- ✅ Federation mit anderen Servern
- ✅ Automatisches SSL
- ✅ Moderne Web-Client
- ✅ Admin-Interface
Für weitere Informationen und fortgeschrittene Konfigurationen schaue in die offizielle Synapse-Dokumentation.
Viel Erfolg mit deinem Matrix-Server! 💬🔐
Docker Compose Konfiguration
Hier ist die vollständige docker-compose.yml Datei:
services:
traefik:
image: traefik:v3.0
restart: unless-stopped
command:
# Providers
- --providers.docker=true
- --providers.docker.exposedbydefault=false
# Entrypoints
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --entrypoints.federation.address=:8448
#- --entrypoints.turn-udp.address=:3478/udp
#- --entrypoints.turn-tcp.address=:3478/tcp
#- --entrypoints.turns-udp.address=:5349/udp
#- --entrypoints.turns-tcp.address=:5349/tcp
# HTTP -> HTTPS redirect
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
# ACME/Let's Encrypt
- --certificatesresolvers.le.acme.tlschallenge=true
- --certificatesresolvers.le.acme.email=deine-email@deine-domain.de
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
# Optional: set to DEBUG if you need ACME diagnostics
# - --log.level=DEBUG
# Optional: dashboard on internal socket only (no port published)
- --api.dashboard=true
ports:
- "80:80"
- "443:443"
- "8448:8448"
#- "3478:3478/udp"
#- "3478:3478/tcp"
#- "5349:5349/udp"
#- "5349:5349/tcp"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik/acme.json:/letsencrypt/acme.json
db:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_USER: synapse
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: synapse
# Ensure C collation for Synapse
POSTGRES_INITDB_ARGS: "--encoding=UTF8 --lc-collate=C --lc-ctype=C"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U synapse"]
interval: 10s
timeout: 5s
retries: 10
volumes:
- dbdata:/var/lib/postgresql/data
synapse:
image: matrixdotorg/synapse:latest
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
# Keep this aligned with homeserver.yaml server_name
SYNAPSE_SERVER_NAME: ${SYNAPSE_SERVER_NAME}
SYNAPSE_REPORT_STATS: ${SYNAPSE_REPORT_STATS}
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
AWS_DEFAULT_REGION: ${AWS_DEFAULT_REGION}
AWS_S3_ADDRESSING_STYLE: ${AWS_S3_ADDRESSING_STYLE}
volumes:
- ./synapse:/data
- ./whatsapp/:/whatsapp/
#- mw_data:/data
ports:
- "8008:8008"
labels:
- traefik.enable=true
# Client API via 443
- traefik.http.routers.synapse-web.rule=Host(`matrix.deine-domain.de`) && (PathPrefix(`/_matrix`) || PathPrefix(`/_synapse`))
- traefik.http.routers.synapse-web.entrypoints=websecure
- traefik.http.routers.synapse-web.tls.certresolver=le
- traefik.http.routers.synapse-web.service=synapse-web
- traefik.http.services.synapse-web.loadbalancer.server.port=8008
- traefik.http.middlewares.synapse-buf.buffering.maxRequestBodyBytes=0
- traefik.http.routers.synapse-web.middlewares=synapse-buf
# Federation via 8448
- traefik.http.routers.synapse-fed.rule=Host(`matrix.deine-domain.de`) && PathPrefix(`/_matrix`)
- traefik.http.routers.synapse-fed.entrypoints=federation
- traefik.http.routers.synapse-fed.tls.certresolver=le
- traefik.http.routers.synapse-fed.service=synapse-fed
- traefik.http.services.synapse-fed.loadbalancer.server.port=8008
element:
image: vectorim/element-web:latest
restart: unless-stopped
depends_on:
- synapse
volumes:
# Provide your Element config
- ./element/config.json:/app/config.json:ro
labels:
- traefik.enable=true
- traefik.http.routers.element.rule=Host(`chat.deine-domain.de`)
- traefik.http.routers.element.entrypoints=websecure
- traefik.http.routers.element.tls.certresolver=le
- traefik.http.routers.element.service=element
- traefik.http.services.element.loadbalancer.server.port=80
admin:
image: etkecc/synapse-admin
container_name: admin
environment:
REACT_APP_SERVER: https://matrix.deine-domain.de
TZ: Europe/Berlin
ports:
- "8081:80"
restart: unless-stopped
certs-dumper:
image: ldez/traefik-certs-dumper:latest
restart: unless-stopped
depends_on:
- traefik
volumes:
- ./traefik/acme.json:/acme.json:ro
- ./certs:/out
env_file:
- .env
environment:
# Export-Modus: directory mit fullchain.pem und privkey.pem pro Domain
- DOMAIN=${TURN_DOMAIN}
entrypoint:
- sh
- -c
- |
set -e
while true; do
traefik-certs-dumper file --version v2 --watch=false --source /acme.json --domain-subdir --dest /out
if [ -f "${CERTS_PATH}/certificate.crt" ]; then
chown 0:65534 "${CERTS_PATH}/certificate.crt" || true
chmod 0640 "${CERTS_PATH}/certificate.crt" || true
fi
if [ -f "${CERTS_PATH}/privatekey.key" ]; then
chown 0:65534 "${CERTS_PATH}/privatekey.key" || true
chmod 0640 "${CERTS_PATH}/privatekey.key" || true
fi
sleep 3600
done
coturn:
image: coturn/coturn:latest
container_name: coturn
restart: unless-stopped
depends_on:
- certs-dumper
user: "root:root"
entrypoint:
- sh
- -c
- |
set -e
#echo "Waiting for $${CERTS_PATH}/certificate.crt and $${CERTS_PATH}/privatekey.key ..."
#for i in $$(seq 1 180); do
# [ -f "$${CERTS_PATH}/certificate.crt" ] && [ -f "$${CERTS_PATH}/privatekey.key" ] && break
# sleep 1
#done
#[ -f "$${CERTS_PATH}/certificate.crt" ] && [ -f "$${CERTS_PATH}/privatekey.key" ] || { echo "Certs missing after 180s"; exit 1; }
exec turnserver \
--log-file=stdout \
--no-cli \
--fingerprint \
--listening-port=3478 \
--tls-listening-port=5349 \
--use-auth-secret \
--static-auth-secret="$${TURN_STATIC_AUTH_SECRET}" \
--realm="$${TURN_REALM}" \
--min-port=49160 \
--max-port=49200 \
--cert="$${CERTS_PATH}/certificate.crt" \
--pkey="$${CERTS_PATH}/privatekey.key" \
--no-tlsv1 \
--stale-nonce=600 \
--total-quota=100 \
--no-multicast-peers \
--no-tcp-relay \
$${EXTERNAL_IP:+--external-ip=$${EXTERNAL_IP}} \
$${RELAY_IP:+--relay-ip=$${RELAY_IP}}
environment:
- CERTS_PATH="/certs/${TURN_DOMAIN}"
- TURN_REALM=${TURN_REALM}
- TURN_STATIC_AUTH_SECRET=${TURN_STATIC_AUTH_SECRET}
- TURN_DOMAIN=${TURN_DOMAIN}
- EXTERNAL_IP=${EXTERNAL_IP}
- RELAY_IP=${RELAY_IP}
network_mode: host
#ports:
# - "3478:3478/tcp"
# - "3478:3478/udp"
# - "5349:5349/tcp"
# - "5349:5349/udp"
# - "49160-49200:49160-49200/udp"
volumes:
- ./certs:/certs:ro
healthcheck:
test: ["CMD-SHELL", "timeout 2 bash -c '</dev/tcp/127.0.0.1/3478' || exit 1"]
interval: 30s
timeout: 5s
retries: 5
labels:
- traefik.enable=true
- traefik.tcp.routers.turn-udp.rule=HostSNI(`*`)
- traefik.tcp.routers.turn-udp.entrypoints=turn-udp
- traefik.tcp.routers.turn-udp.service=turn-udp
- traefik.tcp.services.turn-udp.loadbalancer.server.port=3478
- traefik.tcp.routers.turn-tcp.rule=HostSNI(`*`)
- traefik.tcp.routers.turn-tcp.entrypoints=turn-tcp
- traefik.tcp.routers.turn-tcp.service=turn-tcp
- traefik.tcp.services.turn-tcp.loadbalancer.server.port=3478
- traefik.tcp.routers.turns-udp.rule=HostSNI(`*`)
- traefik.tcp.routers.turns-udp.entrypoints=turns-udp
- traefik.tcp.routers.turns-udp.service=turns-udp
- traefik.tcp.services.turns-udp.loadbalancer.server.port=5349
- traefik.tcp.routers.turns-tcp.rule=HostSNI(`*`)
- traefik.tcp.routers.turns-tcp.entrypoints=turns-tcp
- traefik.tcp.routers.turns-tcp.service=turns-tcp
- traefik.tcp.services.turns-tcp.loadbalancer.server.port=5349
livekit:
image: livekit/livekit-server:latest
restart: unless-stopped
environment:
LIVEKIT_PORT: "7880"
LIVEKIT_RTC_TCP_PORT: "7881"
LIVEKIT_RTC_UDP_PORT: "7882" # alternativ Port-Range 50000-60000/udp
LIVEKIT_KEYS: "${LIVEKIT_API_KEY}: ${LIVEKIT_API_SECRET}"
LIVEKIT_WEBRTC_USE_EXTERNAL_IP: "true"
command: >
--bind 0.0.0.0
--node-ip 0.0.0.0
--port 7880
#--rtc.tcp_port 7881
#--rtc.udp_port 7882
labels:
- traefik.enable=true
- traefik.http.routers.livekit.rule=Host(`rtc.deine-domain.de`)
- traefik.http.routers.livekit.entrypoints=websecure
- traefik.http.routers.livekit.tls.certresolver=le
- traefik.http.middlewares.livekit-strip.stripprefix.prefixes=/livekit/sfu
- traefik.http.routers.livekit.middlewares=livekit-strip
- traefik.http.services.livekit.loadbalancer.server.port=7880
ports:
- "7881:7881/tcp" # WebRTC TCP fallback
- "7882:7882/udp" # WebRTC UDP (UDP-Mux)
# Falls du statt UDP-Mux lieber Range nutzt: 50000-60000/udp publishen
lk-jwt:
image: ghcr.io/element-hq/lk-jwt-service:latest
restart: unless-stopped
environment:
LIVEKIT_URL: "wss://rtc.deine-domain.de/livekit/sfu" # via Traefik auf 7880
LIVEKIT_KEY: "${LIVEKIT_API_KEY}"
LIVEKIT_SECRET: "${LIVEKIT_API_SECRET}"
# optional: RATE_LIMIT, LOG_LEVEL usw.
labels:
- traefik.enable=true
# Route: Host + Pfadpräfix
- traefik.http.routers.lkjwt.rule=Host(`jwt.deine-domain.de`)
- traefik.http.routers.lkjwt.entrypoints=websecure
- traefik.http.routers.lkjwt.tls.certresolver=le
- traefik.http.services.lkjwt.loadbalancer.server.port=8080
# CORS freischalten (sonst blockt der Browser die JWT-Anfrage)
- traefik.http.middlewares.lkjwt-cors.headers.accesscontrolallowmethods=GET,OPTIONS
- traefik.http.middlewares.lkjwt-cors.headers.accesscontrolallowheaders=*
- traefik.http.middlewares.lkjwt-cors.headers.accesscontrolalloworiginlist=*
- traefik.http.middlewares.lkjwt-cors.headers.addvaryheader=true
# Middlewares anwenden (Reihenfolge egal)
- traefik.http.routers.lkjwt.middlewares=lkjwt-cors
wellknown:
image: nginx:alpine
volumes:
- ./well-known:/usr/share/nginx/html/.well-known:ro
labels:
- traefik.enable=true
- traefik.http.routers.wellknown.rule=Host(`matrix.deine-domain.de`) && PathPrefix(`/.well-known/matrix`)
- traefik.http.routers.wellknown.entrypoints=websecure
- traefik.http.routers.wellknown.tls.certresolver=le
- traefik.http.services.wellknown.loadbalancer.server.port=80
mw-db:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_DB: mautrix_whatsapp
POSTGRES_USER: mautrix
POSTGRES_PASSWORD: ${MW_POSTGRES_PASSWORD:-change_me}
volumes:
- mw_db:/var/lib/postgresql/data
mautrix-whatsapp:
image: dock.mau.dev/mautrix/whatsapp:latest
restart: unless-stopped
depends_on:
- mw-db
volumes:
- ./whatsapp/:/data/
ports:
- "29318:29318" # AppService-Callback Port für Synapse
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:29318/health"]
interval: 30s
timeout: 5s
retries: 5
volumes:
dbdata:
mw_db:
mw_data:
Wichtig: Ersetze alle deine-domain.de Einträge in der docker-compose.yml mit deiner tatsächlichen Domain!