# 🌐 Joplin Web – Self-Hosted Docker Eine selbst gehostete **Joplin Web App**, die den offiziellen Joplin-Web-Client aus dem Joplin-Quellcode baut und anschließend über einen schlanken nginx-Container bereitstellt. Die Web-App kann mit einem vorhandenen **Joplin Server** synchronisiert werden und ermöglicht den Zugriff auf Joplin-Notizen direkt über den Browser. > ⚠️ Joplin Web befindet sich noch in Entwicklung/Beta. Vor produktivem Einsatz sollten Backups der Joplin-Daten vorhanden sein. --- ## ✨ Features - 🌐 Joplin direkt im Webbrowser - 🐳 Vollständig über Docker betreibbar - 🔄 Synchronisation mit einem eigenen Joplin Server - 🔐 Betrieb über HTTPS / Reverse Proxy - 🗄️ Browserbasierter lokaler Speicher - 📎 Unterstützung der Joplin-Synchronisation - 🚀 Fertiges Docker-Image kann über eine Container Registry verteilt werden - 🛡️ Zusätzlicher Zugriffsschutz über den Reverse Proxy möglich --- ## 🏗️ Aufbau ```text ┌──────────────────────┐ │ Browser │ │ Firefox / Chrome ... │ └──────────┬───────────┘ │ HTTPS │ ▼ ┌──────────────────────┐ │ Reverse Proxy │ │ nginx / NPM / etc. │ └──────────┬───────────┘ │ ▼ ┌──────────────────────┐ │ Joplin Web │ │ nginx + Web App │ │ Port 3000 │ └──────────┬───────────┘ │ HTTPS / API │ ▼ ┌──────────────────────┐ │ Joplin Server │ │ Sync + Benutzer │ └──────────────────────┘ ``` --- # 📁 Projektstruktur ```text joplin-web/ ├── Dockerfile ├── docker-compose.yml ├── nginx.conf ├── README.md └── .gitignore ``` --- # 🚀 Installation ## Variante 1 – Fertiges Container-Image Wenn das Image bereits in einer Container Registry vorhanden ist, wird auf dem Zielserver kein Joplin-Build benötigt. Beispiel: ```yaml services: joplin-web: image: gitea.example.com/username/joplin-web:latest container_name: joplin-web restart: unless-stopped ports: - "3000:80" security_opt: - no-new-privileges:true ``` Container starten: ```bash docker compose pull docker compose up -d ``` Status prüfen: ```bash docker compose ps ``` Logs anzeigen: ```bash docker logs -f joplin-web ``` Die Web-App ist anschließend lokal unter folgendem Port erreichbar: ```text http://SERVER-IP:3000 ``` > Für den produktiven Betrieb sollte Joplin Web über **HTTPS** bereitgestellt werden. --- # 🔨 Variante 2 – Image selbst bauen Repository klonen: ```bash git clone https://gitea.example.com/username/joplin-web.git cd joplin-web ``` Image bauen: ```bash docker compose build --no-cache --progress=plain ``` Anschließend starten: ```bash docker compose up -d ``` Status prüfen: ```bash docker compose ps ``` --- # 🐳 Docker Image Das Docker-Image wird in mehreren Stufen gebaut. ## Builder Im ersten Schritt wird der offizielle Joplin-Quellcode geladen und die Web-App gebaut. Dabei werden unter anderem verwendet: - Devbox - Nix - Node.js - Yarn - Joplin Source - Joplin Mobile Web Build Der Web-Build wird anschließend aus folgendem Verzeichnis übernommen: ```text packages/app-mobile/web/dist ``` ## Runtime Der eigentliche produktive Container benötigt die Build-Umgebung nicht mehr. Das fertige Web-Frontend wird über: ```text nginx ``` bereitgestellt. Dadurch bleibt der laufende Container wesentlich kleiner als die Build-Umgebung. --- # 🔐 HTTPS Joplin Web sollte nicht dauerhaft über eine unverschlüsselte HTTP-Verbindung verwendet werden. Bestimmte Browser-APIs, die Joplin Web benötigt, stehen nur in einem **Secure Context** zur Verfügung. Empfohlen: ```text https://joplin-web.example.com ``` Der Reverse Proxy kann intern weiterhin auf: ```text http://JOPLIN-WEB-IP:3000 ``` weiterleiten. --- # 🧠 Cross-Origin Isolation Für bestimmte Browser-Funktionen werden folgende HTTP-Header ausgeliefert: ```text Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: require-corp Cross-Origin-Resource-Policy: same-origin ``` Überprüfen: ```bash curl -I http://127.0.0.1:3000 ``` Beispiel: ```text HTTP/1.1 200 OK Server: nginx Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: require-corp Cross-Origin-Resource-Policy: same-origin ``` Im Browser kann zusätzlich geprüft werden: ```javascript window.isSecureContext ``` und: ```javascript crossOriginIsolated ``` --- # 🔄 Verbindung mit Joplin Server In Joplin Web unter der Synchronisationskonfiguration als Ziel **Joplin Server** auswählen. Als Server-URL wird ausschließlich die Basis-URL verwendet: ```text https://joplin.example.com ``` Nicht: ```text https://joplin.example.com/api ``` und nicht: ```text https://joplin.example.com/login ``` Anschließend die Zugangsdaten des Joplin-Benutzers eintragen. --- # 🌍 CORS bei eigenem Joplin Server Wenn Joplin Web und Joplin Server unterschiedliche Domains verwenden, muss der Browser Cross-Origin-Anfragen zulassen. Beispiel: ```text Joplin Web: https://joplin-web.example.com Joplin Server: https://joplin.example.com ``` Ohne entsprechende Konfiguration können Fehler auftreten wie: ```text Blocked by CORS policy ``` oder: ```text Cross-Origin Request Blocked ``` ## Beispiel für nginx / Nginx Proxy Manager Auf dem Reverse Proxy des **Joplin Servers**: ```nginx proxy_hide_header Access-Control-Allow-Origin; proxy_hide_header Access-Control-Allow-Methods; proxy_hide_header Access-Control-Allow-Headers; more_set_headers "Access-Control-Allow-Origin: https://joplin-web.example.com"; more_set_headers "Access-Control-Allow-Methods: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS"; more_set_headers "Access-Control-Allow-Headers: Content-Type, Authorization, X-API-MIN-VERSION, X-API-AUTH"; more_set_headers "Vary: Origin"; ``` > Die Domain muss an die eigene Joplin-Web-Adresse angepasst werden. --- ## CORS testen Preflight-Anfrage: ```bash curl -i -X OPTIONS \ -H "Origin: https://joplin-web.example.com" \ -H "Access-Control-Request-Method: PUT" \ -H "Access-Control-Request-Headers: content-type,x-api-min-version,x-api-auth" \ https://joplin.example.com/api/items/root:/testing.txt:/content ``` Die Antwort sollte unter anderem enthalten: ```text Access-Control-Allow-Origin: https://joplin-web.example.com Access-Control-Allow-Methods: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization, X-API-MIN-VERSION, X-API-AUTH ``` --- # 🔒 Zusätzlicher Zugriffsschutz Da die Web-App grundsätzlich über ihre URL erreichbar ist, empfiehlt sich ein zusätzlicher Schutz auf Reverse-Proxy-Ebene. Mögliche Lösungen: - HTTP Basic Authentication - Nginx Proxy Manager Access Lists - Authelia - Authentik - VPN / WireGuard / NetBird Die Architektur sieht dann beispielsweise so aus: ```text Internet │ ▼ Reverse Proxy │ ├── Authentifizierung │ ▼ Joplin Web │ ▼ Joplin Server ``` Die eigentlichen Joplin-Zugangsdaten werden weiterhin für die Synchronisation mit dem Joplin Server verwendet. --- # 🔄 Update ## Bei Verwendung eines fertigen Images ```bash docker compose pull docker compose up -d ``` Nicht mehr verwendete Images können anschließend entfernt werden: ```bash docker image prune ``` --- ## Bei lokalem Build Repository aktualisieren: ```bash git pull ``` Danach neu bauen: ```bash docker compose build --no-cache docker compose up -d ``` --- # 📦 Image in eine Gitea Container Registry pushen Zunächst anmelden: ```bash docker login gitea.example.com ``` Image taggen: ```bash docker tag joplin-web-joplin-web:latest \ gitea.example.com/username/joplin-web:latest ``` Danach: ```bash docker push gitea.example.com/username/joplin-web:latest ``` Auf einem anderen Docker-Server genügt anschließend: ```bash docker login gitea.example.com docker compose pull docker compose up -d ``` Damit muss Joplin auf dem Zielserver **nicht erneut aus dem Quellcode gebaut werden**. --- # 🧹 Nützliche Docker-Befehle Container anzeigen: ```bash docker compose ps ``` Logs: ```bash docker logs -f joplin-web ``` Container neu starten: ```bash docker compose restart ``` Container stoppen: ```bash docker compose down ``` Container starten: ```bash docker compose up -d ``` Image neu bauen: ```bash docker compose build --no-cache ``` --- # 🛠️ Fehlerbehebung ## Web-App lädt nicht Container prüfen: ```bash docker compose ps ``` Logs: ```bash docker logs joplin-web --tail 100 ``` Webserver lokal testen: ```bash curl -I http://127.0.0.1:3000 ``` --- ## Synchronisation schlägt fehl Browser-Entwicklertools öffnen und unter **Console** bzw. **Network** nach Fehlern suchen. Häufige Ursachen: - falsche Joplin-Server-URL - CORS - fehlender `X-API-AUTH` Header - Reverse-Proxy-Konfiguration - Zertifikatsprobleme --- ## Browser zeigt alten Stand Joplin Web verwendet Browser-Speicher und Service Worker. Bei Problemen: 1. Alle Joplin-Web-Tabs schließen. 2. Website-Daten der Joplin-Web-Domain löschen. 3. Browser vollständig schließen. 4. Browser neu starten. 5. Joplin Web erneut öffnen. --- # ⚠️ Hinweise Joplin Web speichert lokale Client-Daten im Browser. Daher gilt: - Browserdaten nicht unüberlegt löschen. - Synchronisation vor größeren Änderungen vollständig durchführen. - Regelmäßige Backups des Joplin Servers erstellen. - Die Web-App möglichst nur über HTTPS betreiben. - Für öffentlich erreichbare Installationen zusätzlichen Zugriffsschutz verwenden. --- # 🔗 Links - Joplin: https://joplinapp.org/ - Joplin GitHub: https://github.com/laurent22/joplin - Joplin Build Documentation: https://joplinapp.org/help/dev/BUILD/ - Docker: https://www.docker.com/ - Gitea: https://about.gitea.com/ --- # 📜 Lizenz Dieses Repository enthält die Docker-/Deployment-Konfiguration für Joplin Web. Joplin selbst ist ein eigenständiges Open-Source-Projekt. Für Joplin gelten die Lizenzbedingungen des offiziellen Joplin-Projekts. --- ## ❤️ Credits [Joplin](https://joplinapp.org/) wird von Laurent Cozic und der Joplin-Community entwickelt. Dieses Repository stellt lediglich eine Docker-basierte Möglichkeit bereit, den Joplin-Web-Client selbst zu bauen und zu hosten.