Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung Cloud / Hosting 27.06.2026 · 10 min Lesezeit

Joplin mit Docker installieren: Quelloffene Notiz- und To-do-App mit selbst gehostetem Server

Joplin Server als Docker-Stack: So richtest du die quelloffene Notiz-App mit PostgreSQL, Ende-zu-Ende-Verschlüsselung und Team-Sync DSGVO-konform auf deinem Linux-Server ein – Evernote oder OneNote ersetzen ohne Cloud-Abhängigkeit.

Joplin mit Docker installieren als selbst gehostete Open Source Notiz und To do App mit Joplin Server, Ende zu Ende verschlüsselter Synchronisation, PostgreSQL, Redis, Backups und sicherer Docker Infrastruktur.

Joplin ist eine quelloffene Notiz- und To-do-App mit über 55.000 GitHub-Sternen, Markdown-Support, Notizbuch-Struktur und optionaler Ende-zu-Ende-Verschlüsselung (E2EE). Während Joplin-Clients standardmäßig über Cloud-Dienste wie Dropbox oder OneDrive synchronisieren, ermöglicht der selbst gehostete Joplin Server eine vollständige Datenkontrolle: Alle Notizen und Anhänge bleiben auf deiner Infrastruktur, Teams können Notizbücher gemeinsam nutzen, und du bist unabhängig von Drittanbietern. Besonders für KMU, die Evernote oder Microsoft OneNote durch eine DSGVO-konforme Lösung ersetzen wollen, ist das ein überzeugendes Argument. Diese Anleitung richtet den Joplin Server mit Docker Compose auf einem beliebigen Linux-Host ein – inklusive PostgreSQL, persistentem Storage und Web-Admin.

Voraussetzungen

  1. Linux-Host, VM oder NAS mit Docker-Unterstützung (Ubuntu 22.04/24.04 oder Debian empfohlen) – mindestens 2 GB RAM, empfohlen 4 GB; mindestens 10 GB freier Speicher
  2. Docker Engine (mind. Version 20.10) und Docker Compose Plugin v2 installiert – falls noch nicht vorhanden, folge der Anleitung Docker und Docker Compose auf Linux installieren
  3. Netzwerkzugriff auf Port 22300 des Hosts (oder Port 80/443 bei Reverse-Proxy)
  4. Für HTTPS-Betrieb: Domain oder DynDNS-Eintrag und ein Reverse-Proxy (Nginx, Traefik oder Caddy) – empfehlenswert für alles außerhalb des lokalen Netzes; eine Einführung bietet Caddy als Reverse Proxy einrichten
  5. Texteditor (nano, vim oder VS Code) für compose.yaml und .env
  6. Browser und curl für die Verifikation

Schritt 1: Projektordner anlegen

Lege einen eigenen Ordner für den Joplin-Stack an. Alle nachfolgenden Dateien kommen in dieses Verzeichnis. Die Unterordner für PostgreSQL-Daten werden beim ersten Start automatisch erstellt.

mkdir -p /opt/joplin
cd /opt/joplin

Verifizieren: Das Verzeichnis existiert und ist leer:

ls /opt/joplin
# Erwartete Ausgabe: (leer)

Schritt 2: .env-Datei mit Secrets anlegen

Lege eine .env-Datei im Projektordner an. Diese Datei enthält alle sensiblen Zugangsdaten und die öffentliche Base-URL. Sie wird von Docker Compose automatisch eingelesen und darf nicht ins öffentliche Versionsverwaltungssystem eingecheckt werden. Ersetze 192.168.1.100 durch die IP-Adresse oder den Hostnamen deines Servers und wähle ein sicheres Passwort.

# /opt/joplin/.env

# Öffentliche URL des Joplin Servers – EXAKT so, wie Clients ihn erreichen.
# Bei Reverse-Proxy mit HTTPS: https://joplin.example.com (kein trailing slash!)
APP_BASE_URL=http://192.168.1.100:22300

# PostgreSQL-Zugangsdaten (müssen in db- und app-Service identisch sein)
POSTGRES_USER=joplin
POSTGRES_PASSWORD=SicheresPasswortHier
POSTGRES_DATABASE=joplindb

Schränke die Datei-Berechtigungen ein, damit nur root sie lesen kann:

chmod 600 /opt/joplin/.env

Verifizieren:

ls -la /opt/joplin/.env
# Erwartete Ausgabe: -rw------- 1 root root ... /opt/joplin/.env

Schritt 3: compose.yaml erstellen

Erstelle die Datei /opt/joplin/compose.yaml mit folgendem Inhalt. Der Stack besteht aus zwei Services: db (PostgreSQL 16) und app (Joplin Server). Der app-Container startet erst, wenn die Datenbank den Healthcheck bestanden hat – das verhindert Verbindungsfehler beim ersten Start.

services:
  db:
    image: postgres:16
    container_name: joplin-db
    restart: unless-stopped
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: ${POSTGRES_DATABASE}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DATABASE}"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - joplin-net

  app:
    image: joplin/server:latest
    container_name: joplin-app
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    ports:
      - "22300:22300"
    environment:
      APP_PORT: 22300
      APP_BASE_URL: ${APP_BASE_URL}
      DB_CLIENT: pg
      POSTGRES_HOST: db
      POSTGRES_PORT: 5432
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DATABASE: ${POSTGRES_DATABASE}
    networks:
      - joplin-net

networks:
  joplin-net:
    driver: bridge

Wichtige Entscheidungen in dieser Konfiguration:

  1. PostgreSQL statt SQLite: DB_CLIENT=pg aktiviert PostgreSQL, das für Multi-User-Betrieb zwingend nötig ist. SQLite ist nur für lokale Tests geeignet und bei Container-Neustart verlustgefährdet.
  2. Kein Expose von Port 5432: Der Datenbank-Port wird bewusst nicht nach außen gemappt – der App-Container erreicht die DB intern über den Service-Namen db im selben Docker-Netzwerk.
  3. Healthcheck + depends_on: Verhindert Race-Conditions beim ersten Start zuverlässig.
  4. Image-Tag latest: Entspricht derzeit Version 3.7.1 (Mai 2026). Für eine reproduzierbare Produktion kannst du auf einen versionierten Tag pinnen, z. B. joplin/server:3.7.1.

Verifizieren: Die YAML-Syntax ist fehlerfrei:

docker compose -f /opt/joplin/compose.yaml config --quiet && echo "YAML OK"
# Erwartete Ausgabe: YAML OK

Schritt 4: Stack starten

Starte den Stack aus dem Projektordner heraus. Docker Compose liest die .env-Datei automatisch ein:

cd /opt/joplin
docker compose up -d

Beim allerersten Start lädt Docker die Images herunter (Joplin Server ca. 537 MB, PostgreSQL ca. 400 MB) und richtet die Datenbank ein. Das kann einige Minuten dauern.

Verifizieren: Beide Container laufen und der db-Container zeigt healthy:

docker compose ps
# Erwartete Ausgabe (gekürzt):
# NAME           IMAGE                  STATUS
# joplin-db      postgres:16            Up ... (healthy)
# joplin-app     joplin/server:latest   Up ...

docker compose logs app --tail=30
# Kein "error", kein "FATAL" – Zeilen wie:
# Joplin Server [info] App listening on port 22300

Erscheint in den Logs ein Fehler wie FATAL: password authentication failed, stimmen die Datenbankpasswörter in der .env nicht überein – Stack stoppen, .env korrigieren, docker compose up -d erneut ausführen.

Schritt 5: Erst-Einrichtung im Browser

Öffne im Browser http://<SERVER-IP>:22300. Du siehst die Anmeldeseite des Joplin Web-Admin. Melde dich mit den Standard-Zugangsdaten an:

  1. E-Mail: admin@localhost
  2. Passwort: admin

Ändere sofort nach dem ersten Login E-Mail-Adresse und Passwort unter Admin > Users > admin. Das Standard-Passwort ist öffentlich bekannt und darf in keiner produktiven Umgebung aktiv bleiben.

Weitere Nutzer kannst du ebenfalls unter Admin > Users anlegen. Jeder Nutzer erhält eine eigene E-Mail-Adresse und ein Passwort, mit dem er sich in den Joplin-Clients authentifiziert.

Verifizieren: Der HTTP-Endpunkt antwortet:

curl -I http://localhost:22300
# Erwartete Ausgabe:
# HTTP/1.1 200 OK  (oder 302 Found bei Redirect zum Login)

Schritt 6: Joplin-Client verbinden

Öffne einen Joplin-Desktop-Client (Windows, macOS oder Linux) und navigiere zu Werkzeuge > Einstellungen > Synchronisierung. Wähle als Synchronisierungsziel Joplin Server und trage folgende Werte ein:

  1. Joplin Server URL: den Wert aus APP_BASE_URL, z. B. http://192.168.1.100:22300
  2. E-Mail: die gerade gesetzte Admin-E-Mail (oder ein neu angelegter Nutzer)
  3. Passwort: das geänderte Passwort

Klicke auf „Synchronisierung prüfen" – der Client meldet eine erfolgreiche Verbindung. Beim nächsten Sync werden alle Notizen auf den Server übertragen.

Für Mobile-Clients (Android / iOS) ist der Weg identisch: Konfiguration > Synchronisierung > Joplin Server.

Verifizieren:

docker compose logs app --tail=10
# Nach dem ersten Client-Sync erscheinen Einträge wie:
# GET /api/items/root:/... 200

Schritt 7: Ende-zu-Ende-Verschlüsselung aktivieren (optional)

E2EE ist eine rein clientseitige Funktion und funktioniert unabhängig vom Sync-Ziel. Aktiviere sie im Desktop-Client unter Werkzeuge > Einstellungen > Verschlüsselung. Joplin verschlüsselt alle Notizen vor dem Upload; der Server selbst sieht nur verschlüsselte Daten. Das Schlüssel-Passwort muss an einem sicheren Ort aufbewahrt werden – geht es verloren, sind alle Notizen unwiederbringlich verschlüsselt.

Verifizieren: Im Client-Menü erscheint unter Verschlüsselung der Status „Aktiviert" und ein Verschlüsselungsschlüssel ist aufgelistet.

Schritt 8: HTTPS mit Reverse-Proxy (empfohlen für Internet-Betrieb)

Joplin Server terminiert selbst kein TLS. Für den Zugriff aus dem Internet ist ein Reverse-Proxy mit SSL-Zertifikat Pflicht – ohne HTTPS werden Anmeldedaten und Notizinhalte unverschlüsselt übertragen. Eine vollständige Anleitung für Caddy (automatisches Let's-Encrypt-Zertifikat) findest du unter Caddy als Reverse Proxy einrichten.

Wichtig: Nach dem Umstieg auf HTTPS muss APP_BASE_URL in der .env-Datei auf die HTTPS-URL aktualisiert werden:

APP_BASE_URL=https://joplin.example.com

Anschließend Stack neu starten:

cd /opt/joplin
docker compose up -d

Verifizieren:

curl -I https://joplin.example.com
# Erwartete Ausgabe: HTTP/2 200 (oder 302)

Updates und Backups

Joplin Server führt Datenbankmigrationen beim Start automatisch durch. Ein Update läuft daher in zwei Befehlen:

cd /opt/joplin
docker compose pull && docker compose up -d

Erstelle vor jedem Update ein Datenbank-Backup:

# PostgreSQL-Dump
docker exec joplin-db pg_dump -U joplin joplindb > /opt/joplin/backup-$(date +%F).sql

# Backup prüfen
ls -lh /opt/joplin/backup-*.sql

Das ./data/postgres-Verzeichnis sollte zusätzlich in eine Backup-Strategie einbezogen werden – docker compose down -v würde Named Volumes löschen, daher werden hier Bind Mounts (Pfad-Volumes) verwendet. Eine vollständige Strategie beschreibt die Anleitung MySQL & PostgreSQL Backup automatisieren mit cron.

Verifizieren nach Update:

docker compose ps
# Beide Container Up/healthy, kein Restart-Loop

docker compose logs app --tail=20
# Keine Migrationsfehler; Zeile: "Joplin Server [info] App listening on port 22300"

Eckdaten auf einen Blick

ParameterWert
Imagejoplin/server:latest (aktuell 3.7.1, Mai 2026)
Begleit-Imagepostgres:16
Image-Größeca. 537 MB
Port (App)22300 (HTTP, Web-UI + API)
Port (DB intern)5432 (nicht nach außen exponieren)
Volume (DB)./data/postgres:/var/lib/postgresql/data
Pflicht-EnvAPP_BASE_URL, DB_CLIENT, POSTGRES_HOST, POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DATABASE
Standard-Loginadmin@localhost / admin (sofort ändern!)
Architekturamd64 (x86_64); ARM64 vor Einsatz prüfen

Troubleshooting / Typische Fehler

  1. „Invalid origin" im Client: APP_BASE_URL stimmt nicht exakt mit der URL überein, die der Client verwendet. Häufige Ursachen: http statt https, fehlender oder falscher Port, ein Trailing-Slash am Ende der URL. Lösung: .env anpassen, Stack neu starten.
  2. App startet nicht / Verbindung zur DB schlägt fehl: Falls depends_on mit Healthcheck fehlt, startet der App-Container bevor PostgreSQL bereit ist. Mit der gezeigten Konfiguration wird das verhindert. Prüfen: docker compose logs db – erscheint database system is ready to accept connections?
  3. FATAL: password authentication failed for user "joplin": POSTGRES_PASSWORD in .env stimmt nicht mit dem beim DB-Init gesetzten Wert überein. Stack vollständig stoppen, ./data/postgres löschen (Datenverlust!), .env korrigieren und neu starten.
  4. Standard-Passwort vergessen zu ändern: admin@localhost / admin sind öffentlich bekannt. Sofort nach dem ersten Login unter Admin > Users ändern.
  5. Port 22300 bereits belegt: Fehlermeldung Bind: address already in use. Prüfen mit ss -tlnp | grep 22300, Prozess stoppen oder externen Port in der compose.yaml anpassen (z. B. 22301:22300).
  6. SQLite statt PostgreSQL aktiv: Wenn DB_CLIENT fehlt oder falsch gesetzt ist, fällt Joplin auf SQLite zurück. Nicht für Multi-User geeignet, und die Datei geht bei docker compose down verloren. Lösung: DB_CLIENT=pg plus alle POSTGRES_*-Variablen setzen.
  7. Kein HTTPS im Internet-Betrieb: Ohne Reverse-Proxy mit TLS werden Anmeldedaten unverschlüsselt übertragen. Caddy, Nginx oder Traefik vorschalten; APP_BASE_URL auf https:// ändern.

Häufige Fragen

Welche Joplin-Clients können sich mit dem selbst gehosteten Server synchronisieren?

Alle offiziellen Clients unterstützen Joplin Server als Sync-Ziel: Desktop (Windows, macOS, Linux), Mobile (Android und iOS) sowie die Terminal-App. In den Sync-Einstellungen wählst du „Joplin Server" aus und trägst die APP_BASE_URL samt Zugangsdaten ein.

Ist Ende-zu-Ende-Verschlüsselung (E2EE) mit dem selbst gehosteten Server möglich?

Ja. E2EE ist eine clientseitige Funktion und funktioniert mit jedem Sync-Ziel – auch mit dem selbst gehosteten Server. Du aktivierst sie in jedem Client unter Einstellungen > Verschlüsselung. Der Server empfängt und speichert dann ausschließlich bereits verschlüsselte Daten und hat keinen Zugriff auf die Klartextinhalte.

Kann ich Notizbücher mit anderen Nutzern teilen?

Ja, das ist eine der Hauptfunktionen gegenüber WebDAV- oder S3-Sync. Im Web-Admin legst du unter Admin > Users weitere Nutzerkonten an. Im Desktop-Client kannst du anschließend ein Notizbuch über die Share-Funktion für andere Nutzer freigeben – änderungen werden bidirektional synchronisiert.

Wie führe ich ein Update auf eine neue Version durch?

Mit docker compose pull && docker compose up -d. Joplin Server führt ausstehende Datenbankmigrationen beim Neustart automatisch aus. Erstelle vorher unbedingt einen PostgreSQL-Dump mit docker exec joplin-db pg_dump -U joplin joplindb > backup.sql.

Was ist der Unterschied zwischen Joplin Server und Joplin Cloud?

Joplin Cloud ist der kommerzielle Hosted-Service von Laurent Cailleux (dem Joplin-Entwickler) – keine eigene Infrastruktur nötig, dafür monatliche Kosten und Datenspeicherung beim Anbieter. Joplin Server ist das selbst gehostete Backend: volle Datenkontrolle, DSGVO-Konformität ohne Auftragsverarbeitungsvertrag mit einem Cloud-Anbieter, aber eigenverantwortliche Wartung und Backups.

Brauche ich den optionalen Transcribe-Service?

Nur wenn du Sprachnotizen automatisch transkribieren möchtest. Der Service (joplin/transcribe:latest) benötigt bis zu 16 GB RAM und 4 CPUs und ist für die meisten Installationen nicht nötig. Die in dieser Anleitung gezeigte vereinfachte Konfiguration ohne Transcribe ist vollständig funktionsfähig.

Fazit

Joplin Server ist eine ausgereifte, aktiv gepflegte Lösung für Teams und Einzelpersonen, die ihre Notizen selbst hosten wollen. Die Docker-Compose-Installation ist in unter 30 Minuten abgeschlossen, und der Stack ist dank PostgreSQL-Healthcheck und Bind-Mounts produktionstauglich. Der wichtigste Schritt nach dem ersten Start: das Standard-Passwort sofort ändern. Wer den Server über das lokale Netz hinaus nutzen möchte, sollte einen Reverse-Proxy mit HTTPS vorschalten – Joplin selbst terminiert kein TLS. Für Container-Sicherheit im Allgemeinen lohnt sich ein Blick auf Docker Compose absichern: Secrets, Healthchecks, Non-Root und Read-Only.

Weiterführende Anleitungen und Quellen

  1. Docker und Docker Compose auf Linux installieren – die Self-Hosting-Grundlage
  2. Caddy als Reverse Proxy einrichten: Anfänger-Anleitung mit automatischem HTTPS
  3. MySQL & PostgreSQL Backup automatisieren mit cron
  4. Docker Compose absichern: Secrets, Healthchecks, Non-Root und Read-Only

Offizielle Quellen: Joplin Server README (GitHub) · Joplin Server auf Docker Hub · Joplin Server Changelog

Passende Anleitungen auf S-EDV

  1. PCI DSS 4.0.1: Checkout-Skripte als Sicherheitsrisiko - was Admins jetzt prüfen
  2. OpenSSL: Neun Schwachstellen im Sicherheitsrelease vom 9. Juni 2026, darunter Hi
  3. Automatische Sicherheitsupdates unter Debian/Ubuntu mit unattended-upgrades rich