Author: Robert

  • Joplin Server with Podman and Quadlets (2025 Edit)

    Joplin Server with Podman and Quadlets (2025 Edit)

    Prepare Environment

    The /tmp folder needs to be mounted on tmpfs (or ramfs…)

    sudo systemctl enable --now tmp.mount
    

    Open port in software firewall

    sudo firewall-cmd --permanent --add-port 22300/tcp
    sudo firewall-cmd --reload
    

    Create joplin user and add subgid and subuid values. The range size below is probably not really necessary…

    sudo useradd -m -c "Joplin Container User" joplin<br>sudo usermod --add-subuids 100000-165536 --add-subgids 100000-165536
    

    Create central storage for sync data (adjust for your environment)

     sudo mkdir -p /appdata/joplin
     sudo chown -R joplin:joplin /appdata/joplin
     sudo chmod 2777 /appdata/joplin
     sudo semanage fcontext -a container_file_t "/appdata/joplin(/.*)?"
     sudo restorecon -Rv /appdata/joplin/
    

    Login as joplin user. Note you can not use su here because it will require a login shell. Alternately you can

    sudo machinectl shell joplin@
    mkdir -p ~/.config/containers/systemd  # For Quadlet files
    mkdir -p ~/cvols/postgres              # For database
    

    Set up reverse proxy (optional) if you don’t want to expose the port

    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;
    
    location /joplin/ {
        proxy_redirect off;
        rewrite	^/joplin/(.*)$ /$1 break;
        proxy_pass http://127.0.0.1:22300/joplin;
    }
    

    Create Podman secrets via environment variables

    export POSTGRES_PASSWORD='blah'
    export POSTGRES_USER='blah'
    export MAILER_AUTH_PASSWORD='blah'
    export MAILER_AUTH_USER='blah'
    podman secret create mailer_auth_password --env MAILER_AUTH_PASSWORD
    podman secret create mailer_auth_user --env MAILER_AUTH_USER
    podman secret create postgres_password --env POSTGRES_PASSWORD
    podman secret create postgres_user --env POSTGRES_USER
    

    Or Create Podman secrets using echo

    echo -n 'blah' | podman secret create mailer_auth_password -
    echo -n 'blah' | podman secret create mailer_auth_user -
    echo -n 'blah' | podman secret create postgres_password -
    echo -n 'blah' | podman secret create postgres_user -
    

    Either of the methods run the risk of your password being in your shell history. Either clear your history when done, or configure your history to ignore echo and export lines, or ignore lines starting with a space and preface all commands with a space.

    Quadlet Setup

    Create three files in your ~/.config/containers/systemd folder

    The jsync.network file contents (alter to suit your needs)

    # jsync.network
    [Network]
    Subnet=192.168.30.0/24
    Gateway=192.168.30.1
    Label=app=joplin
    

    The jsync_app.container file (adjust for your environment, per what you created above)
    Note: Replace myserver, smtp_server with your server name and your smtp server name respectively.

    # jsync_app.container
    [Unit]
    Requires=jsync_db.service
    After=jsync_db.service
    
    [Container]
    Environment=APP_PORT=22300
    Environment=APP_BASE_URL='http://myserver/joplin'
    Environment=DB_CLIENT=pg
    Environment=POSTGRES_DATABASE='joplin'
    Environment=POSTGRES_PORT=5432
    Environment=POSTGRES_HOST='myserver'
    Environment=MAILER_ENABLED=1
    Environment=MAILER_HOST='smtp_server'
    Environment=MAILER_PORT=587
    Environment=MAILER_SECURITY='starttls'
    Environment=MAILER_NOREPLY_NAME='Joplin'
    Environment=MAILER_NOREPLY_EMAIL='noreply@localhost'
    Environment=STORAGE_DRIVER='Type=Filesystem; Path=/sync_data'
    Environment=STORAGE_DRIVER_FALLBACK='Type=Database; Mode=ReadAndClear'
    Image=docker.io/joplin/server:latest
    PublishPort=22300:22300
    Volume=/appdata/joplin:/sync_data:z
    Network=jsync.network
    Secret=postgres_password,type=env,target=POSTGRES_PASSWORD
    Secret=mailer_auth_password,type=env,target=MAILER_AUTH_PASSWORD
    Secret=mailer_auth_user,type=env,target=MAILER_AUTH_USER
    Secret=postgres_user,type=env,target=POSTGRES_USER
    
    [Service]
    Restart=always
    
    [Install]
    WantedBy=multi-user.target default.target
    ```
    

    The jysnc_db.container file (adjust per your environment per what you created above)

    # jsync_db.container
    [Container]
    Environment=POSTGRES_DB='joplin'
    Image=docker.io/postgres:16
    PublishPort=5432:5432
    Volume=/home/joplin/cvol/postgres:/var/lib/postgresql/data:z
    Secret=postgres_password,type=env,target=POSTGRES_PASSWORD
    Secret=postgres_user,type=env,target=POSTGRES_USER
    Network=jsync.network
    
    [Service]
    Restart=always
    

    Now update systemctl

    systemctl --user daemon-reload
    systemctl --user start jsync_app.service
    

    If you get invalid origin error and are running selinux, you may need to

    setsebool httpd_can_network_connect true