Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung Docker 10.10.2026 · 10 min Lesezeit

Zipline mit Docker Compose: Datei-Uploads und Kurzlinks selbst hosten

Zipline ist ein selbst gehosteter Upload-Dienst für Dateien und Screenshots mit eingebautem URL-Kürzer, kompatibel zu ShareX und Flameshot. Die Anleitung zeigt die Installation von Version 4.8.0 mit PostgreSQL per Docker Compose, den Upload per curl, getestete Berechtigungen, Reverse Proxy mit HTTPS-Links sowie ein Backup, das sich nachweislich zurückspielen lässt.

Geprüft am 10.10.2026 · für Zipline 4.8.0, PostgreSQL 16

Mit KI erstellt – redaktionelle Prüfung ausstehend

WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Illustration: Schriftzug Zipline selbst hosten mit Karten für Datei-Upload, Kurzlinks und Docker Compose neben Upload-Wolke und Server

Zipline ist ein selbst gehosteter Dienst für Datei- und Screenshot-Uploads mit eingebautem URL-Kürzer. Screenshot-Werkzeuge wie ShareX, Flameshot oder ein simples curl-Skript laden Dateien per API-Token hoch und bekommen sofort einen teilbaren Link zurück. Für KMU ist das eine Alternative zu öffentlichen Bildhostern: Supportfotos und Logauszüge bleiben auf dem eigenen Server. Diese Anleitung installiert Zipline 4.8.0 mit PostgreSQL per Docker Compose, prüft Upload, Kurzlinks und Berechtigungen und zeigt ein Backup, das sich nachweislich zurückspielen lässt.

Voraussetzungen

Im Leerlauf belegte Zipline im Test rund 180 MiB RAM, PostgreSQL rund 23 MiB.

  • Linux-Server oder VM mit Docker Engine und Docker Compose v2, 1 bis 2 CPU-Kerne, 2 GB RAM genügen für ein kleines Team.
  • Architektur x86_64 (amd64) oder ARM64: Das Image ghcr.io/diced/zipline:4.8.0 wird laut Registry-Manifest für beide Plattformen gebaut.
  • Rund 2 GB freier Platz für die Images (Zipline 1,05 GB, postgres:16 639 MB) plus Speicher für Ihre Dateien; Bildschirmvideos wachsen schnell.
  • Für den Betrieb außerhalb des LANs eine Domain mit TLS-Zertifikat und ein Reverse Proxy (Nginx, Caddy oder Nginx Proxy Manager).
  • curl, jq und openssl auf dem Server.
EckdatenWert
Projektdiced/zipline, MIT-Lizenz, rund 3.400 GitHub-Sterne
Version4.8.0 (Release vom 27.09.2026), Image-Tag 4.8.0 ohne „v“
Imagesghcr.io/diced/zipline:4.8.0, postgres:16
Port3000/TCP (Weboberfläche und API)
PersistenzVolume pgdata, Bind-Mounts ./uploads, ./public, ./themes
Pflicht-VariablenPOSTGRESQL_PASSWORD, CORE_SECRET (mindestens 32 Zeichen)
HealthcheckGET /api/healthcheck liefert {"pass":true}

Wer nur einen Dateimanager mit Freigaben sucht, ist mit FileBrowser Quantum oder copyparty besser bedient. Grenzen von Zipline: ein einzelner Maintainer, keine Ende-zu-Ende-Verschlüsselung, und der Container läuft als root, die Upload-Dateien auf dem Host gehören deshalb root.

Schritt 1: Projektordner, compose.yaml und .env anlegen

Die folgende compose.yaml entspricht der offiziellen docker-compose.yml aus dem Repository, mit einer Änderung: Statt latest ist die Version 4.8.0 festgeschrieben, damit ein docker compose pull nicht unbemerkt ein Major-Update einspielt. Achtung beim Tag: ghcr.io/diced/zipline:v4.8.0 existiert nicht, die Registry führt die Version ohne „v“.

sudo mkdir -p /opt/zipline && cd /opt/zipline
sudo nano compose.yaml
services:
  postgresql:
    image: postgres:16
    restart: unless-stopped
    env_file:
      - .env
    environment:
      POSTGRES_USER: ${POSTGRESQL_USER:-zipline}
      POSTGRES_PASSWORD: ${POSTGRESQL_PASSWORD:?POSTGRESQL_PASSWORD is required}
      POSTGRES_DB: ${POSTGRESQL_DB:-zipline}
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ['CMD', 'pg_isready', '-U', 'zipline']
      interval: 10s
      timeout: 5s
      retries: 5

  zipline:
    image: ghcr.io/diced/zipline:4.8.0
    restart: unless-stopped
    ports:
      - '3000:3000'
    env_file:
      - .env
    environment:
      - DATABASE_URL=postgres://${POSTGRESQL_USER:-zipline}:${POSTGRESQL_PASSWORD}@postgresql:5432/${POSTGRESQL_DB:-zipline}
    depends_on:
      postgresql:
        condition: service_healthy
    volumes:
      - './uploads:/zipline/uploads'
      - './public:/zipline/public'
      - './themes:/zipline/themes'
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://0.0.0.0:3000/api/healthcheck']
      interval: 15s
      timeout: 2s
      retries: 2

volumes:
  pgdata:

Heben Sie postgres:16 nicht eigenmächtig auf 18 an: Dort liegt das Volume unter /var/lib/postgresql, und ein Major-Wechsel braucht Dump und Restore.

Zwei Zufallswerte mit je 32 Zeichen erzeugt der Befehl aus der offiziellen Doku:

echo "POSTGRESQL_PASSWORD=$(openssl rand -base64 42 | tr -dc A-Za-z0-9 | cut -c -32 | tr -d '\n')" > .env
echo "CORE_SECRET=$(openssl rand -base64 42 | tr -dc A-Za-z0-9 | cut -c -32 | tr -d '\n')" >> .env
chmod 600 .env

Struktur der Datei (Platzhalterwerte):

# .env (Beispielwerte, nicht übernehmen)
POSTGRESQL_PASSWORD=ErsetzenDurch32ZufaelligeZeichen1
CORE_SECRET=ErsetzenDurchMindestens32Zeichen99
# erst nach Einrichtung des Reverse Proxy aktivieren:
# CORE_TRUST_PROXY=true
# CORE_TRUSTED_PROXIES=172.18.0.1
# CORE_RETURN_HTTPS_URLS=true

CORE_SECRET signiert Sitzungscookies und verschlüsselt API-Tokens. Bewahren Sie es zusätzlich im Passwortmanager auf.

Verifizieren: docker compose config --quiet endet ohne Ausgabe. Fehlt das Datenbankpasswort, bricht Compose bereits hier ab: required variable POSTGRESQL_PASSWORD is missing a value: POSTGRESQL_PASSWORD is required.

Schritt 2: Container starten und Healthchecks prüfen

docker compose pull
docker compose up -d
docker compose ps
docker compose logs --no-log-prefix zipline | head

Im Test war Zipline nach gut 20 Sekunden healthy. Das Log nennt die Version:

[INFO   server] starting zipline mode="production" version="4.8.0" argv=[]
[INFO   db] connecting to database
[INFO   server] server started hostname="0.0.0.0" port=3000

Verifizieren: Beide Dienste zeigen in docker compose ps den Status (healthy), und curl -s http://localhost:3000/api/healthcheck liefert {"pass":true}.

Schritt 3: Ersten Administrator anlegen

Beim ersten Aufruf von http://SERVER:3000 leitet Zipline auf eine Einrichtungsseite um, auf der Sie Benutzername und Passwort des Super-Administrators festlegen. Ohne Browser geht es per API; GET /api/setup verrät, ob die Instanz noch unkonfiguriert ist:

curl -s http://localhost:3000/api/setup
# {"firstSetup":true}

curl -s -H 'content-type: application/json' \
  -d '{"username":"admin","password":"LANGES-PASSWORT"}' \
  http://localhost:3000/api/setup | jq '.user.role'
# "SUPERADMIN"

Richten Sie die Instanz direkt nach dem Start ein: Solange firstSetup wahr ist, kann jeder, der Port 3000 erreicht, den Administrator anlegen. Ein zweiter Versuch nach der Einrichtung scheitert mit {"error":"E9001: Forbidden","code":9001,"statusCode":403}. Auch die Selbstregistrierung ist in 4.8.0 ab Werk aus, POST /api/auth/register antwortet mit E1037: User registration is disabled. Weitere Konten legen Administratoren unter Administrator, Users an.

Zipline-Dashboard nach der Anmeldung mit zuletzt hochgeladenen Dateien und Upload-Statistik
Das Dashboard nach der Einrichtung: drei Testdateien, eine davon passwortgeschützt, dazu Speicher- und Abrufstatistik.

Verifizieren: GET /api/setup liefert jetzt {"firstSetup":false}, und die Anmeldung im Browser führt zum Dashboard.

Schritt 4: Datei per API-Token hochladen und abrufen

Den Token finden Sie über den Benutzernamen oben rechts, Settings, Feld Token. Er hat vollen Kontozugriff; speichern Sie ihn in einer Datei mit Rechten 600.

TOKEN=$(cat ~/.zipline-token)
curl -s -H "authorization: $TOKEN" -F file=@bericht.png \
  http://localhost:3000/api/upload | jq -r '.files[0].url'
# http://localhost:3000/u/2eOD40.png

/u/NAME zeigt die Datei an, /raw/NAME liefert Rohdaten; die SHA-256-Prüfsumme der Testdatei war identisch. HTTP-Header steuern Optionen pro Upload, etwa x-zipline-max-views: 1 für Einmal-Links: Der erste Abruf lieferte 200, jeder weitere 404. Mit x-zipline-password wird die Datei passwortgeschützt, /raw/NAME antwortet dann ohne Freigabe mit E3018: Invalid access token provided. und ein falsches Passwort mit E3005: Incorrect password.

Upload-Seite von Zipline mit Ablagefläche für Dateien und Hinweis auf 100 MB Grenze pro Datei
Upload im Browser: Dateien per Drag and Drop oder Einfügen aus der Zwischenablage, Grenze ab Werk 100 MB pro Datei.

Den Kurzlink-Dienst erreichen Sie über denselben Token:

curl -s -H "authorization: $TOKEN" -H 'content-type: application/json' \
  -d '{"destination":"https://s-edv.com/"}' \
  http://localhost:3000/api/user/urls | jq -r .url
# http://localhost:3000/go/o97dvD

/go/o97dvD antwortete mit HTTP 302 auf das Ziel. Mehr Funktionen bietet Shlink.

Verifizieren: Der Upload liefert JSON mit files[0].url, die Datei erscheint im Ordner uploads und im Dashboard unter Files.

Schritt 5: Berechtigungen gegenprüfen

Ein erfolgreicher Upload beweist noch nichts über die Zugriffskontrolle. Prüfen Sie deshalb auch die Fälle, die scheitern müssen:

  • Upload ohne Token: HTTP 401, E2000: Invalid login session.
  • Upload mit verändertem Token: HTTP 401, E2001: Invalid token.
  • Normaler Benutzer liest oder löscht die Datei eines anderen über /api/user/files/ID: HTTP 404, E4000: File not found. Zipline verrät damit nicht einmal, dass die Datei existiert.
  • Normaler Benutzer ruft /api/users oder /api/server/settings auf: HTTP 403, E3000: Admin only.
  • Anmeldung mit falschem Passwort: HTTP 400, E1044: Invalid username or password.

Öffentliche Links sind ohne Anmeldung abrufbar, das ist der Zweck des Dienstes. Schnelle Wiederholungen beantwortet der Rate-Limiter mit HTTP 429 (Rate limit exceeded, retry in 5 seconds).

Benutzerverwaltung von Zipline im Administratorbereich mit einem Konto der Rolle User
Administrator, Users: Hier legen Sie weitere Konten an. Das Konto mit der Rolle User sah im Test keine fremden Dateien.

Verifizieren: Alle fünf Aufrufe liefern den genannten Fehlercode, die Datei des Administrators bleibt unter /raw/NAME mit 200 abrufbar.

Schritt 6: Reverse Proxy, TLS und ShareX

Hinter einem Proxy gibt Zipline zunächst Links mit http:// zurück. Das ist im Test genau so aufgetreten. Abhilfe schaffen drei Variablen in der .env: CORE_TRUST_PROXY=true, CORE_TRUSTED_PROXIES mit der Adresse des Proxys, wie sie der Container sieht, und CORE_RETURN_HTTPS_URLS=true. Bei einem Proxy auf dem Host kommen Anfragen vom Gateway des Compose-Netzes (im Test 172.18.0.1, eingetragen war das Docker-Netz 172.16.0.0/12; enger ist besser). Niemals 0.0.0.0/0 eintragen, sonst fälschen Clients ihre IP. Danach lieferte der Upload https://-Links. Nginx-Beispiel:

server {
    listen 443 ssl;
    server_name files.example.de;
    ssl_certificate     /etc/ssl/files.example.de.pem;
    ssl_certificate_key /etc/ssl/files.example.de.key;
    client_max_body_size 100M;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

client_max_body_size sollte zur Upload-Grenze passen; die Oberfläche zeigt ab Werk „100 MB limit per file“. Mit Caddy reicht reverse_proxy localhost:3000, Details in unserer Caddy-Anleitung. Binden Sie Port 3000 danach nur noch an 127.0.0.1:3000:3000, damit niemand den Proxy umgeht.

Für ShareX erzeugt Zipline die Konfiguration selbst: Settings, Abschnitt Generate Uploaders, ShareX. Im Dialog wählen Sie Upload File oder Shorten URL, Namensformat, Kompression und Abrufgrenze, dann Download. Die .sxcu-Datei importieren Sie per Doppelklick in ShareX. Auch Flameshot und ein Shell-Skript stehen bereit.

Dialog Generate ShareX Uploader in Zipline mit Zieltyp, Namensformat, Kompression, Abrufgrenze und Download-Schaltfläche
ShareX-Konfiguration erzeugen: Zieltyp und Optionen wählen, dann die .sxcu-Datei herunterladen und in ShareX importieren.

Verifizieren: Ein Upload über die Domain liefert eine URL mit https://, und curl -I https://files.example.de/api/healthcheck antwortet mit 200.

Schritt 7: Backup und Restore

Metadaten, Benutzer und Kurzlinks liegen in PostgreSQL, die Dateien in uploads. Sichern Sie beides bei gestoppter Anwendung:

cd /opt/zipline && mkdir -p backup
docker compose stop zipline
docker compose exec -T postgresql pg_dump -U zipline zipline > backup/zipline_db.sql
tar czf backup/zipline_files.tgz uploads public themes
docker compose start zipline

Kopieren Sie backup/, compose.yaml und .env auf ein anderes System. Restore nach gelöschter Datenbank und geleertem Upload-Ordner:

docker compose stop zipline
docker compose exec -T postgresql psql -U zipline -d postgres -c 'DROP DATABASE zipline;'
docker compose exec -T postgresql psql -U zipline -d postgres -c 'CREATE DATABASE zipline OWNER zipline;'
docker compose exec -T postgresql psql -v ON_ERROR_STOP=1 -q -U zipline zipline < backup/zipline_db.sql
sudo tar xzf backup/zipline_files.tgz
docker compose start zipline

Ergebnis: Zipline war nach dem Start wieder healthy, /api/user/files?page=1 listete alle drei Testdateien, die Prüfsumme der Binärdatei war identisch, und der alte API-Token funktionierte weiter (HTTP 200). Letzteres gilt nur, weil das CORE_SECRET gleich blieb. Mit einem neuen Secret antwortete derselbe Token mit E2001: Invalid token, alle Benutzer müssen sich neu anmelden und neue Tokens in ShareX hinterlegen. Weitere Strategien für Dumps beschreibt die Anleitung PostgreSQL mit pg_dump und pg_restore sichern.

Verifizieren: Der Restore endet mit Exit-Code 0, Dateien sind im Dashboard sichtbar und unter /raw/NAME abrufbar.

Schritt 8: Updates, Rollback und Entfernen

Für ein Update tragen Sie den neuen Tag in compose.yaml ein, sichern wie in Schritt 7 und führen docker compose pull und docker compose up -d aus. Lesen Sie vorher die Release-Hinweise: Zipline 4.8 hat Prisma durch Drizzle ersetzt und migriert die Datenbank beim ersten Start einmalig; ältere Instanzen als 4.7.0 müssen zuerst auf 4.7.0. Rollback heißt deshalb: alten Tag eintragen und Datenbank samt Uploads aus dem Backup vor dem Update zurückspielen. Nur den Tag zurückzudrehen reicht nicht.

Zum Entfernen genügt docker compose down. Warnung: docker compose down -v löscht das Volume pgdata mit allen Benutzern, Links und Metadaten endgültig, und rm -rf /opt/zipline entfernt zusätzlich alle hochgeladenen Dateien. Führen Sie beides nur mit geprüftem Backup aus.

Verifizieren: Nach einem Update zeigt das Startprotokoll die neue Versionsnummer, und die Testdatei ist weiterhin abrufbar.

Troubleshooting

  • Container startet in Schleife, Log zeigt CORE_SECRET: Invalid input: expected string, received undefined: Die Variable fehlt in der .env. Wert erzeugen und docker compose up -d.
  • CORE_SECRET: Secret must contain at least 32 characters: Der Wert ist zu kurz. Mindestens 32 Zeichen verwenden.
  • Alle Tokens melden E2001: Invalid token: Das CORE_SECRET wurde geändert. Altes Secret zurückspielen oder neue Tokens verteilen.
  • Pull scheitert, Tag nicht gefunden: Image-Tag mit „v“ angegeben. Die Registry beantwortet v4.8.0 mit 404, richtig ist 4.8.0.
  • Links beginnen mit http:// trotz HTTPS: CORE_TRUST_PROXY, CORE_TRUSTED_PROXIES und CORE_RETURN_HTTPS_URLS setzen (Schritt 6).
  • /api/user/files liefert 400 mit expected number, received NaN: Der Parameter ?page=1 fehlt.

Häufige Fragen

Brauche ich PostgreSQL zwingend?

Nein. Zipline unterstützt alternativ PGlite, eine eingebettete Datenbank in einem Verzeichnis (DATABASE_URL=pglite:///zipline/data/database). PostgreSQL ist für Teams die robustere Wahl.

Wie schütze ich das Admin-Konto zusätzlich?

Zipline bietet TOTP-Zweifaktor und Passkeys in den Benutzereinstellungen sowie OAuth und OIDC, etwa mit Authentik oder Keycloak. Für alle, die die Instanz mit anderen teilen, empfiehlt der Hardening-Leitfaden mindestens eine dieser Optionen.

Wer kann eine hochgeladene Datei sehen?

Jeder, der den Link kennt. Die Dateinamen sind zufällig, aber nicht geheim. Für sensible Inhalte nutzen Sie Passwortschutz, Ablaufzeit (x-zipline-deletes-at) oder eine Abrufgrenze.

Fazit

Zipline ist mit zwei Containern und zwei Pflichtvariablen schnell betriebsbereit und deckt Screenshot-Uploads, Einmal-Links und Kurzlinks in einer Oberfläche ab. Die Zugriffskontrolle hielt im Test allen Gegenversuchen stand. Kritisch sind das CORE_SECRET und die gemeinsame Sicherung von Datenbank und Upload-Ordner. Mit festem Tag und Backup vor jedem Update bleibt der Dienst pflegeleicht.

Weiterführende Anleitungen und Quellen

ZiplineDocker ComposeShareXSelfhostingPostgreSQLURL-KürzerDatei-Upload