Relayium

Relayium selbst hosten: eigener Datei- und Textübertragungs-Server

Zuletzt aktualisiert: 2026-08-06

Relayium ist AGPL-3.0-lizenziert und Open Source, und der Server ist ein einziges eigenständiges Image — keine externe Datenbank, kein Storage-Bucket eines Drittanbieters, nichts, wofür man sich anmelden müsste. Wenn du lieber alles selbst betreibst, statt dich auf relayium.com zu verlassen, bringt dich diese Anleitung mit Docker zu einem laufenden Server und richtet die CLI darauf aus.

Selbst hosten heißt, volle Kontrolle darüber zu haben, wo deine Daten liegen, eine eigene Domain und ein eigenes TLS-Zertifikat zu nutzen, und von den Betriebsentscheidungen anderer unabhängig zu sein. Alles Folgende stützt sich auf Dateien, die tatsächlich im Repository liegen — docker-compose.yml, server/.env.example und docs/self-hosting.md —, sodass hier kein Flag und keine Einstellung erwähnt wird, die es nicht wirklich gibt.

Warum selbst hosten

Relayiums Echtzeit-Übertragungen sind Ende-zu-Ende verschlüsselt. Ein selbst gehostetes TURN-Relay kann verschlüsselte Bytes transportieren und der Server verarbeitet Signalisierungsmetadaten, doch beide können Dateiklartext weder lesen noch entschlüsseln; weder Server noch Relay speichern eine serverseitige Kopie oder Historie von Echtzeitinhalten. Der Server hält aber dein Konto und — bei gespeicherten/link-basierten Übertragungen — verschlüsselte Blobs sowie eine kleine SQLite-Datenbank. Selbst hosten bedeutet, dass diese Daten auf Infrastruktur liegen, die du kontrollierst, unter deiner eigenen Domain, ohne dass fremde Betriebsentscheidungen mitspielen.

Da das Projekt AGPL-3.0-lizenziert und Open Source ist (github.com/relayium/relayium), kannst du genau nachlesen, was der Server tut, bevor du ihm irgendetwas anvertraust, und ihn frei forken oder verändern.

Schnellstart mit Docker

Was du vor Schritt 1 brauchst

  • Einen Host mit Docker Engine und dem Compose-Plugin. docker compose version gibt eine Versionszeile aus; "docker: 'compose' is not a docker command" heißt, dass das Plugin fehlt.
  • Einen Klon des Repositorys. Die Compose-Datei baut das Image aus genau diesem Quellbaum, braucht also das Dockerfile und web/ daneben — es gibt kein fertiges Image zum Ziehen.
  • Platz für das benannte Volume relayium-data: die SQLite-Datenbank plus den Chiffretext gespeicherter Übertragungen, den du aufbewahrst.
  • Eine Domain und einen TLS-terminierenden Reverse-Proxy, falls außer dir noch jemand den Server nutzt. Der Container spricht nur unverschlüsseltes HTTP und veröffentlicht standardmäßig ausschließlich auf dem Loopback-Interface.
  • Sonst nichts. Keine externe Datenbank, kein Object-Storage-Bucket, kein Konto bei Dritten.

Im Repository-Root liegen ein Dockerfile und ein docker-compose.yml, die ein einziges eigenständiges Image bauen — ein statisches Go-Binary, das die vorgebaute Web-App ausliefert, sodass für den reinen Betrieb weder ein separates Node, eine Go-Toolchain noch nginx nötig ist.

  1. Klone das Repository und wechsle hinein.

    git clone https://github.com/relayium/relayium.git
    cd relayium
  2. Bauen und starten. Das Platzhalter-Secret ist auch dann Pflicht, wenn das Relay aus ist: Compose prüft beim Parsen die Pflichtvariable des per Profil deaktivierten coturn-Dienstes mit, sodass ein blankes docker compose up die Arbeit verweigert.

    RELAYIUM_TURN_SECRET=placeholder docker compose up -d --build
  3. Prüfe, ob der Container oben geblieben ist und nicht in einer Neustartschleife hängt.

    docker compose ps
  4. Frag die Instanz, ob sie tatsächlich ausliefern kann. Nimm /readyz, nicht /healthz — der Unterschied ist der ganze Sinn dieser Prüfung, und der Erwartungskasten unten sagt, warum.

    curl -s http://127.0.0.1:8080/readyz
  5. Kopiere die Konfigurationsvorlage und trag deine öffentliche URL ein. RELAYIUM_BASE_URL baut die Links in ausgehenden E-Mails und entscheidet, ob Session-Cookies das Secure-Flag bekommen — es muss also deine echte https://-Adresse sein.

    cp server/.env.example server/.env
    chmod 600 server/.env
  6. Setz nginx oder Caddy davor, das TLS für deine Domain terminiert und alles — /, /api, /ws, /admin — an Port 8080 weiterreicht. Danach neu starten, damit server/.env greift.

    docker compose up -d

So sieht eine funktionierende Instanz aus

Der Container meldet Up, und beide Endpunkte antworten. Entscheidend ist ready: /healthz liefert bedingungslos ok, noch bevor irgendetwas geöffnet wurde, und geht deshalb auch auf einer Instanz durch, deren Datenbank oder Blob-Verzeichnis unbrauchbar ist. /readyz pingt die SQLite-Datenbank und das Blob-Verzeichnis an und antwortet mit 503, sobald eines von beiden kaputt ist.

$ docker compose ps
NAME                IMAGE                     STATUS          PORTS
relayium-server-1   relayium/relayium:local   Up 12 seconds   127.0.0.1:8080->8080/tcp

$ curl -s http://127.0.0.1:8080/healthz
ok
$ curl -s http://127.0.0.1:8080/readyz
ready

Ein TURN-Relay für netzwerkübergreifende Übertragungen hinzufügen

Übertragungen im selben Netzwerk (LAN) und SSH-basiertes push/pull funktionieren ohne irgendetwas Zusätzliches. Netzwerkübergreifende Echtzeit-Übertragungen (zwei Geräte hinter unterschiedlichen NATs) brauchen manchmal ein TURN-Relay, um einen Pfad herzustellen — das Relay sieht dabei stets nur Chiffretext, niemals deinen Dateiinhalt.

docker-compose.yml hat ein optionales relay-Profil, das zusammen mit dem Hauptserver coturn (den TURN-Server) und eine kleine Redis-Instanz für die Zählung der Relay-Bytes startet.

Das Secret muss an zwei verschiedene Stellen gelangen, und wenn das schiefgeht, scheitert es lautlos. coturn bekommt es über die Variablenersetzung von Compose, die nur aus der Shell oder einer .env im Projektwurzelverzeichnis aufgelöst wird. Der Server liest es aus seiner eigenen Umgebung — also aus server/.env — und ein leeres Secret schaltet TURN vollständig ab. Setzt du nur eines von beiden, läuft ein coturn, für das der Server nie Zugangsdaten ausstellt: Jeder Container meldet sich gesund, im Log steht nichts, und Übertragungen durch strenge NATs scheitern weiter genau wie vorher.

  1. Erzeuge ein einziges langes Zufalls-Secret. Überall unten kommt derselbe Wert hin.

    openssl rand -hex 32
  2. Trag dieses Secret und die Relay-Adressen, auf die deine Domain zeigt, in server/.env ein — erst damit schaltet der Server TURN überhaupt ein.

    RELAYIUM_TURN_SECRET=<the value from step 1>
    RELAYIUM_TURN_URLS=turn:example.com:3478,turns:example.com:5349
  3. Exportiere dieselbe Datei in die Shell, damit die Ersetzung von Compose coturn dasselbe Secret übergeben kann. Sourcen hält die Quelle einzig und hält das Secret von der Kommandozeile fern, wo ps es sichtbar machen würde.

    set -a; . ./server/.env; set +a
  4. Starte den Stack mit dem relay-Profil.

    docker compose --profile relay up -d --build
  5. Öffne die Relay-Ports in der Firewall des Hosts. coturn läuft im Host-Netzwerk, das sind also Host-Regeln und keine Docker-Regeln: UDP 3478 und 49152-65535, TCP 3478 und 5349.

  6. Prüfe, ob der Server — nicht nur coturn — mit dem Secret hochgekommen ist. Genau diese Prüfung fängt den lautlosen Fall ab.

    docker compose exec server env | grep RELAYIUM_TURN

So sieht ein funktionierendes Relay aus

Beide Schlüssel kommen aus dem Inneren des Server-Containers nicht leer zurück. Dass coturn läuft, sagt für sich genommen nichts — der Browser bekommt ausschließlich Relay-Zugangsdaten, die der Server ausgestellt hat.

$ docker compose exec server env | grep RELAYIUM_TURN
RELAYIUM_TURN_SECRET=3f7a…
RELAYIUM_TURN_URLS=turn:example.com:3478,turns:example.com:5349

Installiere die CLI auf deinem Rechner

Dieser letzte Schritt führt die relayium-CLI auf deinem eigenen Rechner aus (nicht auf dem Server), installiere sie dort also, falls noch nicht geschehen. Unter macOS oder Linux:

curl -fsSL https://relayium.com/install.sh | sh

Die CLI auf deinen Server ausrichten

Die Relayium CLI verwendet für netzwerkübergreifendes send/receive und text standardmäßig den Rendezvous-Server auf relayium.com. Übergib --server, um stattdessen deinen eigenen zu nutzen.

  1. Melde dich auf einem Rechner, der die CLI aus dem Abschnitt oben schon hat, an deinem eigenen Server an statt an relayium.com. Es erscheinen eine URL und ein Code; bestätige ihn in einem Browser, der an deiner Instanz angemeldet ist.

    relayium login --server https://your-domain
  2. Prüfe, an welchen Server die gespeicherten Zugangsdaten gebunden sind. whoami nimmt keine Flags — es meldet, was die Anmeldung tatsächlich geschrieben hat, und genau das macht den Aufruf wertvoll.

    relayium whoami
  3. Gib beim Senden dasselbe --server mit. Ohne es prägt die CLI den Pairing-Code auf relayium.com, und die Gegenseite findet ihn auf deiner Instanz nie.

    relayium send ./report.pdf --server https://your-domain
  4. Empfange auf dem anderen Rechner mit dem ausgegebenen Code und demselben --server. Textsitzungen laufen genauso.

    relayium receive 483920 --server https://your-domain
    relayium text --server https://your-domain
    relayium text 483920 --server https://your-domain

Woran du merkst, dass sie mit deiner Instanz spricht

whoami gibt das Konto aus und dahinter in Klammern den Server, an den es gebunden ist. Steht dort deine eigene Domain und nicht relayium.com, ist es bestätigt.

$ relayium login --server https://your-domain
Open https://your-domain/device and enter code: WDJB-MJHT
logged in as you@example.com

$ relayium whoami
you@example.com (https://your-domain)

Wenn es nicht funktioniert

Fünf Fehler decken fast jedes gescheiterte Selbst-Hosting ab. Zu jedem gibt es eine Zeile zum Lesen oder einen Befehl zum Ausführen, der die Sache entscheidet — und drei der fünf sehen wie Erfolg aus, bis du die Prüfung wirklich laufen lässt.

Symptom, Prüfung, Lösung

docker compose up verweigert den Start komplett, bevor überhaupt etwas gebaut wird.
docker compose up -d --build
# required variable RELAYIUM_TURN_SECRET is missing a value

Compose ersetzt die Variablen der ganzen Datei, bevor es nach Profilen filtert — die Pflichtvariable des abgeschalteten coturn-Dienstes wird also auch dann geprüft, wenn das Relay aus ist. Stell irgendeinen Platzhalter voran — RELAYIUM_TURN_SECRET=placeholder docker compose up -d --build — und ersetze ihn erst durch ein echtes Secret, wenn du das relay-Profil tatsächlich einschaltest.

Der Container ist Up, aber ein Browser auf einem anderen Rechner erreicht ihn nicht.
docker compose ps
# PORTS  127.0.0.1:8080->8080/tcp

Diese Bindung ans Loopback ist die Voreinstellung, damit ein öffentlicher Host kein unverschlüsseltes HTTP ins Internet stellt. Lass sie im Produktivbetrieb so und terminiere TLS in einem Reverse-Proxy auf demselben Host. Für eine reine LAN-Kiste ohne Proxy veröffentlichst du weiter mit RELAYIUM_BIND=0.0.0.0 docker compose up -d — diese Variable liest compose, nicht der Server.

/healthz sagt ok, aber die Registrierung schlägt fehl und gespeicherte Links tauchen nie auf.
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/readyz
# 503

/healthz antwortet bedingungslos mit ok und belegt nur, dass der Prozess lauscht. /readyz pingt die SQLite-Datenbank und das Blob-Verzeichnis an, eine 503 heißt also, dass eines von beiden unbrauchbar ist — prüfe, ob das Volume relayium-data gemountet ist und ob RELAYIUM_DB und RELAYIUM_BLOB_DIR hineinzeigen.

relayium login gibt eine Bestätigungs-URL auf localhost aus, die du nicht öffnen kannst.
relayium login --server https://your-domain
# Open http://localhost:8080/device and enter code: WDJB-MJHT

Der Server baut diese URL aus RELAYIUM_BASE_URL, dessen Vorgabe http://localhost:8080 ist. Trag in server/.env deine echte https://-Adresse ein und starte neu. Der Wert entscheidet außerdem, ob Session-Cookies das Secure-Flag bekommen — ihn falsch zu lassen ist also nicht nur ein Schönheitsfehler.

coturn läuft, aber netzwerkübergreifende Übertragungen durch strenge NATs scheitern weiter — und nichts wird geloggt.
docker compose exec server env | grep RELAYIUM_TURN
# keine Ausgabe

Das Secret hat coturn über die Compose-Ersetzung erreicht, den Server aber nie, und dessen leeres Secret schaltet TURN vollständig ab. Trag RELAYIUM_TURN_SECRET und RELAYIUM_TURN_URLS in server/.env ein, source die Datei mit set -a; . ./server/.env; set +a, damit die Ersetzung denselben Wert sieht, und starte das relay-Profil neu. Beide Schlüssel müssen bei dieser Prüfung nicht leer zurückkommen.

Häufige Fragen

Muss ich TURN einrichten?

Nur, wenn netzwerkübergreifende Echtzeit-Übertragungen auch durch strenge NATs funktionieren sollen. Übertragungen im selben Netzwerk, SSH-basiertes push/pull und daemon-direct funktionieren alle ohne TURN — TURN dient ausschließlich der NAT-Traversal auf dem netzwerkübergreifenden Pairing-Code-Pfad.

Ist die CLI immer noch kostenlos, wenn ich selbst hoste?

Ja. Die CLI bleibt mit relayium.com wie mit deinem eigenen Server kostenlos. Ein Konto auf dem Zielserver brauchen send oder text ohne Code zum Erzeugen eines Codes sowie up zum Speichern einer Datei. receive, down und text mit dem ausgegebenen Code brauchen keine Anmeldung.

Kann ich meine eigene Domain und ein eigenes TLS-Zertifikat verwenden?

Ja. Das Docker-Image lauscht selbst auf reinem HTTP auf :8080; setze nginx oder Caddy mit deiner eigenen Domain und deinem Zertifikat davor (z. B. über certbot/Let's Encrypt). docs/self-hosting.md beschreibt, was zu proxyen ist; Relayiums eigene Produktions-nginx-Konfiguration ist nicht veröffentlicht, du musst also deine eigene schreiben.

Welche Daten speichert mein selbst gehosteter Server?

Eine SQLite-Datenbank (Konten, Sessions) unter RELAYIUM_DB und, bei gespeicherten/link-basierten Übertragungen, verschlüsselte Blobs unter RELAYIUM_BLOB_DIR, die der Server selbst nicht entschlüsseln kann. Der Server speichert keine Echtzeit-Dateien oder Nachrichteninhalte und leitet nur den Signaling-Handshake weiter; Empfangsgeräte können Dateien oder Text dennoch behalten.

Installiere die kostenlose Relayium CLI und richte sie mit --server auf deinen eigenen Server aus.

CLI holen

Weiterlesen