// Tutorial · Self-Hosting · Datenschutz

PrivateBin selbst hosten: Dein Zero-Knowledge-Pastebin mit Docker Compose

📅 25.09.2026 ⏱ 9 Min. Lesezeit ✍️ Redaktion

Teile Geheimnisse, Logs oder SSH-Keys, ohne dass der Server sie je zu Gesicht bekommt: PrivateBin verschlüsselt jeden Paste 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 dir dein eigenes Zero-Knowledge-Pastebin mit Docker Compose ein – inklusive korrekter Volume-Rechte, read_only-Betrieb und Reverse Proxy.

Warum ein eigenes Pastebin?

Öffentliche Paste-Dienste wirken praktisch – bis du Log-Auszüge, Konfigurationen oder Tokens einfügst, die dir nicht gehören. Wer selbst hostet, entscheidet, was gespeichert wird, wie lange es lebt und wer darauf zugreifen darf. Und weil PrivateBin zusätzlich Ende-zu-Ende-verschlüsselt, wird aus „eigener Server“ sogar „der Server sieht nichts“.

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

Konzept: Zero Knowledge & XSS-Schutz

PrivateBin (Fork des eingestellten ZeroBin) funktioniert anders als ein klassisches Pastebin:

  • Verschlüsselung im Browser: AES-256 im Galois-Counter-Modus; der Schlüssel steht nur in der URL nach dem # und wird nie zum Server gesendet.
  • Server ohne Wissen: Gespeichert wird ausschließlich Chiffrat – selbst ein Server-Admin kann Inhalte nicht lesen.
  • XSS-Schutz: Strikte Content-Security-Policy, kein aktiver Inhalt von Dritten, Paste-Inhalte werden nie roh gerendert.
  • Kontrolle über Lebensdauer: Ablaufzeit, Passwortschutz und „Burn after reading“ (Paste löscht sich nach dem ersten Öffnen).

Vorbereitung: Ordner & Volume-Rechte

Das Image ist ein All-in-One-Container (Nginx + PHP-FPM + Alpine), dessen Prozesse als UID 65534 / GID 82 („nobody“) laufen. Genau diese Rechte braucht deshalb auch dein Daten-Volume – sonst kann PrivateBin keine Pastes speichern:

mkdir -p ~/privatebin/privatebin-data
sudo chown -R 65534:82 ~/privatebin/privatebin-data
Nicht user: 1000:1000 setzen! Der offizielle Container startet seine Dienste über s6 mit festen Benutzern. Ein Container-User von außen bricht diese Annahme und führt zu Schreibfehlern oder 502/403. Stattdessen gehört das Volume dem Nutzer 65534:82 – genau das erledigt der chown oben.

Docker Compose – die Datei

So sieht das fertige Setup aus (deine Vorlage, aufgeräumt und um die offiziell empfohlenen tmpfs-Mounts ergänzt):

services:
  privatebin:
    image: privatebin/nginx-fpm-alpine:stable   # statt Tag-Los: stable/Version pinnen
    container_name: privatebin
    restart: unless-stopped
    read_only: true
    tmpfs:
      - /tmp:nodev,noexec,mode=1777
      - /run:nodev,exec,mode=1777
    deploy:
      resources:
        limits:
          cpus: "2.0"
          memory: 2G
    ports:
      - "127.0.0.1:50520:8080"                  # Web-UI nur lokal
    volumes:
      - ./privatebin-data:/srv/data             # Pastes, Salt & Limiter-Daten
      # - ./conf.php:/srv/cfg/conf.php:ro       # nur bei eigener conf.php (siehe unten)

Warum die einzelnen Zeilen?

  • read_only: true + tmpfs: Das Root-Dateisystem ist schreibgeschützt; beschreibbar bleiben nur /tmp, /run, /var/lib/nginx/tmp und /srv/data. Das verkleinert die Angriffsfläche deutlich – wird vom Image offiziell unterstützt.
  • 127.0.0.1:50520:8080: Der Container lauscht intern auf Port 8080 (Nginx). Gebunden wird nur an localhost – der Zugriff von außen läuft ausschließlich über deinen Reverse Proxy.
  • ./privatebin-data:/srv/data: Hier landen Pastes, der Server-Salt und Limiter-Daten – das ist dein komplettes Backup.
  • Das Image kennt nur HTTP – TLS übernimmt zwingend der Proxy davor.

Konfiguration (conf.php) & Umgebungsvariablen

Standardmäßig startet der Container mit einer sicheren Default-Konfiguration. Willst du z. B. Datei-Uploads, Diskussionen, ein eigenes Template oder andere Speicher-Backends aktivieren, legst du eine eigene conf.php an und mountest sie schreibgeschützt nach /srv/cfg/conf.php:

# Beispiel: conf.php in ~/privatebin ablegen und zusätzlich mounten
# volumes:
#   - ./conf.php:/srv/cfg/conf.php:ro
  • Basis ist die conf.sample.php des Projekts – einfach kopieren, anpassen, mounten.
  • Alternativ lässt sich der Konfigurationspfad über die Variable CONFIG_PATH verschieben.
  • Zeitzone für Logs und Ablaufzeiten: TZ und PHP_TZ setzen (Achtung: spätere Änderung verschiebt Ablaufzeiten bestehender Pastes!).
  • Standard-Limit für Datei-Uploads sind 10 MiB – mehr geht über eigene php.ini- (nach /etc/php/conf.d/) und Nginx-Dateien (nach /etc/nginx/http.d/, Stichwort client_max_body_size).

Reverse Proxy & 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:

paste.meine.domain {
  encode zstd gzip
  reverse_proxy 127.0.0.1:50520
}
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 ~/privatebin
docker compose up -d
docker compose logs -f privatebin   # bis "ready" / ohne Fehler

Danach https://paste.meine.domain öffnen – es gibt kein Admin-Konto, die Seite ist sofort nutzbar:

  • Neuer Paste: Text einfügen, Ablaufzeit wählen, optional Passwort setzen oder „Nach dem Lesen löschen“ aktivieren → Link teilen.
  • QR-Code & Syntax-Highlighting inklusive – praktisch fürs Übertragen aufs Handy.
  • Standardmäßig sind Datei-Upload und Kommentarfunktion deaktiviert – wer sie braucht, aktiviert sie in der conf.php.

Wartung, Purge & Backups

Das Image bringt zwei Admin-Skripte mit, die du direkt im laufenden Container aufrufst:

# abgelaufene Pastes aufräumen
docker exec -t privatebin administration --purge

# Statistik über gespeicherte Pastes
docker exec -t privatebin administration --statistics

Für Updates reicht docker compose pull && docker compose up -d – mit getaggten Images (z. B. :stable oder :1.5.1) behältst du die Kontrolle, wann Neues kommt. :latest folgt dagegen der neuesten Veröffentlichung.

Backup unbedingt mitnehmen: ./privatebin-data enthält neben den Pastes auch den Server-Salt. Geht er verloren, lassen sich alte Pastes nicht mehr entschlüsseln. Ein tar des Ordners (bei laufendem Stack: vorher kurz stoppen) genügt als vollständiges Backup.

Häufige Probleme (FAQ)

ProblemLösung
Pastes lassen sich nicht speichern / 500erVolume-Rechte prüfen: sudo chown -R 65534:82 privatebin-data und Container neu starten.
Eigene conf.php wird ignoriertMuss exakt nach /srv/cfg/conf.php gemountet sein – Pfad und Dateiname prüfen, Container neu erstellen (up -d --force-recreate).
Fehler beim Schreiben trotz VolumeBei read_only: true die tmpfs-Einträge für /tmp und /run ergänzen – ohne sie kann PHP-FPM keine Sockets/Caches anlegen.
Datei-Uploads über 10 MiB schlagen fehlclient_max_body_size (Nginx) und upload_max_filesize/post_max_size (PHP) erhöhen – Dateien in /etc/nginx/http.d/ und /etc/php/conf.d/ mounten.
Ablaufzeiten verschieben sich nach TZ-ÄnderungAblaufzeiten werden mit der PHP_TZ zum Erstellen berechnet – Zeitzone möglichst von Anfang an setzen.
502/403 hinter dem ProxyContainer läuft nicht als user: 1000; Rechte-Hinweis oben befolgen und Logs via docker compose logs privatebin prüfen.

Fazit

PrivateBin ist in zehn Minuten aufgesetzt und danach erstaunlich unauffällig im Betrieb: kein Konto, keine Datenbank, ein kleiner Datenordner. Der Clou bleibt das Konzept – selbst dein eigener Server kann deine Pastes nicht lesen. Kombiniert mit read_only, HTTPS davor und regelmäßigem Backup ist das Pastebin so privat, wie Pastebins nur sein können.

Die vier Merksätze: ① Volume gehört UID 65534/GID 82 – nicht user: 1000. ② read_only + tmpfs für /tmp & /run. ③ Immer HTTPS davor, der Schlüssel lebt im URL-Fragment. ④ Backup von privatebin-data inkl. Server-Salt.
📝
Redaktion Selfhosted Praxistests & Tutorials rund um Open-Source-Hosting – immer mit Blick auf Datensparsamkeit und eigene Kontrolle.