Version: 25.4.17
In dem folgenden Tutorial wollen wir Matrix Synapse inklusive SSL Zertifikate von Letsencrypt mit dem Nginx Proxy Manager auf einem VPS im Internet (in unserem Fall bei Hetzner.com) installieren.
Ein großes Dankeschön geht an Patrick von https://www.cleveradmin.de/ mit dem ich zusammen eine tolle Lösung erarbeitet habe!!
🛠 Voraussetzungen:
– VPS ist eingerichtet mit root Zugang (Ubuntu)
– Eine Domain ist vorhanden mit Zugriff auf die DNS Einträge
Hier reicht ein kleiner Server von z.B. Hetzner.com aus:

⚠️ Firewall: Bitte die Firewall bei Hetzner konfigurieren und nur folgende Ports zulassen:

TCP: 22, 80, 81, 443 (Matrix Synapse, NPM)
TCP: 7881-7882 und UDP 50100 – 50200 sind die Vorbereitungen für Element Call
⚠️ Der DNS Zugriff muss ebenfalls gegeben sein, bei einer vorhandenen Domain:

Wir starten und loggen uns auf dem Server ein:
ssh root@SERVER-IP
Zuerst sorgen wir dafür das der Server auf dem neusten Stand ist mit folgendem Befehl:
apt-get update && apt-get upgrade && apt-get autoremove
Danach starten wir den Server neu und loggen uns dann erneut ein:
sudo reboot now
Wir erstellen die Ordner für unsere beiden Projekte (Synapse und Nginx Proxy Manager -NPM-)
mkdir -p /home/npm
mkdir -p /home/synapse/data
Jetzt installieren wir erst einmal Docker indem wir im /home/ Verzeichnis eine Datei Namens „install.sh“ erstellen:
nano /home/install.sh
In die Datei kommt folgender Inhalt rein (Danke an Patrick Asmus: https://git.techniverse.net/scriptos/public-linux-docker-installer)
#!/bin/bash
# Script Name: docker-installer.v3.sh
# Beschreibung: Docker & Docker-Compose Installer für Ubuntu 22.04 Jammy und Ubuntu 24.04
# Aufruf: bash ./docker-installer.v3.sh
# Autor: Patrick Asmus
# Web: https://www.techniverse.net
# Git-Reposit.: https://git.techniverse.net/scriptos/linux-docker-installer
# Version: 3.4.5
# Datum: 15.11.2024
# Modifikation: change docker-compose version
#####################################################
# Variablen:
COMPOSEVERSION="v2.30.3"
# Betriebssystem und Version prüfen
OS=$(lsb_release -is)
VERSION=$(lsb_release -rs)
if [ "$OS" != "Ubuntu" ] || { [ "$VERSION" != "22.04" ] && [ "$VERSION" != "24.04" ]; }; then
echo "Dieses Script unterstützt nur Ubuntu 22.04 und Ubuntu 24.04. Installation abgebrochen."
exit 1
fi
# Variablen
USER="root"
DOCKER_ROOT_DIR="/var/docker-bin"
COMPOSE_DIR="/home/docker-container"
# Docker installieren
sudo apt update
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common
if [ "$VERSION" == "22.04" ]; then
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
elif [ "$VERSION" == "24.04" ]; then
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
fi
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io
sudo usermod -aG docker $USER
# Docker-Root-Verzeichnis ändern & Compose Verzeichnis erstellen
sudo systemctl stop docker
sudo mkdir -p $DOCKER_ROOT_DIR
echo "{\"data-root\": \"$DOCKER_ROOT_DIR\"}" | sudo tee /etc/docker/daemon.json > /dev/null
cp /var/lib/docker/* $DOCKER_ROOT_DIR -r
rm -R /var/lib/docker
sudo systemctl start docker
mkdir -p $COMPOSE_DIR
# Docker-compose installieren (gleicher Prozess für beide Versionen)
sudo apt install -y curl
sudo curl -L "https://github.com/docker/compose/releases/download/$COMPOSEVERSION/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# Optional: Plugin für Oh my ZSH aktivieren
echo "OhMyZSH Plugin für Docker hinzufügen"
sudo sed -i 's/plugins=(git)/plugins=(git docker)/g' /root/.zshrc
# Überprüfen der Installation
docker --version
docker-compose --version
exit 0
Anschließend führen wir die Datei aus:
cd /home/
bash install.sh
Nach Abschluss des Skripts ist Docker und Docker-Compose installiert.
⚠️ Wichtig: Jetzt legen wir ein spezielles Netzwerk an, in welchem die einzelnen Container später gemeinsam tätig sind:
docker network create --subnet=172.37.51.0/24 matrixnetwork
Wir fangen mit Matrix Synapse an und gehen dazu in den synapse Ordner und führen folgenden Befehl aus:
cd /home/synapse/
docker run --rm \
-v /home/synapse/data:/data \
-e SYNAPSE_CONFIG_PATH=/data/homeserver.yaml \
-e SYNAPSE_SERVER_NAME=subdomain.deinedomain.com \
-e SYNAPSE_REPORT_STATS=no \
matrixdotorg/synapse:latest generate
⚠️ „subdomain.deinedomain.de“ wird natürlich vorher mit der eigenen Domain abgeändert. z.b: matrix.deinedomain.de
Das Skript hat jetzt die sogenannte homeserver.yaml Datei im Ordner /synapse/data/ angelegt.
Diese öffnen wir mit:
nano /home/synapse/data/homeserver.yaml
Die Datei sollte wie folgt aussehen:
server_name: "matrix.deinedomain.com"
pid_file: /data/homeserver.pid
listeners:
- port: 8008
tls: false
type: http
x_forwarded: true
resources:
- names: [client, federation]
compress: false
#database:
# name: sqlite3
#args:
# database: /data/homeserver.db
# Datenbank Einstellungen
database:
name: psycopg2
args:
user: synapseuser
password: 7Kn33B655TgV339-777bN
database: synapsedb
host: synapse-db
port: 5432
cp_min: 5
cp_max: 10
log_config: "/data/matrix.deinedomain.com.log.config"
media_store_path: /data/media_store
registration_shared_secret: "N42a1.-58.nB,dkym:N4Jb=b7L+b@cW589HcF&4if~A0iD8e*FQ"
enable_registration: false
report_stats: false
macaroon_secret_key: "XHfX:isQQ44BNbpXcU44pKe55Q+IOH9dfH5SVYAykjnONFzEmp"
form_secret: "&Ld^9od94fYE=mDb,Q8RYaGuJH;##KCCB:AIq3KAL+X23dxE3"
signing_key_path: "/data/matrix.domain.com.signing.key"
trusted_key_servers:
- server_name: "matrix.org"
experimental_features:
msc3266_enabled: true
msc4222_enabled: true
msc4140_enabled: true
max_event_delay_duration: 24h
rc_message:
per_second: 0.5
burst_count: 30
rc_delayed_event_mgmt:
per_second: 1
burst_count: 20
# Alle Medien löschen die älter als 120 Tage sind, spart Speicherplatz auf dem Server !!
media_retention:
local_media_lifetime: 120d
remote_media_lifetime: 120d
#Upload Größe von Dateien auf max. 250 MB setzen
max_upload_size: 250M
# Aktiviert die Bereitstellung der .well-known URL zur Weiterleitung föderativer Kommunikation an Port 443 - wird später für Element Call benötigt !!
#serve_server_wellknown: true
# vim:ft=yaml
⚠️ Wichtig ist die Datenbankeinstellungen noch wie folgt abzuändern:
#database:
# name: sqlite3
#args:
# database: /data/homeserver.db
# Datenbank Einstellungen
database:
name: psycopg2
args:
user: synapseuser
password: 7Kn33B655TgV339-777bN
database: synapsedb
host: synapse-db
port: 5432
cp_min: 5
cp_max: 10
Dabei wird die SQL3 Datenbank deaktiviert und Postgres aktiviert.
Wir speichern die Datei (STRG+O) und schließen den Editor wieder (STRG+X) und legen nun die „docker-compose.yml“ Datei im Ordner Synapse an:
nano /home/synapse/docker-compose.yml
Dabei könnt ihr die folgende Compose-Datei verwenden und ändert diese mit euren Daten ab (Passwort für Postgres noch abändern):
version: "3.9"
services:
synapse-db:
image: postgres:17.4
networks:
matrixnetwork:
ipv4_address: 172.37.51.12
container_name: Synapse-DB
hostname: synapse-db
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD", "pg_isready", "-q", "-d", "synapsedb", "-U", "synapseuser"]
timeout: 45s
interval: 10s
retries: 10
volumes:
- /home/synapse/db:/var/lib/postgresql/data
environment:
- POSTGRES_DB=synapsedb
- POSTGRES_USER=synapseuser
- POSTGRES_PASSWORD=7Kn33B655TgV339-777bN
- POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
restart: always
synapse:
image: matrixdotorg/synapse:latest
networks:
matrixnetwork:
ipv4_address: 172.37.51.13
container_name: Synapse
hostname: synapse
security_opt:
- no-new-privileges:true
environment:
- TZ=Europe/Berlin
- SYNAPSE_CONFIG_PATH=/data/homeserver.yaml
volumes:
- /home/synapse/data:/data
ports:
- 8008:8008
restart: always
depends_on:
synapse-db:
condition: service_started
networks:
matrixnetwork:
external: true
Nun schauen wir, dass wir wieder in das Projektverzeichnis gehen und den Container starten:
cd /home/synapse
docker compose up -d
Das Ergebnis können wir mit „docker ps“ in der Kommandozeile abfragen. Wir sollten sehen, dass die Container (Synapse und die Datenbank) als „Healthy“ läuft:

Den ersten Benutzer (Admin) legen wir mit folgendem Befehl an:
⚠️ Wichtig: wieder in das /home/synapse/ Verzeichnis wechseln!
docker compose exec synapse register_new_matrix_user -u benutzername -p passwort -a -k "geheimes-passwort" http://localhost:8008
Erklärung:
-u
: gewünschter Benutzername-p
: Passwort-a
: macht den Benutzer zum Admin (optional)-k
: shared secret aushomeserver.yaml
http://localhost:8008
: Synapse Admin-API-Endpunkt (achte auf korrekte Adresse, evtl.http://synapse:8008
innerhalb von Compose)
✅ Fertig: Teil 1 ist erledigt. Matrix Synapse ist installiert. Herzlichen Glückwunsch ! Überprüft werden kann dies mit aufruf der URL:
http://server-ip:8008

Nächster Schritt: NPM – Nginx Proxy Manager als Docker installieren (SSL Zertifikat / HTTPS)
nano /home/npm/docker-compose.yml
In die Datei fügen wir folgenden Code ein:
version: '3'
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
ports:
- '80:80'
- '81:81'
- '443:443'
networks:
matrixnetwork:
ipv4_address: 172.37.51.10
environment:
DB_MYSQL_HOST: "db"
DB_MYSQL_PORT: 3306
DB_MYSQL_USER: "npm"
DB_MYSQL_PASSWORD: "npm"
DB_MYSQL_NAME: "npm"
restart: always
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
db:
image: 'jc21/mariadb-aria:latest'
networks:
matrixnetwork:
ipv4_address: 172.37.51.11
environment:
MYSQL_ROOT_PASSWORD: 'npm'
MYSQL_DATABASE: 'npm'
MYSQL_USER: 'npm'
MYSQL_PASSWORD: 'npm'
restart: always
volumes:
- ./mysql:/var/lib/mysql
networks:
matrixnetwork:
external: true
Nun sorgen wir dafür, dass neben dem NPM auch immer die Datenbank beim Systemstart mitgestartet wird. In der docker-compose.yml klappt das nämlich nicht immer und man kann sich dann nicht bei NPM einloggen auf dem Interface.
🛠 Die Lösung ist ein: „systemd-Service“
Service-Datei erstellen (z. B. /etc/systemd/system/npm-stack.service
):
[Unit]
Description=Docker Compose Nginx Proxy Manager Stack
Requires=docker.service
After=docker.service
[Service]
WorkingDirectory=/pfad/zum/docker-compose-verzeichnis
ExecStart=/usr/local/bin/docker-compose up -d
Restart=always
TimeoutStartSec=0
[Install]
WantedBy=multi-user.target
⚠️ Passe den Pfad bei WorkingDirectory
an den Ordner an, in dem die docker-compose.yml
liegt! (/home/npm)
Aktivieren und starten:
sudo systemctl daemon-reexec
sudo systemctl enable npm-stack
sudo systemctl start npm-stack
✅ Ergebnis
Jetzt wird der komplette NPM Stack – inklusive mariadb-aria
– beim Booten des Systems automatisch gestartet, ohne dass manuell docker compose up
ausgeführt werden muss.
Wir starten den NPM container indem wir in das Verzeichnis /home/npm/ gehen und dort den Befehl ausführen:
docker compose up -d
Nun öffnen wir die Admin-Oberflöche von NPM mittels: http://IP-ADRESSE:81 und bekommen folgendes Bild:

Wir loggen uns ein mit:
– admin@example.com
– changeme
Und können nun unsere eigenen Userdaten vergeben.
Wichtig ist jetzt, dass wir verschiedene Subdomains anlegen:
– npm.domain.de
– matrix.domain.de
Die Subdomains verweisen dabei in den DNS Einstellungen von Hetzner auf den VPS auf dem das alles installiert ist. Nachdem wir das gemacht haben, geht es beim NPM weiter:

Im NPM klicken wir jetzt auf „Hosts“ -> „Proxy Hosts“ -> „Add Proxy Host“ und fügen folgendes ein:

⚠️ Wichtig: Es werden zur Weiterleitung die internen Docker-IPs der Container verwendet. Das verhindert spätere Fehler. Dafür haben wir auch ganz am Anfang des Tutorials das spezielle Docker-Netzwerk eingerichtet. Alle Container sind im gleichen Netzwerk.
Weiter gehts: Dann auf den Reiter SSL klicken:

Nun auf Save klicken und ihr habt einen „Reverse Proxy“ eingerichtet. Eure Matrix Server ist nun per https:// erreichbar unter „matrix.domainname.de“.
Dasselbe macht ihr noch mit dem NPM, damit auch dieser über SSL abgesichert ist:

Und auch wieder unter SSL das SSL Zertifikat anfordern.
Im NPM unter „SSL Certificates“ können nun die SSL Zertifikate eingesehen werden:

✅ Fertig! Matrix Synapse mit NPM (beides im Docker Container) wurde auf einem VPS im Internet installiert.
Wer jetzt wissen möchte wie man Element Call installiert, der schaut sich das Tutorial von Patrick an:
https://www.cleveradmin.de/blog/2025/04/matrixrtc-element-call-backend-einrichten/

bc1q8dxp9mlt3mkvaklu2vn8j6jteqyv53kschc90v

Lightning: tom@blitz.cmdsrv.de
