homelab-codex-ws/docs/joplin-server.md

6.2 KiB

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:

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:

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:

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:

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:

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.