// Tutorial · Self-Hosting · Datenschutz

SkySend selbst hosten: E2E-verschlüsseltes Teilen mit Docker Compose

📅 03.09.2026 ⏱ 10 Min. Lesezeit ✍️ Redaktion

Große Dateien oder geheime Notizen teilen, ohne dass der Server sie je zu Gesicht bekommt: SkySend verschlüsselt Uploads direkt im Browser (AES-256-GCM) – auf dem Server liegt nur Chiffrat, der Schlüssel verlässt dein Gerät nie. In diesem Tutorial richtest du dein eigenes Zero-Knowledge-Sharing mit Docker Compose ein – inklusive Limits, Ablaufzeiten, Quota und Reverse Proxy.

Warum SkySend?

E-Mail-Anhänge scheitern an Größenlimits, Messenger komprimieren die Qualität und Cloud-Dienste lesen mit – wer einmal eine 3 GB-Datei verschicken wollte, kennt das Problem. SkySend ist ein minimalistischer, selbst hostbarer Dienst zum Teilen von Dateien und Notizen mit Ende-zu-Ende-Verschlüsselung: keine Konten, kein Tracking, keine fremden Server.

Live ausprobieren: Unsere Instanz läuft unter sky.huuu.biz – ideal, um Bedienung und Ablaufzeiten vor dem Selbst-Hosten zu testen.

Konzept: Zero Knowledge

SkySend funktioniert nach demselben Prinzip wie das Vorbild-Paar timvisee/send (Dateien) und PrivateBin (Notizen):

  • Verschlüsselung im Browser: AES-256-GCM (streaming, 64-KB-Blöcke); Schlüsselableitung über HKDF-SHA256, bei Passwortschutz zusätzlich Argon2id.
  • Schlüssel nur in der URL: Er steht ausschließlich im Fragment (#…) und wird nie zum Server gesendet.
  • Server ohne Wissen: Gespeichert werden nur verschlüsselte Blobs und Metadaten – selbst ein Admin kann Inhalte nicht lesen.
  • Kontrolle über Lebensdauer: Ablaufzeit, Download-Limit, Passwort und „Burn after reading“ – danach werden die Daten gelöscht.

Vorbereitung: Ordner & Volumes

SkySend braucht zwei Datenverzeichnisse, die beide gemountet werden müssen. Ohne Volumes gehen beim Container-Neubau alle Uploads und der Datenbankstand verloren:

mkdir -p ~/skysend/data ~/skysend/uploads
cd ~/skysend
printf 'PUID=1000\nPGID=1000\n' > .env
sudo chown -R 1000:1000 data uploads
  • ./data/data: SQLite-Datenbank (/data/db/skysend.db), Metadaten und Branding (/data/branding).
  • ./uploads/uploads: die verschlüsselten Upload-Dateien.
PUID/PGID: Das Image unterstützt beide Variablen. Dank des ${PUID:-1000}-Fallbacks in der Compose-Datei startet der Stack auch ohne .env – dann läuft der Prozess als UID 1000.

Docker Compose – die Datei

So sieht das fertige Setup aus (deine Vorlage, aufgeräumt: leerer networks: {}-Block entfernt, Ports in Anführungszeichen):

services:
  skysend:
    image: skyfay/skysend:latest
    container_name: skysend
    restart: unless-stopped
    deploy:
      resources:
        limits:
          cpus: "4.0"
          memory: 4G
    ports:
      - "127.0.0.1:50548:3000"        # Web-UI nur lokal
    volumes:
      - ./data:/data                  # DB, Metadaten & Branding
      - ./uploads:/uploads            # verschlüsselte Dateien
    environment:
      - BASE_URL=https://sky.huuu.biz:50548   # öffentliche URL für Teilen-Links
      - FILE_MAX_SIZE=4GB
      - FILE_MAX_FILES_PER_UPLOAD=250
      - FILE_EXPIRE_OPTIONS_SEC=600,3600,86400,172800,604800,1209600
      - FILE_DEFAULT_EXPIRE_SEC=600
      - FILE_DOWNLOAD_OPTIONS=1,2,5,10,20,50,100
      - FILE_DEFAULT_DOWNLOAD=1
      # - FILE_UPLOAD_QUOTA_BYTES=110GB          # Quota (deaktiviert)
      # - FILE_UPLOAD_CONCURRENT_CHUNKS=10       # parallele Chunks
      - CUSTOM_LINK_URL=https://huuu.biz/#contact   # Footer-Link
      - CUSTOM_LINK_NAME=DMCA
      - CUSTOM_COLOR=474f63
      - PUID=${PUID:-1000}
      - PGID=${PGID:-1000}
      # Alle Optionen: https://docs.skysend.app/user-guide/configuration/environment-variables

Warum die einzelnen Zeilen?

  • BASE_URL: Aus dieser Adresse baut SkySend alle Teilen-Links – sie muss exakt der öffentlichen URL entsprechen (Details im Proxy-Abschnitt).
  • 127.0.0.1:50548:3000: Die Web-UI lauscht intern auf Port 3000; gebunden wird nur an localhost, der Zugriff von außen läuft über den Reverse Proxy.
  • FILE_*: Dateigröße (4 GB), Dateien je Upload (250) sowie wählbare Ablauf- und Download-Limits – Standard ist hier 10 Minuten bei 1 Download.
  • CUSTOM_*: eigener Footer-Link (z. B. DMCA) samt Anzeigename und Primärfarbe der UI (474f63, das #-Präfix ist optional).

Konfiguration & Umgebungsvariablen

Die wichtigsten Werte aus deiner Konfiguration im Überblick:

VariableWertBedeutung
BASE_URLerforderlichÖffentliche Instanz-URL; dient CORS und allen generierten Teilen-Links.
FILE_MAX_SIZE4GBMaximale Dateigröße pro Upload (Default: 2 GB).
FILE_MAX_FILES_PER_UPLOAD250Max. Dateien je Multi-Upload (Default: 32).
FILE_EXPIRE_OPTIONS_SEC10 Min. … 14 TageAuswählbare Ablaufzeiten in Sekunden.
FILE_DEFAULT_EXPIRE_SEC600Vorausgewählte Ablaufzeit – muss in den Optionen enthalten sein.
FILE_DOWNLOAD_OPTIONS1–100Auswählbare Download-Limits; 1 = „nur einmal lesbar“.
CUSTOM_LINK_URL/NAMEDMCAEigener Footer-Link, z. B. auf eine Kontakt-/DMCA-Seite.
CUSTOM_COLOR474f63Primärfarbe der UI als 6-stelliger Hex-Code.
PUID/PGID1000UID/GID, unter denen der Container-Prozess läuft.

Weitere praktische Optionen aus der offiziellen Referenz:

  • TRUST_PROXY=true: hinter einem Reverse Proxy setzen, damit Rate-Limits und Quotas die echte Client-IP sehen.
  • FILE_UPLOAD_WS (Default true): Uploads laufen über eine WebSocket-Verbindung und umgehen damit HTTP/2-Engpässe von Proxies; Clients fallen sonst automatisch auf HTTP-Chunked-Upload zurück.
  • FILE_UPLOAD_QUOTA_BYTES + FILE_UPLOAD_CONCURRENT_CHUNKS: Upload-Quota (z. B. 110GB) bzw. parallele Chunks für schnellere Uploads in Chromium-Browsern.
  • CUSTOM_TITLE, CUSTOM_PRIVACY, CUSTOM_LEGAL: Titel bzw. Footer-Links auf Datenschutz/Impressum; CUSTOM_LOGO über /data/branding.
  • STORAGE_BACKEND=s3: S3-kompatibler Speicher statt Dateisystem (R2, MinIO, Hetzner …).

Quotas bleiben dabei privat: SkySend speichert nur HMAC-gehashte IPs mit täglich rotierendem Schlüssel – kein Klartext, keine Korrelation über Tage hinweg.

Reverse Proxy, BASE_URL & HTTPS

Der Container spricht nur HTTP – also gehört ein TLS-terminierender Proxy davor. Da der Port nur auf localhost lauscht, reicht ein Proxy auf demselben Host. Wichtig für SkySend: WebSocket-Upgrades (Upload-Transport) und kein Body-Limit (Uploads bis 4 GB regelt die App):

server {
  listen 443 ssl;
  listen [::]:443 ssl;
  http2 on;
  server_name sky.meine.domain;

  access_log off;
  error_log /var/log/nginx/sky.meine.domain.error.log;
  ssl_certificate /etc/ssl/private/sky.meine.domain_ecc/fullchain.cer;
  ssl_certificate_key /etc/ssl/private/sky.meine.domain_ecc/sky.meine.domain.key;

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_early_data on;
  ssl_session_cache shared:SSL:50m;
  ssl_session_timeout 1d;

  # Sicherheits-Header
  add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload" always;
  add_header X-Content-Type-Options "nosniff" always;
  add_header X-Frame-Options "DENY" always;
  add_header Referrer-Policy "no-referrer" always; # schützt den Schlüssel im URL-Fragment

  # Uploads bis FILE_MAX_SIZE (4GB) zulassen – Limit prüft SkySend selbst
  client_max_body_size 0;

  location / {
    proxy_http_version 1.1;
    # WebSocket-Transport für Uploads (FILE_UPLOAD_WS)
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Scheme $scheme;

    # lange Uploads nicht abbrechen
    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;

    proxy_pass http://127.0.0.1:50548;
  }
}

Lief der Dienst vorher direkt unter https://sky.huuu.biz:50548, genügt nach dem Proxy-Umzug die Änderung von BASE_URL=https://sky.huuu.biz (ohne Port!) und TRUST_PROXY=true in der Compose-Datei – danach Container neu erstellen: docker compose up -d --force-recreate.

Warum HTTPS Pflicht ist: Der Entschlüsselungsschlüssel steckt im URL-Fragment (#…). Über unverschlüsseltes HTTP wäre er im Klartext unterwegs – und damit wäre die Browser-Verschlüsselung wertlos.

Erster Start & Nutzung

cd ~/skysend
docker compose up -d
docker compose logs -f skysend
curl -s http://127.0.0.1:50548/api/health   # {"status":"ok", ...}

Danach die Instanz-URL öffnen (hier https://sky.huuu.biz:50548) – es gibt kein Admin-Konto, die Seite ist sofort nutzbar:

  • Dateien: Drag & Drop, mehrere Dateien/Ordner, optionales Passwort, Ablaufzeit und Download-Limit wählen → Link teilen.
  • Notizen: Markdown-Text, Passwörter, Code oder SSH-Keys (Ed25519/RSA) – wahlweise „Burn after reading“.
  • Dashboard im Browser: hochgeladene Einträge mit Live-Status und Auto-Cleanup (IndexedDB, bleibt lokal).
  • CLI: Das Kommandozeilen-Tool nutzt dieselbe Ende-zu-Ende-Verschlüsselung: skysend upload ./datei.pdf.

Wer den Link hat, kann lesen – mehr nicht. Konten gibt es keine.

Backups, Quotas & Updates

Für Updates reicht docker compose pull && docker compose up -d – Daten bleiben dank Volumes erhalten. :latest folgt dabei der neuesten Veröffentlichung; für reproduzierbare Stände lässt sich das Image auf einen konkreten Versions-Tag pinnen.

Backup unbedingt mitnehmen: Sicher ./data (Datenbank!) und ./uploads gemeinsam, damit Metadaten und Blobs zusammenpassen. Wer die auskommentierten Zeilen aktiviert (FILE_UPLOAD_QUOTA_BYTES=110GB), begrenzt das Upload-Volumen pro Nutzer – ohne dass IPs gespeichert werden.

Häufige Probleme (FAQ)

ProblemLösung
Teilen-Link zeigt falsche Domain/PortBASE_URL exakt auf die öffentliche URL setzen – mit Port nur, wenn der wirklich offen ist.
Upload langsam oder bricht abChromium + HTTP/2-Proxy: FILE_UPLOAD_CONCURRENT_CHUNKS erhöhen oder WebSocket-Transport prüfen; Proxy-Timeouts erhöhen.
Rate-Limit trotz wenig TrafficHinter Proxy TRUST_PROXY=true setzen – sonst zählt der Proxy als ein einziger Client.
Daten nach Container-Neubau wegBeide Volumes (/data und /uploads) müssen gemountet sein.
Ablaufzeit/Download-Limit nicht wählbarDer Default-Wert muss in der jeweiligen Options-Liste enthalten sein (z. B. 600 in FILE_EXPIRE_OPTIONS_SEC).
Port-Konflikt beim StartExternen Port prüfen (hier 50548) – nicht doppelt in anderen Stacks vergeben.

Fazit

SkySend ist in zehn Minuten aufgesetzt und danach erstaunlich unauffällig im Betrieb: keine Konten, keine Telemetrie, zwei Datenordner. Der Clou bleibt das Konzept – selbst dein eigener Server kann die Inhalte nicht lesen, weil die Verschlüsselung im Browser passiert. Kombiniert mit HTTPS davor, einem sauberen Proxy (WebSockets!) und regelmäßigem Backup ist das Teilen so privat, wie Teilen nur sein kann.

Die vier Merksätze: ① Zero Knowledge: Verschlüsselung im Browser, Schlüssel nur im URL-Fragment. ② Beide Volumes (/data + /uploads) mounten. ③ BASE_URL exakt setzen, hinter Proxy TRUST_PROXY=true und WebSockets durchreichen. ④ Immer HTTPS – sonst ist der Schlüssel wertlos.
R
...

Wir betreiben unsere komplette Infrastruktur selbst auf Open-Source-Software und schreiben nur über Tools, die wir im Alltag wirklich einsetzen. Fragen zum Artikel? Schreib uns.