The way to an own clowd (part 7) - The First Service: Joplin

Notes, to-do lists, ideas, and projects: Our digital everyday life demands a reliable and flexible solution for organizing information.

Joplin is an open-source alternative to Evernote and similar tools, allowing users to keep complete control over their data. What makes it special: Joplin can be fully self-hosted – including synchronization across all devices, Markdown support, and robust encryption options.

This time I show how to install an own backend and the official apps.

For this you should already finished the following tutorials:

Preparations

First, make sure you are logged in as root:

su

Then open the psql tool as the PostgreSQL administrator:

sudo -u postgres psql

In the psql prompt, create a new user joplinuser and the database joplin:

CREATE USER joplinuser WITH PASSWORD '<A-STRONG-PASSWORD>';

CREATE DATABASE joplin WITH OWNER joplinuser;

GRANT ALL PRIVILEGES ON DATABASE joplin TO joplinuser;

Exit the tool afterwards.

For additional security, it is recommended to generate a random port for the Joplin backend:

echo $(shuf -i 1024-65535 -n 1)

This command will output a random number between 1024 and 65535.

Backend Setup

First, create the main directory for Joplin:

mkdir -p /opt/joplin

Next, create a file named docker-compose.yaml in the /opt/joplin directory with the following content:

services:
  joplin-server:
    image: localhost:5000/joplin/server:latest
    container_name: joplin-server
    environment:
      - APP_BASE_URL=https://<CLOUD-DOMAIN>   # Use your domain as configured in nginx
      - DB_CLIENT=pg
      - POSTGRES_PASSWORD=<STRONG_DATABASE_PASSWORD>
      - POSTGRES_DATABASE=joplin
      - POSTGRES_USER=joplinuser
      - POSTGRES_PORT=<POSTGRES_PORT>
      - POSTGRES_HOST=host.docker.internal
    ports:
      - "22300:22300"
    restart: unless-stopped
    extra_hosts:
      - "host.docker.internal:host-gateway"

Because a local Docker registry is used, you can create and push a local copy of the official Joplin image:

docker pull joplin/server:latest

docker tag joplin/server:latest localhost:5000/joplin/server:latest

docker push localhost:5000/joplin/server:latest

To make the service accessible from outside, create a file called /etc/nginx/sites-available/joplin with the following content (make sure to adjust the values!):

server {
    listen <RANDOM_PORT_FROM_ABOVE> ssl;
    server_name <CLOUD-DOMAIN>;

    ssl_certificate /etc/letsencrypt/live/<CLOUD-DOMAIN>/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/<CLOUD-DOMAIN>/privkey.pem;

    # Adjust maximum upload size if needed
    client_max_body_size 512M;

    location / {
        proxy_pass http://localhost:22300;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;
    }
}

To activate the configuration in NGINX, create a symbolic link in /etc/nginx/sites-enabled:

sudo ln -s /etc/nginx/sites-available/joplin /etc/nginx/sites-enabled/

Then restart the service:

sudo systemctl restart nginx

To automatically start Joplin on boot, you need a service configuration saved as /etc/systemd/system/joplin.service:

[Unit]
Description=Joplin Server (Docker Compose)
Requires=docker.service
After=docker.service

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/joplin
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down

[Install]
WantedBy=multi-user.target

Finally, run these commands to set up Joplin as a background service:

sudo systemctl daemon-reload

sudo systemctl enable joplin

sudo systemctl start joplin

The service is available when you can access https://<CLOUD-DOMAIN>:<JOPLIN-PORT> in your browser and see the login screen.

The default username is usually admin@localhost with the password admin. It is strongly recommended to change the password immediately after your first login!

Setting up the Apps

Joplin provides dedicated apps for mobile devices and Mac: https://joplinapp.org/download/

To connect your device:

  1. Open the Joplin app on your device and go to SettingsSynchronization
  2. Select Joplin Server as the target
  3. Enter the following URL: https://<CLOUD-DOMAIN>:<JOPLIN-PORT> (e.g. https://cloud.my-name.eu:22300)
  4. Enter your Joplin server username and password
  5. Start synchronization

Conclusion

With Joplin, you can set up a powerful, secure, and flexible note-taking solution on your own server.

Setup with Docker is quick, operation is reliable, and with the matching apps for every device, your notes are always in sync and under your control.

Quick & Dirty

# your cloud domain and Postgres passwort
CLOUD_DOMAIN=cloud.example.com
STRONG_PASSWORD='CHOOSE_A_STRONG_PASSWORD'

# create Joplin Postgres user & database
sudo -u postgres psql <<EOF
CREATE USER joplinuser WITH PASSWORD '$STRONG_PASSWORD';
CREATE DATABASE joplin WITH OWNER joplinuser;
GRANT ALL PRIVILEGES ON DATABASE joplin TO joplinuser;
\\q
EOF

# 2. setup Joplin directory and docker-compose.yaml
sudo mkdir -p /opt/joplin
sudo tee /opt/joplin/docker-compose.yaml >/dev/null <<EOC
services:
  joplin-server:
    image: joplin/server:latest
    container_name: joplin-server
    environment:
      - APP_BASE_URL=https://$CLOUD_DOMAIN
      - DB_CLIENT=pg
      - POSTGRES_PASSWORD=$STRONG_PASSWORD
      - POSTGRES_DATABASE=joplin
      - POSTGRES_USER=joplinuser
      - POSTGRES_PORT=5432
      - POSTGRES_HOST=host.docker.internal
    ports:
      - "22300:22300"
    restart: unless-stopped
    extra_hosts:
      - "host.docker.internal:host-gateway"
EOC

# setup NGINX reverse proxy
sudo tee /etc/nginx/sites-available/joplin >/dev/null <<EONG
server {
    listen 443 ssl;
    server_name $CLOUD_DOMAIN;

    ssl_certificate /etc/letsencrypt/live/$CLOUD_DOMAIN/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/$CLOUD_DOMAIN/privkey.pem;

    client_max_body_size 512M;
    location / {
        proxy_pass http://localhost:22300;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
EONG

sudo ln -sf /etc/nginx/sites-available/joplin /etc/nginx/sites-enabled/joplin
sudo systemctl reload nginx

# setup systemd service
sudo tee /etc/systemd/system/joplin.service >/dev/null <<EOSD
[Unit]
Description=Joplin Server (Docker Compose)
Requires=docker.service
After=docker.service

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/joplin
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down

[Install]
WantedBy=multi-user.target
EOSD

# enable and start joplin service
sudo systemctl daemon-reload
sudo systemctl enable joplin
sudo systemctl start joplin