Radet Hannibal-5

Dead Simple Deployment: NoteDiscovery

verified on NoteDiscovery v0.7.6

Recently I've been quite excited by a new open source note taking application called NoteDiscovery. It has a ton of features already and seems to be growing daily at this point thanks to the dedication of its developer Guillermo Villar. The application is self-hosted and super easy to deploy thanks to the excellent docker setup and documentation.

Follow along with my tutorial here to get up and running in about 30 minutes!

Stack Overview

OSUbuntu 24.04
Proxy ServerCaddy
HostingDigitalocean

For this tutorial I'm going to assume you've got a fairly fresh Ubuntu 24.04 installation running. You're free to use any hosting you like; I went with digitalcoean simply because I'm already familiar with their services and their droplets are cheap and easy to spin up. You can also just run the docker container on your local machine, but I wanted to be able to access my notes on-the-go. If you want the same you're in the right place!

Caddy reverse proxy

I went with Caddy as my reverse proxy server because it does everything I need and is very easy to set up. It even handles setting up your SSL certificates automatically! Nginx and even Apache are also options, but require quite a bit more configuration to get up and running safely.

Install caddy

apt install caddy
BASH

Configure the proxy by editing /etc/caddy/Caddyfile. replace [exernal url] with a domain/subdomain pointed to your server. By default NoteDiscovery runs on port 8000, so replace [internal port] with that.

[external url] {
reverse_proxy localhost:[internal port]
}
CADDY

In my case I pointed my subdomain notediscovery on radet5.com at my server so my config looks like this

notediscovery.radet5.com {
reverse_proxy localhost:8000
}
CADDY

Then just restart the caddy service

systemctl restart caddy
BASH

Also wouldn't hurt to double check its status to make sure it is running successfully and is enabled

systemctl status caddy
BASH

If that looks good, you're all set with a SSL secured reverse proxy!

Basic Auth

To lock down your server a little bit further you can also protect the proxy endpoint with a simple password. This takes a little pressure off of NoteDiscovery to handle your authentication. Since it is currently designed more for local network use this is wise.

This example is from Caddy's documentation.

basicauth {
# Username "Bob", password "hiccup"
Bob $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG
}
CADDY

It fits into your overall config like this:

notediscovery.radet5.com {
reverse_proxy localhost:8000
basicauth {
radet $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG
}
}
CADDY

The basic auth configuration takes a username and a bcrypt hash; you'll need to hash your own password with bcrypt. (Check the authentication section of this tutorial for a built-in bcrypt hash generator you can use for this purpose as well as its intended use)

Note that for versions of Caddy v2.8.0 and up basicauth has been changed to basic_auth. But at the time of writing ubuntu is still using the older version in its package manager.

Creating a user

I've created a dedicated user named notediscovery on my server for running the NoteDiscovery docker container in order to isolate it from other services I'm hosting. You can use any user you like, but do not use the root account to run the docker container.

Set this variable to just copy/paste the following commands, otherwise be sure to replace every instance of $user for the rest of the commands in this tutorial with the proper username. This variable will need to be reset if you log out.

user='notediscovery'
BASH

Add user

adduser $user
BASH

Installing and Preparing Rootless Docker

There are two recommended ways of installing Docker on Ubuntu 24.04, either via the Ubuntu maintained package docker.io or the docker maintained package docker-ce. Each package handles the docker installation a bit differently. I decided to stick with the Ubuntu approach.

In order to avoid running the Docker container with root privileges I've also configured the system to support running Docker with user level privileges in the interest of security.

Install the docker.io base package and uidmap

apt install docker.io uidmap
BASH

uidmap is required for the user ID mapping used for rootless

Before we can run the docker-rootless installation script Ubuntu requires a little extra configuration. The following adds a rule to the /etc/apparmor.d/ directory as specified on the Ubuntu Blog in order to allow just our unprivileged rootlesskit process the limited kernel access it needs to run the docker deamon.

cat <<EOT | sudo tee "/etc/apparmor.d/home.$user.bin.rootlesskit"
# ref: https://ubuntu.com/blog/ubuntu-23-10-restricted-unprivileged-user-namespaces
abi <abi/4.0>,
include <tunables/global>
/home/$user/bin/rootlesskit flags=(unconfined) {
userns,
# Site-specific additions and overrides. See local/README for details.
include if exists <local/home.$user.bin.rootlesskit>
}
EOT
systemctl restart apparmor.service
BASH

Finally, enable linger to allow the user level docker instance to continue running after the user has logged out. This way the service will stay up!

loginctl enable-linger $user
BASH

Rootless setup

With that done we can log in as our user to download and run docker rootless install script. Note that you cannot su into the user account you must actually log in.

This must be run as the user who will be running the docker container, in their home direcotry

curl -fsSL https://get.docker.com/rootless | sh
BASH

I found that in order to run docker compose properly I needed docker v2; this will download it and install it where it needs to be

mkdir -p .docker/cli-plugins
curl -SL https://github.com/docker/compose/releases/download/v2.40.3/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose
chmod +x ./.docker/cli-plugins/docker-compose
BASH

It should already be running and enabled, but to just to be sure go ahead and enable and run the user level docker service

systemctl --user enable docker
systemctl --user start docker
BASH

NoteDiscovery install & config

From here you can follow any of the setup instructions, but I'll go over the easiest one here: using the pre-built GHCR image.

First we create the required directories. Your markdown notes will be stored in the data directory.

mkdir -p data plugins themes
BASH

Next we download the default config file along with two of the themes (there are several others you're encouraged to grab from the repo! I'm a fan of Monokai personally)

curl -O https://raw.githubusercontent.com/gamosoft/notediscovery/main/config.yaml
curl -o themes/light.css https://raw.githubusercontent.com/gamosoft/notediscovery/main/themes/light.css
curl -o themes/dark.css https://raw.githubusercontent.com/gamosoft/notediscovery/main/themes/dark.css
BASH

Finally, we'll need the container image itself

curl -O https://raw.githubusercontent.com/gamosoft/notediscovery/main/docker-compose.ghcr.yml
BASH

Authentication setup

Alright, let's lock it down. As per the documentation, the dev has included some handy functions for generating a password hash as well as a secret key both of which we'll need to add to our config file.

We'll need to spin up our docker image, but first let's disable networking on it temporarily since we don't have the auth set up yet. Edit docker-compose.ghcr.yml and add network_mode: "none" like this:

services:
notediscovery:
network_mode: "none"
YAML

Spin up the container

docker compose -f docker-compose.ghcr.yml up -d
BASH

This will ask you for a password and generate a bycrypt password hash for you:

docker compose -f docker-compose.ghcr.yml exec notediscovery python generate_password.py
BASH

And this will generate a random secret key:

docker compose -f docker-compose.ghcr.yml exec notediscovery python -c "import secrets; print(secrets.token_hex(32))"
BASH

Now we can edit the config.yaml and copy the values we've generated into the right spots. Be sure to also enable authentication!

authentication:
# Enable authentication
enabled: true
# Session secret key (paste the output from Step 2)
secret_key: "your_generated_secret_key_here"
# Password hash (paste the output from Step 1)
password_hash: "$2b$12$..."
YAML

It's also a good idea to update the allowed origins, mine looks something like this:

server:
allowed_origins: ["https://notediscovery.radet5.com"]
YAML

OK, we should be ready to go! Remove the network_mode: "none" from the docker-compose.ghcr.yml file and then restart the container

docker compose -f docker-compose.ghcr.yml down
docker compose -f docker-compose.ghcr.yml up -d
BASH

In your browser navigate to your domain and can log in and get started building out your own personal knowledge store!

Checkout the official documentation for more details on further configuration and features available to you

A final note on security

Please do be wary exposing anything to the public internet like this! I've tried to make everything moderately secure in this setup, but it is not perfect. It is important to mitigate your risks. For my own system this is an isolated server with no sensitive information and which I can easily disable should anything go awry. I recommend backing up your notes periodically and not storing sensitive information in them if you are going to put them on a publicly accessible server like this!

Thank you for following along, have fun!

Δ
decorative element
decorative element