178 lines
6.2 KiB
Markdown
178 lines
6.2 KiB
Markdown
|
|
# Joplin Server
|
||
|
|
|
||
|
|
## Description
|
||
|
|
|
||
|
|
This page documents the current Joplin Server state on the Hetzner VPS.
|
||
|
|
|
||
|
|
Joplin Server is running on the VPS and is reachable through Nginx Proxy Manager when requests resolve to the VPS IP.
|
||
|
|
|
||
|
|
## Current configuration
|
||
|
|
|
||
|
|
- Compose path: `/home/dockeruser/docker/joplin-server`
|
||
|
|
- Files:
|
||
|
|
- `/home/dockeruser/docker/joplin-server/docker-compose.yml`
|
||
|
|
- `/home/dockeruser/docker/joplin-server/.env`
|
||
|
|
- `/home/dockeruser/docker/joplin-server/README.md`
|
||
|
|
- Current runtime state: running
|
||
|
|
- `docker compose ps` in `/home/dockeruser/docker/joplin-server` shows:
|
||
|
|
- `joplin-db`: healthy
|
||
|
|
- `joplin-server`: up, bound to `127.0.0.1:22300`
|
||
|
|
- Intended public URL: `https://joplin.okit.pl`
|
||
|
|
|
||
|
|
Current DNS issue:
|
||
|
|
|
||
|
|
- `joplin.okit.pl` currently returns `CNAME okit.pl`, but no valid A or AAAA answer.
|
||
|
|
- Let's Encrypt failed with: `no valid A records found for joplin.okit.pl; no valid AAAA records found for joplin.okit.pl`.
|
||
|
|
- DNS needs to be fixed before normal public HTTPS works.
|
||
|
|
|
||
|
|
Fixes applied on 2026-04-15:
|
||
|
|
|
||
|
|
- Recreated the Joplin compose stack so `joplin-db` used the current Postgres 18 mount layout.
|
||
|
|
- Confirmed the Joplin `.env` password is no longer the placeholder.
|
||
|
|
- Joplin app started successfully and auto-migrated the database.
|
||
|
|
- Updated Nginx Proxy Manager's `proxy_host` database row for `joplin.okit.pl` to forward to `http://127.0.0.1:22300`, with websockets and block-exploits enabled.
|
||
|
|
- Manually updated active NPM config at `/home/dockeruser/docker/npm/data/nginx/proxy_host/1.conf` to use `127.0.0.1:22300`, because this NPM instance did not regenerate the config from SQLite on restart.
|
||
|
|
- Reloaded nginx successfully.
|
||
|
|
|
||
|
|
Successful tests on 2026-04-15:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
curl -sS http://127.0.0.1:22300/api/ping -H 'Host: joplin.okit.pl'
|
||
|
|
# {"status":"ok","message":"Joplin Server is running"}
|
||
|
|
|
||
|
|
curl -sS http://127.0.0.1/api/ping -H 'Host: joplin.okit.pl'
|
||
|
|
# {"status":"ok","message":"Joplin Server is running"}
|
||
|
|
|
||
|
|
curl -sS --resolve joplin.okit.pl:80:135.181.153.108 http://joplin.okit.pl/api/ping
|
||
|
|
# {"status":"ok","message":"Joplin Server is running"}
|
||
|
|
```
|
||
|
|
|
||
|
|
Laptop-side diagnostics on 2026-04-15:
|
||
|
|
|
||
|
|
- Direct test to Joplin over Tailscale:
|
||
|
|
- Command: `curl -v --connect-timeout 5 http://100.95.58.48:22300`
|
||
|
|
- Result: connection refused.
|
||
|
|
- Observed source address: `100.121.168.72`.
|
||
|
|
- TCP test to SSH over Tailscale:
|
||
|
|
- Command: `nc -vz 100.95.58.48 22`
|
||
|
|
- Result: connection succeeded.
|
||
|
|
- TCP test to Joplin over Tailscale:
|
||
|
|
- Command: `nc -vz 100.95.58.48 22300`
|
||
|
|
- Result: connection refused.
|
||
|
|
- Classical SSH test:
|
||
|
|
- Command: `ssh -o BatchMode=yes -o ConnectTimeout=5 dockeruser@100.95.58.48 true`
|
||
|
|
- Result: local SSH client refused to run because `/etc/ssh/ssh_config.d/20-systemd-ssh-proxy.conf` has bad owner or permissions.
|
||
|
|
- Classical SSH test with global config bypassed:
|
||
|
|
- Command: `ssh -F /dev/null -o BatchMode=yes -o ConnectTimeout=5 dockeruser@100.95.58.48 true`
|
||
|
|
- Result: `Permission denied (publickey,password).`
|
||
|
|
- Tailscale SSH wrapper test:
|
||
|
|
- Command: `tailscale ssh dockeruser@100.95.58.48 true`
|
||
|
|
- Result: host key verification failed because no ED25519 host key is known for `ubuntu-4gb-hel1-1.tailedf7b1.ts.net`.
|
||
|
|
- SSH tunnel was not established from the laptop during this test because SSH authentication or host-key verification was not completed.
|
||
|
|
|
||
|
|
## Known facts
|
||
|
|
|
||
|
|
Joplin Compose design:
|
||
|
|
|
||
|
|
- `app`
|
||
|
|
- `image: joplin/server:latest`
|
||
|
|
- `container_name: joplin-server`
|
||
|
|
- `restart: unless-stopped`
|
||
|
|
- `env_file: .env`
|
||
|
|
- Binds only to localhost:
|
||
|
|
- `127.0.0.1:22300:22300`
|
||
|
|
- Depends on `db` with condition `service_healthy`
|
||
|
|
- Network: `joplin-net`
|
||
|
|
- `db`
|
||
|
|
- `image: postgres:18`
|
||
|
|
- `container_name: joplin-db`
|
||
|
|
- `restart: unless-stopped`
|
||
|
|
- No exposed ports
|
||
|
|
- Network: `joplin-net`
|
||
|
|
- Volume:
|
||
|
|
- `postgres_data:/var/lib/postgresql`
|
||
|
|
- Healthcheck:
|
||
|
|
- `pg_isready` using `POSTGRES_USER` and `POSTGRES_DB`
|
||
|
|
- Named volume:
|
||
|
|
- `joplin_postgres_data`
|
||
|
|
- Named network:
|
||
|
|
- `joplin-net`
|
||
|
|
|
||
|
|
Joplin `.env`:
|
||
|
|
|
||
|
|
```env
|
||
|
|
POSTGRES_PASSWORD=<set on VPS; not placeholder>
|
||
|
|
POSTGRES_USER=joplin
|
||
|
|
POSTGRES_DB=joplin
|
||
|
|
APP_PORT=22300
|
||
|
|
APP_BASE_URL=https://joplin.okit.pl
|
||
|
|
DB_CLIENT=pg
|
||
|
|
POSTGRES_HOST=db
|
||
|
|
POSTGRES_PORT=5432
|
||
|
|
```
|
||
|
|
|
||
|
|
Important notes from handoff:
|
||
|
|
|
||
|
|
- `POSTGRES_PASSWORD` has been changed from the original placeholder.
|
||
|
|
- Joplin is intentionally localhost-only.
|
||
|
|
- External access must go through Nginx Proxy Manager.
|
||
|
|
- Because Nginx Proxy Manager uses host networking, Nginx Proxy Manager should forward to `127.0.0.1:22300`.
|
||
|
|
- PostgreSQL is internal-only and should not be exposed publicly.
|
||
|
|
|
||
|
|
Required Nginx Proxy Manager proxy host for Joplin:
|
||
|
|
|
||
|
|
- Domain Names: `joplin.okit.pl`
|
||
|
|
- Scheme: `http`
|
||
|
|
- Forward Hostname / IP: `127.0.0.1`
|
||
|
|
- Forward Port: `22300`
|
||
|
|
- Websockets Support: enabled
|
||
|
|
- Block Common Exploits: enabled
|
||
|
|
- SSL:
|
||
|
|
- Request Let's Encrypt certificate
|
||
|
|
- Force SSL enabled
|
||
|
|
- HTTP/2 enabled
|
||
|
|
|
||
|
|
DNS plan from handoff:
|
||
|
|
|
||
|
|
- Create A record:
|
||
|
|
- `joplin.okit.pl -> 135.181.153.108`
|
||
|
|
- Optional AAAA record:
|
||
|
|
- `joplin.okit.pl -> 2a01:4f9:c014:98f0::1`
|
||
|
|
- For normal Let's Encrypt through Nginx Proxy Manager, ports `80` and `443` must reach this VPS publicly.
|
||
|
|
- Public DNS should not point to the Tailscale IP if using standard Let's Encrypt HTTP validation.
|
||
|
|
|
||
|
|
Commands provided in handoff to start Joplin:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
cd /home/dockeruser/docker/joplin-server
|
||
|
|
nano .env
|
||
|
|
# replace POSTGRES_PASSWORD
|
||
|
|
docker compose up -d
|
||
|
|
docker compose ps
|
||
|
|
docker compose logs -f app
|
||
|
|
```
|
||
|
|
|
||
|
|
Local tests on VPS after Joplin start:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
curl -I http://127.0.0.1:22300
|
||
|
|
curl -I http://127.0.0.1:81
|
||
|
|
curl -I http://127.0.0.1:80
|
||
|
|
```
|
||
|
|
|
||
|
|
Public tests after DNS and Nginx Proxy Manager config:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
dig joplin.okit.pl
|
||
|
|
curl -I https://joplin.okit.pl
|
||
|
|
```
|
||
|
|
|
||
|
|
## Unknown / needs clarification
|
||
|
|
|
||
|
|
- Whether Nginx Proxy Manager will preserve the manual generated-conf fix after future UI edits/restarts. The SQLite row is correct, but the active generated config did not update automatically during this fix.
|
||
|
|
- Whether `joplin.okit.pl` DNS has been created or fixed.
|
||
|
|
- Whether the optional AAAA record is intended.
|
||
|
|
- Whether Let's Encrypt certificate issuance has succeeded.
|
||
|
|
- Whether the laptop has a valid SSH key or password for `dockeruser@100.95.58.48`.
|
||
|
|
- Whether the Tailscale SSH host key for `ubuntu-4gb-hel1-1.tailedf7b1.ts.net` should be accepted on the laptop.
|