- PHP 89.7%
- HTML 10.3%
| config | ||
| matrix | ||
| nginx | ||
| .env | ||
| docker-compose.yml | ||
| integration-example.html | ||
| README.md | ||
| verify-example.php | ||
mCaptcha + Matrix + Bridges – Self-Hosted Setup
Vollständige Anleitung für eine self-hosted Infrastruktur bestehend aus mCaptcha (Datenschutzfreundliches CAPTCHA), Matrix Synapse (Kommunikationsserver), Element Web (Chat-Client) und Bridges zu Telegram, Discord und WhatsApp – alles per Docker Compose auf Ubuntu Server mit Nginx Proxy Manager auf einem separaten Server im selben LAN.
Inhaltsverzeichnis
- 1. Was ist mCaptcha?
- 2. Netzwerkarchitektur
- 3. Voraussetzungen
- 4. Verzeichnisstruktur
- 5. mCaptcha Konfigurationsdateien
- 6. Firewall einrichten
- 7. Nginx Proxy Manager einrichten
- 8. mCaptcha Installation & Start
- 9. Sitekey erstellen
- 10. Integration in die eigene Website
- 11. mCaptcha Wartung & Betrieb
- 12. mCaptcha Fehlerbehebung
- 13. Matrix Synapse – Integration mit mCaptcha
- 14. Matrix Installation & Erststart
- 15. Matrix Wartung
- 16. Bridges – Telegram, Discord & WhatsApp
- 17. Telegram Bridge
- 18. Discord Bridge
- 19. WhatsApp Bridge
- 20. Bridges Wartung & Fehlerbehebung
1. Was ist mCaptcha?
mCaptcha ist ein datenschutzfreundliches, Open-Source CAPTCHA-System auf Basis von Proof-of-Work (PoW). Im Gegensatz zu Google reCAPTCHA oder hCaptcha werden keine persönlichen Daten gesammelt und keine Cookies gesetzt.
Vorteile:
- 100 % self-hosted – keine Abhängigkeit von Drittanbietern
- Kein Tracking, keine Cookies, DSGVO-konform
- Proof-of-Work statt Bildrätseln – benutzerfreundlich
- Kostenlos und Open Source (AGPL-3.0)
2. Netzwerkarchitektur
Important
In diesem Setup laufen Nginx Proxy Manager (NPM) und die Applikationsserver auf zwei verschiedenen Hosts im selben LAN. Docker-Netzwerke sind host-lokal und können nicht serverübergreifend genutzt werden. Die Kommunikation zwischen NPM und den Diensten läuft daher über die interne LAN-IP des App-Servers.
┌─────────────────────────────────────────────────────────────┐
│ Internet │
└───────────────────────────┬─────────────────────────────────┘
│ :80 / :443
┌───────────────────────────▼─────────────────────────────────┐
│ PROXY-SERVER (z.B. 192.168.1.10) │
│ Nginx Proxy Manager – SSL-Terminierung │
│ Let's Encrypt – Reverse Proxy │
└───────────────────────────┬─────────────────────────────────┘
│ internes LAN (192.168.1.0/24)
┌───────────────────────────▼─────────────────────────────────┐
│ APP-SERVER (z.B. 192.168.1.20) │
│ │
│ ┌──────────────────────┐ ┌───────────────────────────┐ │
│ │ mcaptcha_app │ │ matrix_synapse │ │
│ │ LAN:7000 │ │ LAN:8008 │ │
│ ├──────────────────────┤ ├───────────────────────────┤ │
│ │ mcaptcha_db (intern)│ │ matrix_db (intern) │ │
│ ├──────────────────────┤ ├───────────────────────────┤ │
│ │ mcaptcha_cache │ │ matrix_element LAN:8080 │ │
│ │ (intern) │ │ matrix_register LAN:8090 │ │
│ └──────────────────────┘ └───────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Bridges (nur intern – kein Port nach außen!) │ │
│ │ bridge_telegram · bridge_discord · bridge_whatsapp │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Wichtige Konsequenzen dieser Architektur:
| Aspekt | Konsequenz |
|---|---|
| Kein gemeinsames Docker-Netzwerk | Kein proxy external network – NPM ist auf anderem Host |
| Ports binden an LAN-IP | ${LAN_IP}:7000:7000 statt 127.0.0.1:7000:7000 |
| NPM trägt LAN-IP ein | Forward Host = 192.168.1.20, nicht der Container-Name |
| Firewall Pflicht | Ports nur für die IP des Proxy-Servers freigeben |
| Bridges: kein Port nach außen | Bridges kommunizieren intern per Docker-Netzwerk mit Synapse |
3. Voraussetzungen
App-Server (wo Docker läuft):
- Ubuntu Server mit Docker & Docker Compose v2.x
- Bekannte, feste LAN-IP (Beispiel hier:
192.168.1.20) ufwfür die Firewall
Proxy-Server (wo NPM läuft):
- Nginx Proxy Manager bereits installiert
- Bekannte LAN-IP (Beispiel hier:
192.168.1.10) - Ports 80 und 443 aus dem Internet erreichbar
DNS:
- Alle Domains zeigen per A-Record auf die öffentliche IP des Proxy-Servers
4. Verzeichnisstruktur
mcaptcha/
├── docker-compose.yml
├── .env # Secrets – NIEMALS committen!
├── config/
│ └── config.toml
└── matrix/
├── docker-compose-matrix.yml
├── homeserver.yaml
├── log.config
├── element-config.json
├── registration/
│ └── index.php
└── bridges/
├── docker-compose-bridges.yml
├── telegram/
│ ├── config.yaml
│ └── registration.yaml # (auto-generiert)
├── discord/
│ ├── config.yaml
│ └── registration.yaml # (auto-generiert)
└── whatsapp/
├── config.yaml
└── registration.yaml # (auto-generiert)
mkdir -p /opt/docker/mcaptcha/{config,matrix/registration,matrix/bridges/{telegram,discord,whatsapp}}
cd /opt/docker/mcaptcha
5. mCaptcha Konfigurationsdateien
5.1 docker-compose.yml
Note
Die Ports binden an
${LAN_IP}– die LAN-IP des App-Servers. Es gibt keinproxyDocker-Netzwerk, da NPM auf einem anderen Host läuft und Docker-Netzwerke nicht serverübergreifend funktionieren.
version: "3.8"
services:
db:
image: postgres:15-alpine
container_name: mcaptcha_db
restart: unless-stopped
environment:
POSTGRES_USER: ${MCAPTCHA_DB_USER}
POSTGRES_PASSWORD: ${MCAPTCHA_DB_PASSWORD}
POSTGRES_DB: ${MCAPTCHA_DB_NAME}
volumes:
- mcaptcha_db_data:/var/lib/postgresql/data
networks:
- mcaptcha_internal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${MCAPTCHA_DB_USER}"]
interval: 10s
timeout: 5s
retries: 5
cache:
image: redis:7-alpine
container_name: mcaptcha_cache
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- mcaptcha_redis_data:/data
networks:
- mcaptcha_internal
mcaptcha:
image: mcaptcha/mcaptcha:latest
container_name: mcaptcha_app
restart: unless-stopped
depends_on:
db:
condition: service_healthy
cache:
condition: service_healthy
environment:
MCAPTCHA_DATABASE_URL: postgres://${MCAPTCHA_DB_USER}:${MCAPTCHA_DB_PASSWORD}@db:5432/${MCAPTCHA_DB_NAME}
MCAPTCHA_REDIS_URL: redis://:${REDIS_PASSWORD}@cache:6379
MCAPTCHA_SERVER_IP: 0.0.0.0
MCAPTCHA_SERVER_PORT: 7000
MCAPTCHA_APP_SECRET_KEY: ${APP_SECRET_KEY}
MCAPTCHA_SERVER_DOMAIN: ${MCAPTCHA_DOMAIN}
MCAPTCHA_ALLOW_REGISTRATION: ${ALLOW_REGISTRATION:-true}
volumes:
- ./config/config.toml:/etc/mcaptcha/config.toml:ro
ports:
# Nur für den NPM-Server erreichbar – Firewall absichern! (Kapitel 6)
- "${LAN_IP}:7000:7000"
networks:
- mcaptcha_internal
networks:
mcaptcha_internal:
driver: bridge
internal: true
volumes:
mcaptcha_db_data:
mcaptcha_redis_data:
5.2 .env
Warning
Diese Datei niemals committen – in
.gitignoreeintragen!
# ── Netzwerk ─────────────────────────────────────────────────
# LAN-IP dieses App-Servers (nicht die öffentliche IP!)
LAN_IP=192.168.1.20
# ── mCaptcha ─────────────────────────────────────────────────
MCAPTCHA_DB_USER=mcaptcha
MCAPTCHA_DB_PASSWORD=SICHERES_DB_PASSWORT_HIER
MCAPTCHA_DB_NAME=mcaptcha
REDIS_PASSWORD=SICHERES_REDIS_PASSWORT_HIER
# Generieren mit: openssl rand -base64 64
APP_SECRET_KEY=DEIN_GEHEIMER_APP_SCHLUESSEL_MINDESTENS_64_ZEICHEN
# Nur der Hostname, ohne https://
MCAPTCHA_DOMAIN=captcha.deine-domain.de
# Nach erster Registrierung auf "false" setzen!
ALLOW_REGISTRATION=true
5.3 config/config.toml
[server]
ip = "0.0.0.0"
port = 7000
[database]
pool_size = 5
[captcha]
salt = 32
[logging]
level = "info"
[settings]
source_code = "https://github.com/mCaptcha/mCaptcha"
allow_demo = false
6. Firewall einrichten
Important
Da die Ports an der LAN-IP hängen, muss die Firewall auf dem App-Server sicherstellen, dass ausschließlich der Proxy-Server (
192.168.1.10) auf die App-Ports zugreifen darf. Alle anderen Verbindungen werden geblockt.
# Auf dem App-Server (192.168.1.20) ausführen:
# SSH zuerst dauerhaft erlauben!
sudo ufw allow ssh
# ── mCaptcha ─────────────────────────────────────────────────
sudo ufw allow from 192.168.1.10 to any port 7000 comment "mCaptcha – NPM"
# ── Matrix Synapse ────────────────────────────────────────────
sudo ufw allow from 192.168.1.10 to any port 8008 comment "Synapse – NPM"
# ── Element Web ───────────────────────────────────────────────
sudo ufw allow from 192.168.1.10 to any port 8080 comment "Element – NPM"
# ── Registrierungsseite ───────────────────────────────────────
sudo ufw allow from 192.168.1.10 to any port 8090 comment "Matrix Registrierung – NPM"
# Firewall aktivieren
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enable
# Prüfen
sudo ufw status verbose
Note
Die Bridges (Telegram, Discord, WhatsApp) bekommen keinen Port nach außen. Sie kommunizieren ausschließlich intern über das Docker-Netzwerk
matrix_internalmit Synapse – keine Firewall-Regel nötig.
7. Nginx Proxy Manager einrichten
NPM läuft auf dem Proxy-Server (192.168.1.10) und leitet Anfragen an die LAN-IP des App-Servers weiter.
Important
Im Feld Forward Hostname / IP immer die LAN-IP des App-Servers (
192.168.1.20) eintragen – niemals den Docker-Container-Namen, da NPM auf einem anderen Host läuft und diese Namen nicht auflösen kann.
Proxy Hosts Übersicht
| Domain | Forward Host | Port | Websockets | SSL |
|---|---|---|---|---|
captcha.deine-domain.de |
192.168.1.20 |
7000 |
✓ | Let's Encrypt |
matrix.deine-domain.de |
192.168.1.20 |
8008 |
✓ | Let's Encrypt |
element.deine-domain.de |
192.168.1.20 |
8080 |
✓ | Let's Encrypt |
register.deine-domain.de |
192.168.1.20 |
8090 |
✗ | Let's Encrypt |
Custom Config – mCaptcha
Im Advanced Tab des Proxy Hosts für captcha.deine-domain.de:
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 60s;
Custom Config – Matrix Synapse
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400s;
client_max_body_size 50M;
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization" always;
if ($request_method = OPTIONS) {
return 204;
}
Well-Known Delegation
Für kurze Matrix-Adressen (@nutzer:deine-domain.de) im Proxy Host von deine-domain.de unter Advanced eintragen:
location /.well-known/matrix/client {
return 200 '{"m.homeserver":{"base_url":"https://matrix.deine-domain.de"}}';
add_header Content-Type application/json;
add_header Access-Control-Allow-Origin *;
}
location /.well-known/matrix/server {
return 200 '{"m.server":"matrix.deine-domain.de:443"}';
add_header Content-Type application/json;
add_header Access-Control-Allow-Origin *;
}
8. mCaptcha Installation & Start
# Secret Key generieren und als APP_SECRET_KEY in .env eintragen
openssl rand -base64 64
# LAN_IP in .env auf die tatsächliche LAN-IP setzen
cd /opt/docker/mcaptcha
docker compose up -d
docker compose logs -f mcaptcha
Admin-Account erstellen:
https://captcha.deine-domain.deaufrufen- Register klicken und Account anlegen
- In
.envsetzen:ALLOW_REGISTRATION=false - Neu starten:
docker compose up -d --force-recreate mcaptcha
9. Sitekey erstellen
- Nach dem Login auf Add Site klicken
- Name und Domain der Website eintragen
- Schwierigkeitsstufen konfigurieren:
- Level 1 – Threshold:
0, Difficulty:1000(Normalbetrieb) - Level 2 – Threshold:
100, Difficulty:50000(unter Last)
- Level 1 – Threshold:
- Sitekey kopieren – wird für das Widget und die Matrix-Registrierungsseite benötigt
10. Integration in die eigene Website
Frontend (HTML)
<!-- Im <head> -->
<script src="https://captcha.deine-domain.de/api/v1/pow/config.js"></script>
<!-- Im Formular -->
<div
class="mcaptcha__widget-container"
data-sitekey="DEIN_SITEKEY"
data-mcaptcha-url="https://captcha.deine-domain.de"
></div>
<!-- Vor </body> -->
<script
src="https://captcha.deine-domain.de/static/widget/bundle/bundle.js"
data-sitekey="DEIN_SITEKEY"
data-mcaptcha-url="https://captcha.deine-domain.de"
></script>
Server-seitige Verifikation (PHP)
Important
Der Token muss serverseitig verifiziert werden. Clientseitige Prüfung allein ist unsicher.
function verifiziereMcaptcha(string $token, string $sitekey): bool
{
$ch = curl_init('https://captcha.deine-domain.de/api/v1/pow/siteverify');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(['token' => $token, 'key' => $sitekey]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
]);
$response = curl_exec($ch);
curl_close($ch);
return (json_decode($response, true)['valid'] ?? false) === true;
}
$token = $_POST['mcaptcha__token'] ?? '';
if (!verifiziereMcaptcha($token, 'DEIN_SITEKEY')) {
die('CAPTCHA-Verifikation fehlgeschlagen.');
}
11. mCaptcha Wartung & Betrieb
| Befehl | Beschreibung |
|---|---|
docker compose up -d |
Container starten |
docker compose down |
Container stoppen |
docker compose logs -f |
Live-Logs |
docker compose pull && docker compose up -d |
Update |
docker image prune -f |
Alte Images entfernen |
Backup:
docker exec mcaptcha_db pg_dump -U mcaptcha mcaptcha > backup_$(date +%Y%m%d).sql
12. mCaptcha Fehlerbehebung
502 Bad Gateway im NPM
# Vom Proxy-Server (192.168.1.10) testen:
curl -v http://192.168.1.20:7000
# Firewall auf dem App-Server prüfen:
sudo ufw status verbose | grep 7000
# Lauscht der Port auf der richtigen IP?
ss -tlnp | grep 7000
# Erwartet: 192.168.1.20:7000
Widget lädt nicht / CORS-Fehler
MCAPTCHA_DOMAINmuss exakt mit der aufgerufenen Domain übereinstimmen- Websockets Support im NPM Proxy Host aktiviert?
Token-Verifikation schlägt fehl
Content-Type: application/jsonim Request gesetzt?- Endpunkt prüfen:
/api/v1/pow/siteverify
13. Matrix Synapse – Integration mit mCaptcha
Da Synapse keine native mCaptcha-Unterstützung bietet, läuft die Registrierung über eine eigene PHP-Seite, die nach erfolgreicher CAPTCHA-Prüfung den Nutzer per HMAC-signiertem Shared Secret bei Synapse registriert.
Container-Übersicht:
| Container | Port (LAN) | Zweck |
|---|---|---|
matrix_synapse |
192.168.1.20:8008 |
Homeserver API + Federation |
matrix_db |
intern | PostgreSQL für Matrix |
matrix_element |
192.168.1.20:8080 |
Element Web Client |
matrix_registration |
192.168.1.20:8090 |
Registrierungsseite mit mCaptcha |
Warning
enable_registration: falsemuss inhomeserver.yamlgesetzt sein. Die PHP-Seite ist der einzige Registrierungsweg.
13.1 matrix/docker-compose-matrix.yml
version: "3.8"
services:
synapse:
image: matrixdotorg/synapse:latest
container_name: matrix_synapse
restart: unless-stopped
depends_on:
matrix_db:
condition: service_healthy
environment:
SYNAPSE_CONFIG_PATH: /data/homeserver.yaml
volumes:
- synapse_data:/data
- ./matrix/homeserver.yaml:/data/homeserver.yaml:ro
- ./matrix/log.config:/data/log.config:ro
ports:
- "${LAN_IP}:8008:8008"
networks:
- matrix_internal
matrix_db:
image: postgres:15-alpine
container_name: matrix_db
restart: unless-stopped
environment:
POSTGRES_USER: ${MATRIX_DB_USER}
POSTGRES_PASSWORD: ${MATRIX_DB_PASSWORD}
POSTGRES_DB: ${MATRIX_DB_NAME}
POSTGRES_INITDB_ARGS: "--encoding=UTF8 --lc-collate=C --lc-ctype=C"
volumes:
- matrix_db_data:/var/lib/postgresql/data
networks:
- matrix_internal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${MATRIX_DB_USER}"]
interval: 10s
timeout: 5s
retries: 5
element_web:
image: vectorim/element-web:latest
container_name: matrix_element
restart: unless-stopped
volumes:
- ./matrix/element-config.json:/app/config.json:ro
ports:
- "${LAN_IP}:8080:80"
matrix_registration:
image: php:8.2-apache
container_name: matrix_registration
restart: unless-stopped
volumes:
- ./matrix/registration:/var/www/html:ro
ports:
- "${LAN_IP}:8090:80"
networks:
- matrix_internal # Muss Synapse intern erreichen können
environment:
SYNAPSE_URL: http://matrix_synapse:8008
MATRIX_SERVER_NAME: ${MATRIX_DOMAIN}
MATRIX_REGISTRATION_SECRET: ${MATRIX_REGISTRATION_SECRET}
MCAPTCHA_URL: https://${MCAPTCHA_DOMAIN}
MCAPTCHA_SITEKEY: ${MCAPTCHA_SITEKEY}
networks:
matrix_internal:
driver: bridge
internal: true
volumes:
synapse_data:
matrix_db_data:
13.2 .env – Matrix-Variablen ergänzen
# Matrix Synapse
MATRIX_DB_USER=synapse
MATRIX_DB_PASSWORD=SICHERES_MATRIX_DB_PASSWORT_HIER
MATRIX_DB_NAME=synapse
MATRIX_DOMAIN=deine-domain.de
# python3 -c "import secrets; print(secrets.token_hex(64))"
MATRIX_REGISTRATION_SECRET=DEIN_REGISTRATION_SHARED_SECRET_HIER
# Sitekey aus dem mCaptcha Dashboard
MCAPTCHA_SITEKEY=DEIN_SITEKEY_AUS_DEM_MCAPTCHA_DASHBOARD
13.3 matrix/homeserver.yaml
Caution
server_namedarf nach dem ersten Start niemals geändert werden!
server_name: "deine-domain.de"
listeners:
- port: 8008
tls: false
type: http
x_forwarded: true
bind_addresses: ["0.0.0.0"]
resources:
- names: [client, federation]
compress: false
database:
name: psycopg2
args:
user: synapse
password: MATRIX_DB_PASSWORT_HIER # = MATRIX_DB_PASSWORD aus .env
database: synapse
host: matrix_db
port: 5432
cp_min: 5
cp_max: 10
media_store_path: /data/media_store
log_config: "/data/log.config"
signing_key_path: "/data/deine-domain.de.signing.key"
trusted_key_servers:
- server_name: "matrix.org"
enable_registration: false
registration_shared_secret: "DEIN_REGISTRATION_SHARED_SECRET_HIER"
allow_guest_access: false
max_upload_size: 50M
14. Matrix Installation & Erststart
# Secrets generieren
openssl rand -base64 32 # → MATRIX_DB_PASSWORD
python3 -c "import secrets; print(secrets.token_hex(64))" # → MATRIX_REGISTRATION_SECRET
# Signing Key + initiale Config generieren (einmalig!)
docker run --rm \
-v $(pwd)/matrix:/data \
-e SYNAPSE_SERVER_NAME=deine-domain.de \
-e SYNAPSE_REPORT_STATS=no \
matrixdotorg/synapse:latest generate
# homeserver.yaml anpassen (DB-Passwort + Registration Secret eintragen)
# Firewall-Regeln aktivieren (Kapitel 6)
# Matrix-Container starten
docker compose -f docker-compose.yml \
-f matrix/docker-compose-matrix.yml up -d
# Erreichbarkeit vom Proxy-Server testen:
# curl -s http://192.168.1.20:8008/_matrix/client/versions
Admin-Account erstellen:
docker exec -it matrix_synapse register_new_matrix_user \
-c /data/homeserver.yaml \
-u admin -p 'SICHERES_PASSWORT' \
--admin http://localhost:8008
15. Matrix Wartung
| Befehl | Beschreibung |
|---|---|
docker compose logs -f synapse |
Live-Logs |
docker exec -it matrix_synapse bash |
Shell |
docker exec matrix_db psql -U synapse |
DB-Shell |
Backup:
docker exec matrix_db pg_dump -U synapse synapse > matrix_backup_$(date +%Y%m%d).sql
docker run --rm -v synapse_data:/data -v $(pwd):/backup alpine \
tar czf /backup/synapse_media_$(date +%Y%m%d).tar.gz /data/media_store
16. Bridges – Telegram, Discord & WhatsApp
Bridges verbinden Matrix mit anderen Plattformen. Alle drei Bridges laufen rein intern im Docker-Netzwerk matrix_internal – sie haben keinen Port nach außen und sind damit vom Internet vollständig isoliert.
| Bridge | Image | Interner Port | Extern erreichbar |
|---|---|---|---|
| Telegram | dock.mau.dev/mautrix/telegram |
29317 |
✗ Nein |
| Discord | dock.mau.dev/mautrix/discord |
29334 |
✗ Nein |
dock.mau.dev/mautrix/whatsapp |
29318 |
✗ Nein |
16.1 matrix/bridges/docker-compose-bridges.yml
version: "3.8"
services:
bridge_telegram:
image: dock.mau.dev/mautrix/telegram:latest
container_name: matrix_bridge_telegram
restart: unless-stopped
depends_on:
bridge_telegram_db:
condition: service_healthy
volumes:
- ./matrix/bridges/telegram:/data
networks:
- matrix_internal # Erreicht Synapse intern per Container-Name
- bridge_internal # Isoliertes Netz für DB-Zugriff
# Kein ports: – Bridge ist nicht von außen erreichbar!
bridge_telegram_db:
image: postgres:15-alpine
container_name: matrix_bridge_telegram_db
restart: unless-stopped
environment:
POSTGRES_USER: ${TELEGRAM_BRIDGE_DB_USER}
POSTGRES_PASSWORD: ${TELEGRAM_BRIDGE_DB_PASSWORD}
POSTGRES_DB: ${TELEGRAM_BRIDGE_DB_NAME}
volumes:
- telegram_bridge_db:/var/lib/postgresql/data
networks:
- bridge_internal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${TELEGRAM_BRIDGE_DB_USER}"]
interval: 10s
timeout: 5s
retries: 5
bridge_discord:
image: dock.mau.dev/mautrix/discord:latest
container_name: matrix_bridge_discord
restart: unless-stopped
depends_on:
bridge_discord_db:
condition: service_healthy
volumes:
- ./matrix/bridges/discord:/data
networks:
- matrix_internal
- bridge_internal
bridge_discord_db:
image: postgres:15-alpine
container_name: matrix_bridge_discord_db
restart: unless-stopped
environment:
POSTGRES_USER: ${DISCORD_BRIDGE_DB_USER}
POSTGRES_PASSWORD: ${DISCORD_BRIDGE_DB_PASSWORD}
POSTGRES_DB: ${DISCORD_BRIDGE_DB_NAME}
volumes:
- discord_bridge_db:/var/lib/postgresql/data
networks:
- bridge_internal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DISCORD_BRIDGE_DB_USER}"]
interval: 10s
timeout: 5s
retries: 5
bridge_whatsapp:
image: dock.mau.dev/mautrix/whatsapp:latest
container_name: matrix_bridge_whatsapp
restart: unless-stopped
depends_on:
bridge_whatsapp_db:
condition: service_healthy
volumes:
- ./matrix/bridges/whatsapp:/data
networks:
- matrix_internal
- bridge_internal
bridge_whatsapp_db:
image: postgres:15-alpine
container_name: matrix_bridge_whatsapp_db
restart: unless-stopped
environment:
POSTGRES_USER: ${WHATSAPP_BRIDGE_DB_USER}
POSTGRES_PASSWORD: ${WHATSAPP_BRIDGE_DB_PASSWORD}
POSTGRES_DB: ${WHATSAPP_BRIDGE_DB_NAME}
volumes:
- whatsapp_bridge_db:/var/lib/postgresql/data
networks:
- bridge_internal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${WHATSAPP_BRIDGE_DB_USER}"]
interval: 10s
timeout: 5s
retries: 5
networks:
matrix_internal:
external: true
name: mcaptcha_matrix_internal # Von Matrix-Compose erstellt
bridge_internal:
driver: bridge
internal: true
volumes:
telegram_bridge_db:
discord_bridge_db:
whatsapp_bridge_db:
16.2 .env – Bridge-Passwörter ergänzen
TELEGRAM_BRIDGE_DB_USER=tg_bridge
TELEGRAM_BRIDGE_DB_PASSWORD=SICHERES_TELEGRAM_DB_PASSWORT
TELEGRAM_BRIDGE_DB_NAME=tg_bridge
DISCORD_BRIDGE_DB_USER=dc_bridge
DISCORD_BRIDGE_DB_PASSWORD=SICHERES_DISCORD_DB_PASSWORT
DISCORD_BRIDGE_DB_NAME=dc_bridge
WHATSAPP_BRIDGE_DB_USER=wa_bridge
WHATSAPP_BRIDGE_DB_PASSWORD=SICHERES_WHATSAPP_DB_PASSWORT
WHATSAPP_BRIDGE_DB_NAME=wa_bridge
16.3 Allgemeiner Initialisierungsablauf
Note
Jede Bridge muss vor dem ersten Start ihre
registration.yamlgenerieren. Diese Datei verbindet Bridge und Synapse über AS-Token und HS-Token – ohne sie ignoriert Synapse alle Bridge-Nachrichten.
# 1. config.yaml anpassen (Domain, DB-Passwort, Credentials)
# 2. Registration generieren – Beispiel Telegram:
docker run --rm \
-v /opt/docker/mcaptcha/matrix/bridges/telegram:/data \
dock.mau.dev/mautrix/telegram:latest
# 3. registration.yaml in den Synapse-Datenordner kopieren
mkdir -p matrix/synapse_data/bridges
cp matrix/bridges/telegram/registration.yaml \
matrix/synapse_data/bridges/telegram-registration.yaml
# (ebenso für discord und whatsapp)
# 4. homeserver.yaml ergänzen (s. 16.4)
# 5. Synapse neu starten
docker compose -f matrix/docker-compose-matrix.yml restart synapse
# 6. Alle Bridges starten
docker compose \
-f docker-compose.yml \
-f matrix/docker-compose-matrix.yml \
-f matrix/bridges/docker-compose-bridges.yml \
up -d
16.4 homeserver.yaml – App Services eintragen
Am Ende der homeserver.yaml ergänzen:
app_service_config_files:
- /data/bridges/telegram-registration.yaml
- /data/bridges/discord-registration.yaml
- /data/bridges/whatsapp-registration.yaml
Warning
Synapse muss nach jeder Änderung an
app_service_config_filesneu gestartet werden.
17. Telegram Bridge
17.1 API-Credentials besorgen
https://my.telegram.org/appsöffnen- API development tools → Neue Anwendung erstellen
api_idundapi_hashnotieren
17.2 matrix/bridges/telegram/config.yaml
homeserver:
address: http://matrix_synapse:8008 # Container-Name funktioniert (gleicher Host!)
domain: deine-domain.de
appservice:
address: http://bridge_telegram:29317
database: postgres://tg_bridge:PASSWORT@bridge_telegram_db:5432/tg_bridge
bridge:
permissions:
"*": relay
"deine-domain.de": user
"@admin:deine-domain.de": admin
telegram:
api_id: 12345678
api_hash: abcdef0123456789
bot_token: disabled
17.3 Starten & Account verknüpfen
docker run --rm \
-v /opt/docker/mcaptcha/matrix/bridges/telegram:/data \
dock.mau.dev/mautrix/telegram:latest
cp matrix/bridges/telegram/registration.yaml \
matrix/synapse_data/bridges/telegram-registration.yaml
docker compose -f matrix/docker-compose-matrix.yml restart synapse
docker compose -f matrix/bridges/docker-compose-bridges.yml \
up -d bridge_telegram bridge_telegram_db
DM an @telegrambot:deine-domain.de in Element:
login → Login starten
sync → Chats synchronisieren
help → Alle Befehle
18. Discord Bridge
18.1 matrix/bridges/discord/config.yaml
homeserver:
address: http://matrix_synapse:8008
domain: deine-domain.de
public_address: https://matrix.deine-domain.de
appservice:
address: http://bridge_discord:29334
database:
type: postgres
uri: postgres://dc_bridge:PASSWORT@bridge_discord_db:5432/dc_bridge
bridge:
permissions:
"*": relay
"deine-domain.de": user
"@admin:deine-domain.de": admin
bridge_reactions: true
bridge_replies: true
encryption:
allow: true
default: false
18.2 Starten & Account verknüpfen
docker run --rm \
-v /opt/docker/mcaptcha/matrix/bridges/discord:/data \
dock.mau.dev/mautrix/discord:latest
cp matrix/bridges/discord/registration.yaml \
matrix/synapse_data/bridges/discord-registration.yaml
docker compose -f matrix/docker-compose-matrix.yml restart synapse
docker compose -f matrix/bridges/docker-compose-bridges.yml \
up -d bridge_discord bridge_discord_db
DM an @discordbot:deine-domain.de:
login-token → Login mit Discord-Token
guilds status → Verbundene Server anzeigen
guild bridge <ID> → Server bridgen
Discord-Token extrahieren:
- Discord im Browser öffnen (nicht Desktop-App)
- F12 → Netzwerk-Tab
- Beliebige Aktion ausführen
- In Request-Headern nach
Authorizationsuchen
Warning
User-Token-Login verstößt formal gegen Discords ToS. Für produktive Umgebungen einen eigenen Discord-Bot mit Bot-Token verwenden.
19. WhatsApp Bridge
Nutzt WhatsApps offizielle Multi-Device API – kein Root nötig, Handy muss nicht dauerhaft online sein.
19.1 matrix/bridges/whatsapp/config.yaml
homeserver:
address: http://matrix_synapse:8008
domain: deine-domain.de
public_address: https://matrix.deine-domain.de
appservice:
address: http://bridge_whatsapp:29318
database:
type: postgres
uri: postgres://wa_bridge:PASSWORT@bridge_whatsapp_db:5432/wa_bridge
bridge:
permissions:
"*": relay
"deine-domain.de": user
"@admin:deine-domain.de": admin
encryption:
allow: true
default: true # E2E empfohlen!
history_sync:
backfill: true
max_initial_conversations: 20
create_portals: true
19.2 Starten & Account verknüpfen
docker run --rm \
-v /opt/docker/mcaptcha/matrix/bridges/whatsapp:/data \
dock.mau.dev/mautrix/whatsapp:latest
cp matrix/bridges/whatsapp/registration.yaml \
matrix/synapse_data/bridges/whatsapp-registration.yaml
docker compose -f matrix/docker-compose-matrix.yml restart synapse
docker compose -f matrix/bridges/docker-compose-bridges.yml \
up -d bridge_whatsapp bridge_whatsapp_db
DM an @whatsappbot:deine-domain.de:
login → QR-Code erhalten
WhatsApp → Einstellungen → Verknüpfte Geräte → QR scannen
sync → Chats synchronisieren
pm +49123456789 → Neuen Chat öffnen
help → Alle Befehle
20. Bridges Wartung & Fehlerbehebung
Alle Container auf einmal starten
cd /opt/docker/mcaptcha
docker compose \
-f docker-compose.yml \
-f matrix/docker-compose-matrix.yml \
-f matrix/bridges/docker-compose-bridges.yml \
up -d
Updates
docker compose -f matrix/bridges/docker-compose-bridges.yml pull
docker compose -f matrix/bridges/docker-compose-bridges.yml up -d
Logs
| Befehl | Beschreibung |
|---|---|
docker compose logs -f bridge_telegram |
Telegram Bridge |
docker compose logs -f bridge_discord |
Discord Bridge |
docker compose logs -f bridge_whatsapp |
WhatsApp Bridge |
docker stats |
Ressourcenverbrauch aller Container |
Fehlerbehebung
502 Bad Gateway im NPM
# Vom Proxy-Server (192.168.1.10) testen:
curl -v http://192.168.1.20:8008/_matrix/client/versions
# Firewall auf dem App-Server prüfen:
sudo ufw status verbose
ss -tlnp | grep -E "7000|8008|8080|8090"
Bridge antwortet nicht in Element
registration.yamlkorrekt nachsynapse_data/bridges/kopiert?- Synapse danach neu gestartet?
- Interne Konnektivität testen:
docker exec matrix_bridge_telegram \
curl -s http://matrix_synapse:8008/_matrix/client/versions
Telegram: 401 Unauthorized
api_id/api_hashkorrekt? → Im Bridge-Chatloginerneut
Discord: Bridge trennt Verbindung
- Token nach Passwortwechsel zurückgesetzt → Token neu extrahieren,
login-tokenerneut
WhatsApp: QR abgelaufen oder Session verloren
- QR-Codes sind nur 60 Sekunden gültig →
reconnectim Element-Chat - Session komplett verloren: WhatsApp → Verknüpfte Geräte → Gerät entfernen →
login
Medien werden nicht übertragen
max_upload_size: 50Minhomeserver.yamlgesetzt?- NPM Advanced Tab:
client_max_body_size 100M; - Speicherplatz prüfen:
df -h
.gitignore
# Secrets niemals committen!
.env
# Synapse-Laufzeitdaten
matrix/synapse_data/
# Auto-generierte Bridge-Registrierungen (enthalten Tokens!)
matrix/bridges/telegram/registration.yaml
matrix/bridges/discord/registration.yaml
matrix/bridges/whatsapp/registration.yaml
# Signing Keys
*.signing.key
Ressourcen
| Projekt | Dokumentation |
|---|---|
| mCaptcha | https://mcaptcha.org/docs |
| Matrix Synapse | https://matrix-org.github.io/synapse/latest/ |
| Element Web | https://github.com/element-hq/element-web |
| mautrix Bridges | https://docs.mau.fi/bridges/ |
| mautrix-telegram | https://docs.mau.fi/bridges/python/telegram/ |
| mautrix-discord | https://docs.mau.fi/bridges/go/discord/ |
| mautrix-whatsapp | https://docs.mau.fi/bridges/go/whatsapp/ |
| Nginx Proxy Manager | https://nginxproxymanager.com/guide/ |