In short. NoteDiscovery is an open-source, self-hosted note app storing notes as plain Markdown, a natural next step from an obsidian self hosted vault. This guide deploys NoteDiscovery on a Contabo VPS with Docker Compose, behind a Caddy reverse proxy for automatic HTTPS. You will provision a Cloud VPS 4 (4 vCPU cores, 8 GB RAM), pull the official ghcr.io/gamosoft/notediscovery image, enable authentication, connect an AI assistant over MCP, and set up backups, in about 20 minutes.
Requirements
NoteDiscovery is a lightweight, self-hosted knowledge base, so it runs comfortably on a small VPS. Before you start, have these ready:
- A Contabo Cloud VPS 4 (4 vCPU cores, 8 GB RAM) or larger, running Ubuntu 24.04 or later
- Docker and the Docker Compose plugin installed on the server
- A domain name with an A record pointing at your VPS IP address
- Caddy for automatic SSL, or an existing reverse proxy if you already run one
If you are moving away from a synced vault in an app like Obsidian, self-hosted setups follow the same shape as this guide: a Docker container, a data volume, and a reverse proxy in front of it. The main difference is that NoteDiscovery ships its own web interface, so there is no separate sync plugin to configure.
The 20-minute estimate in the summary box assumes the VPS is already provisioned and DNS is already pointed at it. DNS propagation itself can add its own wait depending on your registrar, so start that step first if you are following along in real time. Nothing here requires root access beyond the initial server setup: once Docker is installed, every remaining step runs as a container operation or an edit to a mounted config file.
Step 1: Provision Your VPS and Install Docker
Provisioning takes about five minutes, and it is the only part of this guide you do outside Docker. Launch a Cloud VPS 4 from the Contabo Customer Control Panel, choose Ubuntu 24.04 or later, and add your SSH key during setup so you skip password logins entirely. If you do not already have a key pair, generate one locally with ssh-keygen -t ed25519 before you start the order form. The Customer Control Panel lets you paste the public key straight into the provisioning screen.
Once the server boots, connect and lock it down before installing anything. The firewall rules below open only what this guide actually needs: SSH for management, and ports 80 and 443 for Caddy to serve HTTP and HTTPS. Everything else on the server stays unreachable from the outside, which matters more here than on a typical web app, since your notes live on this same machine.
ssh root@your-server-ip
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enableWith the firewall active, install Docker and the Compose plugin:
curl -fsSL https://get.docker.com | sh
apt install docker-compose-plugin -y
docker --version
docker compose versionThis is the same foundation used for any self-hosted knowledge base running in containers, and it is exactly what NoteDiscovery needs before its own container starts: a locked-down firewall, current Docker Engine, and the Compose plugin so multi-container stacks are a single command away. Confirm both version commands return output before moving on.
Step 2: Deploy NoteDiscovery with Docker Compose
NoteDiscovery deploys as a single container, so the whole stack is one file. Create a project directory and a data folder first, since the data folder has to exist before the container starts:
mkdir -p notediscovery/data && cd notediscoveryThen create docker-compose.yml:
services:
notediscovery:
image: ghcr.io/gamosoft/notediscovery:latest
container_name: notediscovery
restart: unless-stopped
ports:
- "8000:8000"
volumes:
- ./data:/app/dataEach line does one job: image pulls the official build straight from GitHub Container Registry, restart: unless-stopped brings NoteDiscovery back after a reboot or crash, ports maps the container’s port 8000 to the same port on the host, and the single volume line is what makes your notes persistent instead of disappearing with the container. No database service is needed, since NoteDiscovery has none.
Bring the stack up:
docker compose up -d
docker compose logs -f notediscoveryNoteDiscovery is now listening on port 8000, and your notes live in ./data as plain Markdown files, not in a database. Visit http://your-server-ip:8000 to confirm the interface loads before adding the reverse proxy. If the container exits immediately, check that the data folder exists and is writable; an empty or missing volume is the most common cause of a failed first start.
Step 3: Set Up Caddy Reverse Proxy with HTTPS
Caddy sits in front of NoteDiscovery and handles HTTPS automatically, so this step needs a Caddyfile and nothing else. Before installing Caddy, confirm your domain’s A record points at the VPS IP address. Caddy’s automatic certificate request fails silently if the domain does not yet resolve to the server, so a dig notes.example.com that returns your server’s IP is worth checking first. Install Caddy on the host:
apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt update && apt install caddy -yThen point the Caddyfile at your domain and the container port:
notes.example.com {
reverse_proxy localhost:8000
}Reload Caddy and it requests a certificate from Let’s Encrypt on its own:
systemctl reload caddyWithin a few seconds your domain serves NoteDiscovery over HTTPS with no manual certificate handling. This is the same pattern used for obsidian web access through a reverse proxy, and for an obsidian self-hosted vault moved onto a public domain: Caddy terminates TLS, and the app container never needs to know encryption is happening at all.
Step 4: Enable Authentication
Password protection ships disabled by default, and the bundled default password is admin, so enabling it, generating a real secret key, and changing that password is the first thing to do after HTTPS is live.
Start by generating a secret key, which encrypts session cookies, from inside the running container:
docker exec -it notediscovery python -c "import secrets; print(secrets.token_hex(32))"Save that value, then download the default NoteDiscovery config file so you have your own editable copy:
curl -o config.yaml https://raw.githubusercontent.com/gamosoft/notediscovery/main/config.yamlEdit the new config.yaml and set:
authentication:
enabled: true
password: "your-strong-password-here"
secret_key: "the-value-from-the-command-above"Mount the file into the container so your edits survive restarts and image updates, adding it to docker-compose.yml alongside the existing data volume:
volumes:
- ./data:/app/data
- ./config.yaml:/app/config.yamlRecreate the container to pick up the new volume:
docker compose up -dReload the site and NoteDiscovery now prompts for the password before showing any notes. Skipping this step isn’t a great idea: the app listens on all network interfaces by default, so an unauthenticated instance behind a public domain is reachable by anyone who finds the URL. Pick a password with real entropy, not a short word with a number appended, and never reuse the secret key across other applications, since together they are the only thing standing between the internet and your entire vault.
Step 5: Connect Your AI Assistant (MCP)
NoteDiscovery includes a built-in MCP server, so connecting an AI assistant is a configuration snippet rather than a separate install. Add this to your assistant’s MCP server configuration, whether that is Claude Desktop, Cursor, or another MCP-compatible client:
{
"mcpServers": {
"notediscovery": {
"command": "docker",
"args": ["run", "--rm", "-i", "-e", "NOTEDISCOVERY_URL=https://notes.example.com", "ghcr.io/gamosoft/notediscovery:latest", "python", "-m", "mcp_server"]
}
}
}Replace the NOTEDISCOVERY_URL value with your own domain from Step 3. Once the assistant reloads its MCP servers, it can search your notes, create new ones from a prompt, follow tags and backlinks across your vault, and append to an existing note like a daily journal, all without leaving the chat window. A prompt like “find all my notes about Docker deployment” or “add this idea to today’s journal entry” is handled through the same MCP tools a human would otherwise click through in the web interface.
This is the feature that most separates NoteDiscovery from a self-hosted knowledge base built purely for humans: the same notes are readable by an AI assistant over a documented protocol, not scraped through a browser extension. If your assistant’s client authenticates over HTTPS rather than a local Docker command, point it at the public domain from Step 3 instead of the container-to-container URL shown above.
Step 6: Back Up Your Notes
Notes live in ./data as plain Markdown files, so backing them up is a file sync job rather than a database export. The simplest option is a scheduled rsync to your storage location:
apt install rclone -y
rclone config
# create a remote named "storage" pointing at your storage location
rclone sync /root/notediscovery/data storage:notediscovery-backupAdd a daily cron entry so the sync runs unattended:
crontab -e
# 0 3 * * * rclone sync /root/notediscovery/data storage:notediscovery-backupBecause the entire vault is Markdown files on disk, a self-hosted knowledge base like this one is easy to restore: copy the backed-up data folder back onto a fresh VPS, point the same Docker Compose file at it, and start the container. It is worth actually running that restore once against a spare VPS or a local Docker install, since a backup you have never tested restoring is only a guess. Keep at least a few days of history in your storage bucket rather than overwriting a single snapshot, so a bad sync or an accidental deletion does not erase the only copy you have.
Troubleshooting
Most of what can go wrong in this setup traces back to one of three causes: a missing volume, a certificate request that ran before DNS was ready, or a config change that only lived inside the container.
Container exits immediately after docker compose up -d. The most common cause is a missing or unwritable data folder, since NoteDiscovery expects it to already exist. Run mkdir -p notediscovery/data from Step 2 again, confirm the folder is owned by your user or root, and bring the stack up once more with docker compose up -d.
Caddy never gets a certificate, and the site stays on HTTP. This almost always means the domain’s A record was not pointing at the VPS IP yet when Caddy made its first request. Confirm resolution with dig notes.example.com, then reload Caddy with systemctl reload caddy once the record has propagated; no Caddyfile change is needed.
The authentication password or secret key change from Step 4 does not take effect. A plain docker compose restart reuses the existing container, so it will not pick up a config.yaml volume that was just added to docker-compose.yml. Run docker compose up -d instead, which recreates the container with the new volume mounted.
FAQ: NoteDiscovery Setup
Copy your Obsidian vault folder directly into the NoteDiscovery data directory, since both store notes as plain Markdown files with the same folder structure. An Obsidian self-hosted vault needs no format conversion: restart the container after copying, and your notes, folders, and internal links appear immediately in the NoteDiscovery file browser, ready to edit or search.
Yes, NoteDiscovery is lightweight enough to run on a 2 GB VPS for a single user with a modest note collection, since it has no database and a small runtime footprint. Larger vaults, multiple concurrent users, or heavy plugin use benefit from more RAM, which is why this guide provisions a Cloud VPS 4 with 8 GB as a comfortable working baseline.
Mount a plugins folder as a Docker volume and place your plugin files inside it, following the structure documented in the project’s plugin guide. NoteDiscovery loads plugins from that mounted directory on container start, so a restart after adding or updating a plugin is enough to pick up the change without rebuilding the image.