Problem setting up Joplin Server / nginx / docker

Hi all,

I hope someone can give me an hint where to search...

I wanted to install Joplin Server directly on my system but can't get it to work. So I thought I try with docker first and with this I propably can install it later without docker.

Result: I'm also not able to get it working with docker.

For other support queries please indicate:

  • The version you are using: 2.7.4
  • The operating system you are using: Debian 11 "bullseye"

I managed to get access to the server (https://xxxxx.xxx/joplin). I get the login-screen and I can click on "forgot my password" and all seems to work. But if I try to log in the first time (admin@localhost, pw admin) I get an error "Invalid URL". If I click on "Go to login page" this is working working again:

Nginx config block for joplin:

    location /joplin/ {
        proxy_pass      http://xxx.xxx.xxx.xxx:22300/;
		proxy_set_header Host $host;
        #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        #proxy_set_header X-Forwarded-Proto $scheme;
        #proxy_set_header X-Forwarded-Port $server_port;
        #proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        client_max_body_size 400M;
    
    	error_log /var/log/nginx/joplin.error.log;

My Docker.env:

# =============================================================================
# PRODUCTION CONFIG EXAMPLE
# -----------------------------------------------------------------------------
# By default it will use SQLite, but that's mostly to test and evaluate the
# server. So you'll want to specify db connection settings to use Postgres.
# =============================================================================
#
 APP_BASE_URL=https://xxxxx.xxx/joplin/
 APP_PORT=22300
# 
 DB_CLIENT=pg
 POSTGRES_PASSWORD=xxxxx
 POSTGRES_DATABASE=joplin
 POSTGRES_USER=joplin
 POSTGRES_PORT=5432
 POSTGRES_HOST=docker

Nginx (access.log, no error log created for this) logfile for my request:

xxx.xxx.xxx.xxx - - [14/Feb/2022:08:27:54 +0100] "POST /joplin/login HTTP/2.0" 500 1870 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xxx.xxx.xxx.xxx Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [14/Feb/2022:08:27:54 +0100] "GET /joplin/css/bulma.min.css HTTP/2.0" 200 206620 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xxx.xxx.xxx.xxx Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [14/Feb/2022:08:27:54 +0100] "GET /joplin/css/main.css HTTP/2.0" 200 1420 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xxx.xxx.xxx.xxx Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [14/Feb/2022:08:27:54 +0100] "GET /joplin/css/fontawesome/css/all.min.css HTTP/2.0" 200 59305 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xxx.xxx.xxx.xxx Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [14/Feb/2022:08:27:54 +0100] "GET /joplin/js/jquery.min.js HTTP/2.0" 200 89501 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xxx.xxx.xxx.xxx Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [14/Feb/2022:08:27:55 +0100] "GET /joplin/js/main.js HTTP/2.0" 200 1280 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xxx.xxx.xxx.xxx Safari/537.36"

Because I use an existing Postgres database I have no docker compose, I only use docker to start the joplin server. I use this commandline:

docker run --name JoplinSRV -p 22300:22300 --rm -d --env-file /root/docker/joplin.env joplin/server:latest

My nginx server is also a different server as the docker-server because it already existed for an other service.

If I run Joplin Server without -d I get this log if I want to log in to the page:

root@docker:/# docker run --name JoplinSRV -p 22300:22300 --rm --env-file /root/docker/joplin.env joplin/server:latest
2022-02-14 07:45:53: App: Starting server v2.7.4 (prod) on port 22300 and PID 6...
2022-02-14 07:45:53: App: NTP time offset: -13ms
2022-02-14 07:45:53: App: Running in Docker: true
2022-02-14 07:45:53: App: Public base URL: https://xxxx.xxx/joplin
2022-02-14 07:45:53: App: API base URL: https://xxxx.xxx/joplin
2022-02-14 07:45:53: App: User content base URL: https://xxxx.xxx/joplin
2022-02-14 07:45:53: App: Log dir: /home/joplin/packages/server/logs
2022-02-14 07:45:53: App: DB Config: {
  client: 'pg',
  name: 'joplin',
  slowQueryLogEnabled: false,
  slowQueryLogMinDuration: 1000,
  autoMigration: true,
  user: 'joplin',
  password: '********',
  port: 5432,
  host: 'docker'
}
2022-02-14 07:45:53: App: Mailer Config: {
  enabled: false,
  host: '',
  port: 465,
  security: 'tls',
  authUser: '',
  authPassword: '********',
  noReplyName: '',
  noReplyEmail: ''
}
2022-02-14 07:45:53: App: Content driver: { type: 1 }
2022-02-14 07:45:53: App: Content driver (fallback): null
2022-02-14 07:45:53: App: Trying to connect to database...
2022-02-14 07:45:53: App: Connection check: {
  latestMigration: { name: '20220201151223_backup_items.js', done: true },
  isCreated: true,
  error: null
}
2022-02-14 07:45:55: App: Auto-migrating database...
2022-02-14 07:45:56: App: Latest migration: { name: '20220201151223_backup_items.js', done: true }
2022-02-14 07:45:56: App: Performing main storage check...
2022-02-14 07:45:56: App: Database storage is special and cannot be checked this way. If the connection to the database was successful then the storage driver should work too.
2022-02-14 07:45:56: App: Starting services...
2022-02-14 07:45:56: ShareService: Starting maintenance...
2022-02-14 07:45:56: EmailService: Service will be disabled because mailer config is not set or is explicitly disabled
2022-02-14 07:45:56: TaskService: Scheduling #1 (Delete expired tokens): 0 */6 * * *
2022-02-14 07:45:56: TaskService: Scheduling #2 (Update total sizes): 0 * * * *
2022-02-14 07:45:56: TaskService: Scheduling #3 (Process oversized accounts): 30 */2 * * *
2022-02-14 07:45:56: TaskService: Scheduling #7 (Compress old changes): 0 0 */2 * *
2022-02-14 07:45:56: TaskService: Scheduling #8 (Process user deletions): 0 */6 * * *
2022-02-14 07:45:56: App: Call this for testing: `curl https://wegele.org/joplin/api/ping`
2022-02-14 07:45:56: ShareService: Maintenance completed in 57ms
2022-02-14 07:49:33: App: GET / (302) (28ms)
2022-02-14 07:49:33: App: GET /login (200) (26ms)
2022-02-14 07:49:34: App: GET /css/bulma.min.css (200) (13ms)
2022-02-14 07:49:34: App: GET /css/fontawesome/css/all.min.css (200) (11ms)
2022-02-14 07:49:34: App: GET /js/jquery.min.js (200) (12ms)
2022-02-14 07:49:34: App: GET /css/main.css (200) (19ms)
2022-02-14 07:49:34: App: GET /js/main.js (200) (16ms)
2022-02-14 07:49:34: App: GET /images/Logo.png (200) (3ms)
2022-02-14 07:49:43: [error] App: Middleware error on /login: TypeError: Invalid URL
    at new NodeError (node:internal/errors:371:5)
    at onParseError (node:internal/url:552:9)
    at new URL (node:internal/url:628:5)
    at acceptOrigin (/home/joplin/packages/server/src/app.ts:122:21)
    at Object.origin (/home/joplin/packages/server/src/app.ts:189:8)
    at cors (/home/joplin/packages/server/node_modules/@koa/cors/index.js:60:24)
    at dispatch (/home/joplin/packages/server/node_modules/koa-compose/index.js:42:32)
    at /home/joplin/packages/server/src/app.ts:181:10
    at Generator.next (<anonymous>)
    at /home/joplin/packages/server/dist/app.js:8:71 {
  input: 'null',
  code: 'ERR_INVALID_URL'
}
2022-02-14 07:49:43: App: GET /css/bulma.min.css (200) (12ms)
2022-02-14 07:49:43: App: GET /css/fontawesome/css/all.min.css (200) (8ms)
2022-02-14 07:49:43: App: GET /js/jquery.min.js (200) (12ms)
2022-02-14 07:49:43: App: GET /css/main.css (200) (20ms)
2022-02-14 07:49:43: App: GET /js/main.js (200) (17ms)

I searched on the net and in this forum but I couldn't find a case similar to this.

Anybody an idea what this error is causing?

If you need any further informations please let me know. Thank you all

Greetings
Dominik

You don't need:

  • the location parameter in Nginx
  • the /joplin in the url (Joplin automatically adds that, so it's just APP_BASE_URL=https://your.domain.com)

While you don't use that, the most surefire way to set this up is still via docker-compose, not running it individually.

If you need that, I'll send you a fully working Nginx reverse proxy config.

Hi ibenny,

thanks for your help. If I remove the /joplin (APP_BASE_URL) i get a 404 from nginx. He redirects to

https://mydomain.com/login

instead of

https://mydomain.com/joplin/login

Why should i don't need the location parameter in nginx? I have several services and they have all its own location block (or more). NextCloud and SeaFile for example. Please let me know so I can learn something...

I don't understand why i can see the login page but then it don't go further...

EDIT: You can send me this nginx-config, maybe I can find something I can use for my case.

EDIT2: If I ping it like described I get this message:

root@docker:/# curl https://xxxxx.xxx/joplin/api/ping
{"status":"ok","message":"Joplin Server is running"}

Greetings
Dominik

Here is the relevant part of the reverse proxy config:

server {
listen 80;
server_name your.domain.com;
return 301 https://your.domain.com$request_uri/login;
}

server {
listen 443 ssl http2;
server_name your.domain.com;

proxy_read_timeout 720s;
proxy_connect_timeout 720s;
proxy_send_timeout 720s;

client_max_body_size 50m;

# Proxy headers
proxy_set_header X-Forwarded-Host $host;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass_header Content-Type;

# SSL parameters
ssl_certificate /etc/letsencrypt/live/your.domain.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/your.domain.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

# log files
access_log /var/log/nginx/your.domain.com.access.log;
error_log /var/log/nginx/your.domain.com.error.log;

# Handle / requests and redirect to a specific port on localhost
location / {
   proxy_redirect off;
   proxy_pass http://127.0.0.1:22300;
}`

The required location parameter is only for the proxy_pass, as you can see, all the previous parameters don't require it. This is my actual config (I changed only the domain) in "production", so if you change the domain to your own one, the Nginx part for you is done.

Just to make sure, my .env is the following (the relevant line), so you can see what I meant:

APP_BASE_URL=https://your.domain.com
#APP_PORT=22300

Edit: I honestly don't see why I couldn't put the whole config block in the proper formatting but I'm sure you can see the point.

1 Like

Thanks again. I tied yesterday evening but I didn't get it to work.

I think we have some different configs in our mind:

I want Joplin Server to run in a subdirectory, not a subdomain. So I want it available in https://mydomain.com/joplin/ and not in https://joplin.mydomain.com/

I think your config for nginx is only possible for a subdomain.

My infrastructure looks like that:

1st server: Nginx, NextCloud and SeaFile are running (fine btw)
2nd server: Picapport is running here, nginx from the first server do the reverse proxy
3rd server: this is for docker and joplin server (the only service at the moment): This should also be proxied by nginx from the first server because he already manages all other services

Or do I have to setup an other nginx server on the docker server for this?

I'm really new with docker...

Greetings and thanks for your help!
Dominik

Thanks. Well, this wasn't explicitly clear from the opening post, but no problem.

However, you would need the reverse proxy running on the server the docker is running on, that's way more managable and "clean", so to speak, a certain level of seperation does wonders in the long run.

So yes, in that case you would need the location but only with /, not with /joplin. The rest is unchanged, the APP_BASE_PORT should still be https:your.domain.com

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.