PairDrop selbst hosten: Die AirDrop-Alternative für alle Geräte
AirDrop ist praktisch – aber nur, wenn alle Geräte im Apple-Universum hängen. PairDrop schickt Dateien peer-to-peer direkt zwischen Browser-Geräten: egal ob Linux-Desktop, Windows-Laptop, Android oder iPhone. Kein Upload in eine Cloud, keine Konten, keine Größenlimits. In diesem Tutorial hostest du dir deine eigene Instanz mit Docker Compose – inklusive Reverse Proxy, Fallback-Optionen und der wichtigsten Privatsphäre-Stellschrauben.
Warum PairDrop?
Dateien zwischen Rechner und Handy zu schicken, ist ohne Apple-Ökosystem oft eine Qual: USB-Kabel, Messenger mit Kompression oder Cloud-Zwischenstopps. PairDrop löst das mit einem Browser-Tab pro Gerät – die Dateien wandern direkt von A nach B, in Originalqualität, ohne Umweg über deinen Server. Als Fork von Snapdrop bringt es dazu erweiterte Funktionen wie dauerhaftes Geräte-Pairing, öffentliche Räume und Teilen direkt aus dem Android/iOS-Share-Menü.
So funktioniert der Transfer
- WebRTC: Die eigentlichen Daten fließen verschlüsselt und direkt zwischen den Geräten (P2P). Der Server vermittelt nur die Verbindung (Signaling).
- Geräte-Erkennung: Alle Geräte im selben Netzwerk (oder Raum) sehen sich – mit Namen, Icon und Empfangsbestätigung vor dem Versand.
- Pairing & Räume: Per 6-stelligem Code oder QR dauerhaft gekoppelte Geräte finden sich auch über das Internet; 5-Buchstaben-Räume verbinden temporär fremde Geräte.
- Kein Zwischenspeichern: Ohne WS-Fallback speichert dein Server nichts – es gibt keine Datenbank und kein Volume mit übertragenen Dateien.
Docker Compose – die Datei
Deine Vorlage, aufgeräumt und kommentiert: entfernt wurden der leere networks: {}-Block und das überflüssige network_mode: bridge (Bridge ist ohnehin das Standard-Netz), der Port steht in Anführungszeichen:
services:
pairdrop:
image: lscr.io/linuxserver/pairdrop:latest
container_name: pairdrop
restart: unless-stopped
deploy:
resources:
limits:
cpus: "4.0"
memory: 4G
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Berlin
- WS_FALLBACK=false
- RATE_LIMIT=false
- RTC_CONFIG=
- DEBUG_MODE=false
ports:
- "127.0.0.1:50513:3000" # Web-UI nur lokal – Zugriff über Reverse Proxy
# - ./rtc_config.json:/config/rtc_config.json:ro # nur bei eigenen STUN/TURN-Servern
Der Container ist bewusst zustandslos – übertragene Dateien werden nirgends gespeichert,
ein Volume ist nicht nötig. PUID/PGID greifen erst, wenn du eigene Dateien
(z. B. eine rtc_config.json) einbindest; dann müssen Host-Ordner dem Nutzer 1000 gehören.
50513 ist in unserem Beispiel-Stack bereits an die ROCm-Variante von Ollama vergeben
(127.0.0.1:50513:11434). Läuft beides auf demselben Host, auf 50512/50514 ausweichen.
Umgebungsvariablen im Detail
| Variable | Bedeutung |
|---|---|
PUID / PGID | Nutzer-/Gruppen-ID für gemountete Dateien (per id ermitteln). |
TZ | Zeitzone für Logs, z. B. Europe/Berlin. |
WS_FALLBACK | true erlaubt den Fallback über Websockets, wenn WebRTC (z. B. per VPN) blockiert ist. Achtung: Dann laufen die Daten durch deinen Server statt P2P – sie sind für den Server lesbar und verbrauchen dessen Bandbreite! |
RATE_LIMIT | true begrenzt Clients auf 100 Anfragen pro 5 Minuten – sinnvoll, sobald die Instanz öffentlich erreichbar ist. |
RTC_CONFIG | Pfad zu einer JSON-Datei mit eigenen STUN/TURN-Servern (Pfad im Container). |
DEBUG_MODE | true loggt Client-IPs – nur zur Fehlersuche, nicht in Produktion. |
Eigene STUN/TURN-Server (z. B. Coturn) bindest du so ein:
# rtc_config.json (im Container erreichbar unter /config/)
{
"iceServers": [
{ "urls": "stun:stun.example.org:3478" },
{ "urls": "turn:turn.example.org:3478",
"username": "pairdrop",
"credential": "geheim" }
]
}
Dann in der Compose-Datei das Volume einbinden und RTC_CONFIG=/config/rtc_config.json setzen.
Ohne eigene TURN-Server nutzen Clients öffentliche STUN-Server und scheitern in hartem NAT ggf. am Direktverbindungsaufbau – genau dafür ist WS_FALLBACK oder ein eigener TURN-Server da.
Reverse Proxy & HTTPS
Öffentlich erreichbar sein soll die Instanz nur über HTTPS mit korrekt gesetzten Headern. Der
LinuxServer-README zufolge ist vor allem X-Forwarded-For entscheidend – wird es falsch gesetzt,
„sehen“ sich alle Clients gegenseitig (alle erscheinen unter derselben IP):
send.meine.domain {
encode zstd gzip
reverse_proxy 127.0.0.1:50513
}
- Caddy setzt
X-Forwarded-Forautomatisch korrekt – ebenso die meisten Standard-Proxys. - Bei Cloudflare Proxy kann eine Zusatzkonfiguration nötig sein (echte Client-IP aus
CF-Connecting-IPübernehmen), damit die Rate-Limit- und Raum-Logik pro Client funktioniert. - WebSockets (Signaling) muss der Proxy durchreichen – bei Caddy und Nginx-aktuell automatisch, bei älteren Nginx-Setups Upgrade-Header prüfen.
Erster Start & Nutzung
cd ~/pairdrop
docker compose up -d
docker compose logs -f pairdrop
Danach https://send.meine.domain auf beiden Geräten öffnen – sie tauchen
automatisch in der Geräteliste auf. Nützlich im Alltag:
- Als App installieren: PWA im Browser-Menü auf den Homescreen – fühlt sich wie eine native App an.
- Paaren statt Räume: Eigene Geräte per 6-stelligem Code/QR dauerhaft koppeln, damit sie sich auch über das Internet finden.
- Mehrere Dateien landen als ZIP auf dem Empfänger; Texte und Code-Snippets lassen sich direkt verschicken.
- Share-Menü: Unter Android/iOS direkt „Mit PairDrop teilen“, unter Linux/Windows per Kontextmenü-Skript.
- Kopierte Inhalte vorab einfügen und Empfänger danach wählen.
Sicherheit & Privatsphäre
- Standard = P2P: Dateien gehen verschlüsselt direkt von Gerät zu Gerät, dein Server sieht nur die Verbindungsvermittlung.
- Keine Konten, kein Speicher: Es gibt nichts anzumelden und nichts zu leaken – die Übertragung existiert nur, solange beide Browser-Tabs offen sind.
- WS-Fallback aus: Solange
WS_FALLBACK=falseist, kann dein Server nie Daten lesen. Erst der Fallback-Modus macht ihn zum Relay. - Rate-Limit einschalten, wenn öffentlich:
RATE_LIMIT=truegegen Missbrauch – kombiniert mit korrektemX-Forwarded-For. - Kein Debug-Modus:
DEBUG_MODE=trueloggt Client-IPs – für den Dauerbetrieb tabu.
Updates & Wartung
Da der Container nichts speichert, ist die Wartung denkbar einfach:
docker compose pull pairdrop && docker compose up -d
- Kein Backup nötig – es gibt keine eigenen Daten. Lediglich optionale Dateien wie
rtc_config.jsonliegen außerhalb. - Der
:latest-Tag zieht die neueste LinuxServer-Version; wer kontrolliert aktualisieren will, pinnt einen Versions-Tag. - Nach Updates reicht ein kurzer Log-Check:
docker compose logs pairdrop.
Häufige Probleme (FAQ)
| Problem | Lösung |
|---|---|
| Gerät wird im selben WLAN nicht gefunden | Client-Isolation/Access-Point-Isolation im Router deaktivieren; beide Geräte müssen PairDrop geöffnet haben. |
| Verbinden schlägt hinter NAT/VPN fehl | VPN blockt WebRTC oft komplett → WS_FALLBACK=true oder eigenen TURN-Server via RTC_CONFIG einrichten. |
| Alle Clients sehen sich gegenseitig / Rate-Limit greift global | X-Forwarded-For im Proxy korrekt setzen (bei Cloudflare: echte Client-IP übernehmen). |
| Instanz öffentlich ohne Rate-Limit | RATE_LIMIT=true setzen – 100 Anfragen pro 5 Minuten pro Client. |
| Port 50513 schon belegt | Mit dem Ollama-ROCm-Stack kollidiert der Port – externen Port ändern (z. B. 50515). |
| WebUI nicht erreichbar über Proxy | WebSocket-/Upgrade-Header des Proxys prüfen; Container lauscht nur auf 127.0.0.1 – Proxy muss auf demselben Host laufen. |
Fazit
PairDrop ist die pragmatischste AirDrop-Alternative, die es ohne Apple-Hardware gibt: Browser auf, Gerät sehen, Datei rüberschieben – fertig. Wer die Instanz privat für sich und Familie betreibt, bekommt mit dem Standard-Setup echten P2P-Transfer ohne Datenumweg. Wer öffentlich hostet, schaltet Rate-Limit und ggf. Fallback bewusst – und weiß dann auch, was das für die Privatsphäre bedeutet.
WS_FALLBACK heißt:
Daten laufen dann durch den Server. ③ Öffentlich hosten → RATE_LIMIT=true + korrektes
X-Forwarded-For. ④ Kein Volume, kein Backup – dafür saubere Updates per pull && up -d.