I’ve recently migrated from Anytype to Joplin for my note taking and wanted to self host the cloud server. Because I currently host my website through GitHub Pages but wanted to use a subdomain to get it

Thanks to the docker compose sample file as well as this and this video from DB Tech, I was able to setup my Joplin Server on Portainer

Here is the official Joplin server guide

I used the sample docker compose file mentioned earlier and modified it to my preferences. Change all fields that use a place holder name prefacing with $. Note that the ${APP_BASE_URL} requires you to include http or https depending on which protocol you’re using.

version: '3'

services:
    db:
        image: postgres:15
        volumes:
            - ./docker/joplin/db:/var/lib/postgresql/data
        ports:
            - "5432:5432"
        restart: unless-stopped
        environment:
            - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
            - POSTGRES_USER=${POSTGRES_USER}
            - POSTGRES_DB=${POSTGRES_DATABASE}
    app:
        image: joplin/server:latest
        depends_on:
            - db
        ports:
            - "22300:22300"
        restart: unless-stopped
        environment:
            - APP_PORT=22300
            - APP_BASE_URL=${APP_BASE_URL}
            - DB_CLIENT=pg
            - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
            - POSTGRES_DATABASE=${POSTGRES_DATABASE}
            - POSTGRES_USER=${POSTGRES_USER}
            - POSTGRES_PORT=5432
            - POSTGRES_HOST=db

Instead of using a reverse proxy, I used Cloudflare’s Zero Trust tunneling access to make it easier. Create an account and add your payment info after selecting the free plan.

  1. Log into your domain registrar
  2. Remove current nameservers
  3. Add Cloudflare’s provided nameservers
  4. Go to Cloudflare Zero Trust dashboard
  5. Click Access, then tunnels
  6. Create a tunnel
  7. Give it a name
  8. Install a connector, click on Docker
  9. Copy the given command that looks like this:
  • Note that I added the -d flag so that it runs in the background of your terminal
docker run -d cloudflare/cloudflared:latest tunnel --no-autoupdate run --token $token
  1. Fill in the fields for the public hostname for your chosen domain
    1. For the URL, write the internal IP with the port, for example: http://192.168.0.101:22300

Once the tunnel is successfully created, you should be able to access your Joplin instance from the internet!

Configuring Admin Page

Following the official guide we access our instance and login using the given credentials:

username: admin@localhost
password: admin

Now that you’ve logged in, go and change the credentials to something genuinely secure. If you’re like me and did not configure the email setup, when you get the confirmation email you can actually see the email by going into the Admin panel and selecting Emails where you will see the email. From here you can retrieve the verificaiton link.

Now we follow the instructions written on the Joplin home page of our login to setup our connection to the server. The only thing to mention is that the self hosting option is a bit hidden in smaller print when you’re selecting the Synchronize button. Make sure to select that option and not the Joplin Cloud/Dropbox/OneDrive options.

Now you’re setup! I would suggest to checkout the End-to-End Encryption docs for enabling E2EE. It is a great way to improve your note security. Just note that if you plan to share a notebook with E2EE enabled (for both you and your partner), you will need your partners decryption key and you will need to share your encryption key.

Issues that I encountered

With the the containers for the database and the app running just fine, I had the following error from the database container:

2023-08-01 06:58:21.839 UTC [1] LOG:  database system is ready to accept connections
2023-08-01 06:58:22.617 UTC [70] ERROR:  relation "knex_migrations" does not exist at character 20
2023-08-01 06:58:22.617 UTC [70] STATEMENT:  select "name" from "knex_migrations" order by "id" desc limit $1

From there the app returns the following error:

2023-08-01 07:01:21: [error] App: 404: GET /: ::ffff:192.168.0.108: Invalid origin: http://192.168.0.102:22300
2023-08-01 07:01:21: App: GET / (404) (5ms)

After speaking with some friends and reaching out to the community, I realized that the cause for this was when I set the scheme to HTTPS instead of HTTP in my Cloudflare tunnel. Once I changed that setting I was able to access the Joplin server.