579 lines
11 KiB
Markdown
579 lines
11 KiB
Markdown
# 🌐 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.
|