Trilium Notes Tutorial: Deploy a Hierarchical Personal Knowledge Base via Docker
Learn how to host Trilium Notes on a VPS using Docker Compose. Build a powerful, customizable, and hierarchical note-taking database.
Trilium Notes Tutorial: Deploy a Hierarchical Personal Knowledge Base via Docker
Trilium Notes is an hierarchical personal knowledge base with a strong focus on build-your-own-customizations, scripting, and advanced developer workflows. Unlike flatter note-taking applications, Trilium uses a powerful SQLite-backed tree structure, permitting deep nesting, cloned notes (notes with multiple parents), attributes, and script execution.
This guide provides a production-grade deployment walkthrough for self-hosting Trilium Notes on a Virtual Private Server (VPS) using Docker Compose, secured behind an Nginx reverse proxy with automated Let's Encrypt SSL certificates.
1. Core Architecture and Database Engine
Before deploying, understanding how Trilium manages state is critical for designing backup and sync strategies.
Database Storage
Trilium stores all notes, revisions, attributes, and options in a single SQLite database named document.db, located inside its data directory (/home/node/trilium-data by default in Docker). SQLite provides ACID compliance and simplified backups, but because it relies on file-system locks, it cannot be run concurrently by multiple write-processes. Mounting this directory over network filesystems (like NFS) is strongly discouraged due to locking latency and corruption risks.
Hierarchical Note Tree & Attributes
Trilium represents notes as entities with parent-child relationships stored in the database. Notes can be cloned, meaning a single note can exist under multiple parents in the tree without duplicating its underlying content or ID. Additionally, Trilium features: - Labels: Key-value metadata attached to notes (e.g., cssClass=dark-theme, shareType=raw). - Relations: Directed associations between notes (e.g., sourceOfRelation -> targetNote). - Script Notes: JavaScript (backend or frontend) execution within the sandbox to automate tasks, generate dynamic templates, or render custom widgets.
Sync Protocol
Trilium implements a custom sync protocol over HTTP/HTTPS: 1. Sync Server (VPS): The central source of truth. 2. Sync Clients (Desktop/Local instances): Pull changes, resolve conflicts using last-write-wins (relying on millisecond-accurate timestamps), and push local mutations. 3. Changelog Tracking: A database table tracks incremental mutations, allowing clients to request delta updates rather than full database synchronization.
2. Docker Compose Configuration
Create a dedicated directory on your VPS to house the Docker Compose configuration and persistent storage volume:
mkdir -p ~/trilium-data
cd ~/trilium-data
Create a docker-compose.yml file. This configuration pins the stable version, mounts a persistent volume to preserve SQLite data, and exposes the container on a local port (8080) to be proxied by Nginx.
version: '3.8'
services:
trilium:
image: zadam/trilium:0.61-latest
container_name: trilium-notes
restart: unless-stopped
security_opt:
- no-new-privileges:true
environment:
- TRILIUM_DATA_DIR=/home/node/trilium-data
- USER_UID=1000
- USER_GID=1000
ports:
- "127.0.0.1:8080:8080"
volumes:
- ./data:/home/node/trilium-data
Key Parameters:
image: We pin the service to a minor version (0.61-latest) to avoid breaking database schema migrations that can occur with major releases.ports: Binding to127.0.0.1:8080ensures the database container is inaccessible directly from the public internet, routing all external traffic through the reverse proxy.volumes: Maps host path./datato the container's/home/node/trilium-datadirectory, preserving SQLite state across container restarts and updates.
3. Reverse Proxy & SSL Configuration (Nginx)
To enable secure HTTPS synchronization and web client access, route traffic through Nginx.
Install Nginx and Certbot
Run the following commands on your Ubuntu/Debian host:
sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx
Configure Nginx Server Block
Create a virtual host configuration file under /etc/nginx/sites-available/trilium.conf:
server {
listen 80;
server_name notes.example.com; # Replace with your domain
location / {
proxy_pass http://127.0.0.1:8080;
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;
# WebSockets support for real-time synchronization
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Adjust maximum upload size for attachments
client_max_body_size 100M;
}
}
Enable the configuration and reload Nginx:
sudo ln -s /etc/nginx/sites-available/trilium.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Acquire Let's Encrypt SSL
Execute Certbot to provision and configure the SSL certificate:
sudo certbot --nginx -d notes.example.com
Certbot will automatically alter the Nginx configuration to enforce SSL redirection (port 80 to port 443) and manage TLS parameters.
4. Initialization and Desktop Client Sync
- Launch the Container:
bash docker compose up -d - Access Setup Wizard: Navigate to
https://notes.example.comin your web browser. Select "I am a new user and want to create a new Trilium document", and configure your master password. - Connecting a Desktop Client:
- Download the official Trilium Desktop application for your client platform.
- On first launch, select "I want to sync with my Trilium instance on a server".
- Input your server URL (
https://notes.example.com) and master password to start the initial sync.
5. Maintenance: Automated Backups & Updates
Automated SQLite Backups
Because SQLite write locking can interfere with simple cp actions, Trilium includes a built-in backup schedule. By default, it generates hourly, daily, and weekly backups within the backup directory inside the data volume.
To create a secondary backup of the SQLite database safely without locking the server, copy the latest auto-backup from the volume directory:
#!/bin/bash
BACKUP_DIR="/home/user/backups"
DATA_DIR="/home/user/trilium-data/data"
TIMESTAMP=$(date +"%Y%m%d%H%M%S")
mkdir -p "$BACKUP_DIR"
# Copy the latest automated backup generated by Trilium
cp "$DATA_DIR/backup/backup-daily.db" "$BACKUP_DIR/trilium-backup-$TIMESTAMP.db"
# Prune backups older than 30 days
find "$BACKUP_DIR" -name "trilium-backup-*.db" -mtime +30 -delete
Container Updates
To pull the latest image and update the container:
cd ~/trilium-data
docker compose pull
docker compose up -d
Trilium will automatically apply database migrations on boot. Always ensure you have a backup copy of your document.db before upgrading.