How to self host Logseq DB graph sync
Before we get into the how, here’s a bit more about the why and the what.
Logseq is a local-first, Markdown-based note-taking application. I love it. The code is open source, and the plugin ecosystem is incredibly helpful.
For most of its history, Logseq has used a file-based database. You own your Markdown files and images, and you sync them through your drive folder across devices. Editing happens one device at a time, which was enough for me — or so I thought.
But file-based systems are slow and heavy. Last year, Logseq began moving toward a graph-based database to better support vector indexing and enable more advanced AI features.
While the new capabilities are exciting, adding another $10–15 to my monthly subscription stack isn’t.
Thankfully, the founder Tienson Qin, and the contributors added support for self-hosting the server on our own machines.
Here is a quick guide to self host your own Logseq server for syncing across devices. I will try to keep this as updated as possible in order to support the upcoming changes.
As of Feb 2026, this feature is in beta.
Table of Contents
- Part 1: Prerequisites & Setup
- Part 2: Deploying the Server
- Part 3: Building the Client
- Security & Maintenance
Part 1: Prerequisites & Setup
This section covers prerequisites and initial setup before building and deploying the server.
Prerequisites & Installation
Development Machine Requirements
You’ll need these tools installed on the machine where you’re building, I recomend not building these things on a remote computer, because this is resource intensive:
Required Software:
- Java 21
- Latest Node LTS and yarn
- Clojure CLI tools
- Git
Installation Steps
On macOS:
# Install Homebrew (if not already installed)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Install Java 21
brew install openjdk@21
# Install nvm (Node Version Manager)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
# Reload shell or run:
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
# Install Node.js LTS
nvm install --lts
nvm use --lts
# Install yarn
npm install -g yarn
# Install Clojure
brew install clojure/tools/clojure
# Verify installations
java -version
node -v
yarn -v
clojure -version On Ubuntu/Debian:
# Update system
sudo apt update && sudo apt upgrade -y
# Install Java 21
sudo apt install -y openjdk-21-jdk
# Install nvm (Node Version Manager)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
# Reload shell or run:
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
# Install Node.js LTS
nvm install --lts
nvm use --lts
# Install yarn
npm install -g yarn
# Install Clojure
curl -O https://download.clojure.org/install/linux-install-1.11.1.1435.sh
chmod +x linux-install-1.11.1.1435.sh
sudo ./linux-install-1.11.1.1435.sh
# Verify installations
java -version
node -v
yarn -v
clojure -version On Windows: Who uses windows?
Clone Repository
# Clone Logseq repository
git clone https://github.com/logseq/logseq.git
cd logseq
# Install root dependencies
yarn install Repository cloned! Next, set up AWS Cognito (or skip to use Logseq’s default).
AWS Cognito Setup (Optional)
Cognito provides user authentication. You can skip this for local testing and use test credentials, logseq packs their own cognito urls in the repository. If you are fine sharing your email and let them handle the user keys, you can skip this step and go to the next step
Why Cognito?
Logseq’s sync server uses AWS Cognito for:
- User authentication and JWT tokens
- Email-based user management
- OAuth2 token refresh
- Free tier covers most personal use (50,000 MAUs)
Step 1: Create a User Pool
Go to AWS Cognito Console:
- https://console.aws.amazon.com/cognito
- Make sure you log on to the correct region
Click “Create user pool”
- Application Type
- Select Single-page application (SPA)
- We don’t need a client secret
- Select Single-page application (SPA)
- Options for sign-in identifiers (important step)
- Username
- Enable self-registration
- Keep other options as default, you have the freedom for names
- Proceed with Create user directory
- Application Type
Step 2: Configure OAuth Domain
After creating the user pool, you need to set up an OAuth domain for token refresh:
- In your User Pool, go to: “App integration” tab
- Scroll to “Domain” section
- Either pick the default domain available here or create your
Step 3: Get Your Credentials
You need to collect several values from your Cognito User Pool. Here’s how to find each component:
Navigate to your User Pool:
- Go to AWS Cognito Console → User pools
- Click on your user pool
1. Find REGION:
- Look at the AWS Console URL in your browser
- Example:
https://console.aws.amazon.com/cognito/v2/idp/user-pools?region=us-east-1 - The region is:
us-east-1 - Common regions:
us-east-1,us-west-2,eu-west-1,ap-southeast-1
2. Find USER-POOL-ID:
- In your User Pool, go to “User pool overview” tab
- Look for “User pool ID” field
- Format:
{region}_{randomstring} - Example:
us-east-1_ABC123DEF
3. Find COGNITO_CLIENT_ID:
- In your User Pool, go to “App integration” tab
- Scroll to “App clients and analytics” section
- Click on your app client name
- Copy the “Client ID” value
- Example:
1abc2def3ghi4jklm5nopqrs
4. Find COGNITO_OAUTH_DOMAIN:
- In your User Pool, go to “App integration” tab
- Look for “Domain” section
- Format:
{your-prefix}.auth.{region}.amazoncognito.com - Example:
my-logseq-sync.auth.us-east-1.amazoncognito.com
Now construct the complete values:
COGNITO_ISSUER (COGNITO-IDP):
- Format:
https://cognito-idp.{REGION}.amazonaws.com/{USER_POOL_ID} - Example:
https://cognito-idp.us-east-1.amazonaws.com/us-east-1_ABC123DEF
- Format:
COGNITO_JWKS_URL:
- Format:
{COGNITO_ISSUER}/.well-known/jwks.json - Example:
https://cognito-idp.us-east-1.amazonaws.com/us-east-1_ABC123DEF/.well-known/jwks.json
- Format:
COGNITO_OAUTH_DOMAIN (LOGIN-URL):
- Format:
{your-prefix}.auth.{REGION}.amazoncognito.com - Example:
my-logseq-sync.auth.us-east-1.amazoncognito.com
- Format:
Summary - Copy these values:
REGION="us-east-1" # Your AWS region
USER_POOL_ID="us-east-1_ABC123DEF" # From User pool overview
CLIENT_ID="1abc2def3ghi4jklm5nopqrs" # From App clients
OAUTH_DOMAIN="my-logseq-sync.auth.us-east-1.amazoncognito.com" # From Domain section
# Constructed values:
COGNITO_ISSUER="https://cognito-idp.us-east-1.amazonaws.com/us-east-1_ABC123DEF"
COGNITO_JWKS_URL="https://cognito-idp.us-east-1.amazonaws.com/us-east-1_ABC123DEF/.well-known/jwks.json" Keep these values noted somewhere, we’ll use them for our server and client setup in the upcoming steps
Part 2: Deploying the Server
Now that you have Cognito set up (or decided to use Logseq’s default), let’s build and deploy the server.
VPS Deployment
Deploy to a cloud VPS (DigitalOcean, Linode, Hetzner, AWS EC2, etc.) for remote access.
Step 1: Build the DB-Sync Server (On Your Local Machine)
# From the Logseq repository root
cd deps/db-sync
# Install dependencies
yarn install
# IMPORTANT: Update wrangler.toml for local/self-hosted deployment
# Edit worker/wrangler.toml and configure:
# - Set name = "logseq-db-sync" (or your preferred name)
# - You can remove or comment out Cloudflare-specific settings
# - This file is used during build but we'll run the Node adapter
# Example changes to worker/wrangler.toml:
# name = "logseq-db-sync"
# Remove: routes, workers_dev settings if present
# Build the Node.js adapter (this compiles ClojureScript to JavaScript)
yarn build:node-adapter
# This will take ~3 minutes...
# Output will be in dist/node-adapter/ Step 2: Configure start.sh with Cognito Values
Edit start.sh in deps/db-sync/ directory with your Cognito credentials from Part 1:
#!/bin/bash
# Server Configuration
export DB_SYNC_PORT=8787 # Default port, change if needed
export DB_SYNC_DATA_DIR="$(pwd)/data/db-sync"
export DB_SYNC_LOG_LEVEL=info # Options: debug, info, warn, error
# AWS Cognito Credentials (Use values from Part 1, Step 3!)
export COGNITO_ISSUER="https://cognito-idp.us-east-1.amazonaws.com/us-east-1_ABC123DEF" # Your COGNITO_ISSUER
export COGNITO_CLIENT_ID="1abc2def3ghi4jklm5nopqrs" # Your COGNITO_CLIENT_ID
export COGNITO_JWKS_URL="https://cognito-idp.us-east-1.amazonaws.com/us-east-1_ABC123DEF/.well-known/jwks.json" # Your COGNITO_JWKS_URL
# Start the server
pm2 start worker/dist/node-adapter.js Note: Replace the example Cognito values with your actual values from Part 1. If using Logseq’s default Cognito, leave the existing values unchanged.
Make it executable:
chmod +x start.sh You may try to run this script on your local machine to try it out
Step 3: Create Deployment Package
# Still in deps/db-sync directory
# Install production dependencies
yarn install --production
# Create a tarball with everything needed for deployment
tar czf node-adapter-deploy.tar.gz
dist/node-adapter/
node_modules/
package.json
yarn.lock
start.sh
# Verify the archive
tar tzf node-adapter-deploy.tar.gz | head -20 Server built and packaged! You now have node-adapter-deploy.tar.gz with your Cognito configuration.
Note: The tarball includes node_modules and your configured start.sh, so you can deploy directly to VPS.
Step 4: Prepare VPS
SSH into your VPS:
ssh root@YOUR_VPS_IP
# Update system
apt update && apt upgrade -y
# Install nvm (Node Version Manager)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
# Reload shell or run:
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
# Install Node.js LTS
nvm install --lts
nvm use --lts
# Install PM2 (process manager)
npm install -g pm2
# Install nginx (optional, for external access)
apt install -y nginx Step 5: Transfer and Extract
From your local machine:
# Transfer the deployment package to VPS
scp node-adapter-deploy.tar.gz root@YOUR_VPS_IP:/tmp/ On your VPS:
# Create directory
mkdir -p /opt/logseq-sync
cd /opt/logseq-sync
# Extract files (includes pre-built server with dependencies)
tar xzf /tmp/node-adapter-deploy.tar.gz Note: The tarball contains the compiled JavaScript server, dependencies, and your pre-configured start.sh. No additional configuration needed!
Step 6: Update Paths in start.sh (Optional)
If you want to change the port or data directory for the VPS, edit start.sh:
# Edit the file
nano start.sh
# Update these lines if needed:
export DB_SYNC_PORT=3001 # Change port if 8787 conflicts
export DB_SYNC_DATA_DIR="/opt/logseq-sync/data" # VPS data path Note: The Cognito values are already configured from Step 2. Only change port/paths if needed.
Step 7: Start the Service with PM2
# Test the start script first (it already has your Cognito config!)
./start.sh
# Press Ctrl+C after verifying it starts
# Start with PM2 using the configured start script
pm2 start start.sh --name logseq-sync
# Save PM2 configuration
pm2 save
# Enable PM2 startup on boot
pm2 startup
# Follow the command it outputs
# Check status
pm2 status
pm2 logs
# Test the health endpoint (use the port you configured in start.sh)
curl http://localhost:8787/health
# Or if you changed to port 3001:
# curl http://localhost:3001/health
# Should return: {"ok":true} Note: All configuration is in start.sh - no need to specify environment variables again!
Step 8: Configure Nginx (Optional)
Note: Skip this step if you only need local access. Nginx provides external access, HTTPS support, and better security.
Create nginx configuration file:
# Create nginx config
sudo nano /etc/nginx/sites-available/logseq-sync Add this configuration:
server {
listen 80;
server_name <YOUR_VM_IP>;
# Increase client body size for snapshot uploads
client_max_body_size 100M;
location / {
# IMPORTANT: Match the port to what you set in start.sh (default is 8787)
proxy_pass http://localhost:8787;
proxy_http_version 1.1;
# WebSocket upgrade headers
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
# Standard proxy headers
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;
# Disable buffering for WebSocket
proxy_buffering off;
proxy_cache_bypass $http_upgrade;
# WebSocket timeout settings (important for long-lived connections)
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
# Keep TCP connections alive
proxy_connect_timeout 60s;
}
} Enable and test nginx:
# Enable the site
sudo ln -s /etc/nginx/sites-available/logseq-sync /etc/nginx/sites-enabled/
# Test configuration
nginx -t
# Reload nginx
systemctl reload nginx
# Verify from outside (use your actual VPS IP)
curl http://YOUR_VPS_IP/health
# Should return: {"ok":true}
# Test WebSocket endpoint
curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" http://YOUR_VPS_IP/sync/test
# Should return 101 Switching Protocols or authentication error (not 502) Step 9: Enable HTTPS with Let’s Encrypt (Optional)
# Install certbot
apt install -y certbot python3-certbot-nginx
# Get certificate (requires a domain name pointing to your VPS)
certbot --nginx -d your-domain.com
# Certbot will automatically:
# - Modify nginx config to use HTTPS
# - Set up auto-renewal
# Test HTTPS
curl https://your-domain.com/health ✅ VPS deployment complete! Server accessible at http://YOUR_VPS_IP (or https://your-domain.com if using Let’s Encrypt)
Part 3: Building the Client
Configure Logseq clients to connect to your self-hosted server.
Note: You must modify the Logseq source code and rebuild the app. This requires modifying 3-4 files in the codebase.
Overview of Required Changes
Files to edit:
src/main/frontend/config.cljs- Server URLs and Cognito settingssrc/main/frontend/handler/user.cljs- Enable sync for self-hosted usersdeps/db/src/logseq/db.cljs- Fix validation to allow sync transactions
Step 1: Update Cognito Configuration
Edit src/main/frontend/config.cljs around lines 27-48:
If using custom Cognito:
(if ENABLE-FILE-SYNC-PRODUCTION
(do (def LOGIN-URL
"https://YOUR_OAUTH_DOMAIN/login?client_id=YOUR_CLIENT_ID&response_type=code&scope=email+openid+phone&redirect_uri=logseq%3A%2F%2Fauth-callback")
(def API-DOMAIN "") ; Leave empty for self-hosted
(def COGNITO-IDP "https://cognito-idp.YOUR_REGION.amazonaws.com/")
(def COGNITO-CLIENT-ID "YOUR_CLIENT_ID")
(def REGION "YOUR_REGION") ; e.g., "us-east-1"
(def USER-POOL-ID "YOUR_USER_POOL_ID") ; e.g., "us-east-1_ABC123DEF"
(def IDENTITY-POOL-ID "") ; Leave empty not required
(def OAUTH-DOMAIN "YOUR_OAUTH_DOMAIN") ; e.g., "my-prefix.auth.us-east-1.amazoncognito.com"
(def PUBLISH-API-BASE "")) ; Leave empty
(do (def LOGIN-URL
"https://YOUR_OAUTH_DOMAIN/login?client_id=YOUR_CLIENT_ID&response_type=code&scope=email+openid+phone&redirect_uri=logseq%3A%2F%2Fauth-callback")
(def API-DOMAIN "")
(def COGNITO-IDP "https://cognito-idp.YOUR_REGION.amazonaws.com/")
(def COGNITO-CLIENT-ID "YOUR_CLIENT_ID")
(def REGION "YOUR_REGION")
(def USER-POOL-ID "YOUR_USER_POOL_ID")
(def IDENTITY-POOL-ID "")
(def OAUTH-DOMAIN "YOUR_OAUTH_DOMAIN")
(def PUBLISH-API-BASE ""))) Note: Replace ALL instances of
YOUR_REGION,YOUR_USER_POOL_ID,YOUR_CLIENT_ID, andYOUR_OAUTH_DOMAINwith values from Part 1, Step 3. Both the production and development blocks should have the same values.
If using Logseq’s default Cognito:
Skip this step - leave the existing Cognito configuration unchanged.
Step 2: Configure Server URLs
In the same file src/main/frontend/config.cljs, around lines 57-66, update:
(defonce db-sync-ws-url
(if db-sync-local?
"ws://YOUR_VPS_IP/sync/%s" ; Your VPS IP or domain
"wss://logseq-sync-prod.logseq.workers.dev/sync/%s"))
(defonce db-sync-http-base
(if db-sync-local?
"http://YOUR_VPS_IP" ; Your VPS IP or domain
"https://logseq-sync-prod.logseq.workers.dev")) For VPS with HTTPS/domain:
(defonce db-sync-ws-url
(if db-sync-local?
"wss://sync.mydomain.com/sync/%s" ; Use wss:// for HTTPS
"wss://logseq-sync-prod.logseq.workers.dev/sync/%s"))
(defonce db-sync-http-base
(if db-sync-local?
"https://sync.mydomain.com" ; Use https:// for HTTPS
"https://logseq-sync-prod.logseq.workers.dev")) For local testing:
(defonce db-sync-ws-url
(if db-sync-local?
"ws://localhost:8787/sync/%s"
"wss://logseq-sync-prod.logseq.workers.dev/sync/%s"))
(defonce db-sync-http-base
(if db-sync-local?
"http://localhost:8787"
"https://logseq-sync-prod.logseq.workers.dev")) Step 3: Enable Sync for Self-Hosted Users
Edit src/main/frontend/handler/user.cljs around line 278:
Find this function:
(defn rtc-group?
[]
(boolean (seq (set/intersection (state/user-groups) #{"team" "rtc_2025_07_10"})))) Replace with:
(defn rtc-group?
[]
(or config/db-sync-local? ; Enable sync for self-hosted users
(boolean (seq (set/intersection (state/user-groups) #{"team" "rtc_2025_07_10"}))))) This change enables the sync feature for your self-hosted server users.
Step 4: Fix Validation for Sync Transactions (optional)
If you face issues Edit deps/db/src/logseq/db.cljs around line 110:
Find this code:
(if (and db-based?
(not
(or (:batch-temp-conn? @conn)
(:rtc-download-graph? tx-meta)
(:reset-conn! tx-meta) Add :rtc-tx? to the list:
(if (and db-based?
(not
(or (:batch-temp-conn? @conn)
(:rtc-download-graph? tx-meta)
(:rtc-tx? tx-meta) ; ADD THIS LINE
(:reset-conn! tx-meta) This fix prevents “Invalid data writing to db!” errors during sync operations.
Step 5: Build the Desktop App
# From Logseq root directory
cd /path/to/logseq
# Install dependencies (if not done already)
yarn install
# Build production desktop app with local sync enabled
# This compiles everything and creates installable packages
# Takes ~10-15 minutes depending on your machine
ENABLE_DB_SYNC_LOCAL=true yarn release-electron
# The built applications will be in:
# - macOS: ./static/Logseq-darwin-x64/ or ./static/Logseq-darwin-arm64/
# - Linux: ./static/Logseq-linux-x64/
# - Windows: ./static/Logseq-win32-x64/ Important: The
ENABLE_DB_SYNC_LOCAL=trueflag is required to enable sync with your self-hosted server.
Step 6: Install and Test
Install the app:
- Navigate to the
static/directory - Find the folder matching your OS and architecture
- Install the app like any other desktop application:
- macOS: Copy
Logseq.appto/Applications/ - Linux: Extract and run the AppImage or use the provided installer
- Windows: Run the installer
.exefile
- macOS: Copy
Test the connection:
Launch your custom-built Logseq app
Sign in:
- Go to Settings → Sync
- Click “Sign in” - you’ll be redirected to your Cognito login page
- Enter your email and create/verify your account
- Allow the OAuth redirect back to Logseq
Create or migrate a graph:
- Only new graphs or migrated graphs can enable remote sync
- Click the three dots (⋮) under “All Graphs”
- Select “Create graph” or use the “Migrate graph” option for existing graphs
Enable sync:
- Go to Settings → Sync
- Toggle “Enable sync” on
- Your graph will start syncing with your server
Verify sync is working:
- Make some edits to your graph
- Check your server logs to see sync activity:
pm2 logs logseq-sync - You should see WebSocket connections and sync operations
Test on another device:
- Install your custom app on another device
- Sign in with the same account
- Your graph should sync automatically
Client configured! Your Logseq app now connects to your self-hosted server.
In case after the installation you see a ‘corrupted app’ error on macOS, run this command in the .app file’s directory to clear the quarantine flag
xattr -dr com.apple.quarantine Logseq.app Security & Maintenance
Essential Security Measures
Enable HTTPS:
- Use Let’s Encrypt for free SSL certificates
- Never run production without HTTPS
Firewall Configuration:
ufw allow 22/tcp # SSH ufw allow 80/tcp # HTTP ufw allow 443/tcp # HTTPS ufw enableRegular Updates:
apt update && apt upgrade -y pm2 updateBackup Your Data:
# Backup sync data regularly tar czf backup-$(date +%Y%m%d).tar.gz /opt/logseq-sync/data/Monitor Logs:
pm2 logs logseq-sync tail -f /var/log/nginx/access.log
Maintenance
Updating the Server
# On your development machine, rebuild the server
cd logseq/deps/db-sync
git pull
yarn install
yarn build:node-adapter
# Install production dependencies locally
yarn install --production
# Use rsync to transfer files to VPS
# This syncs only changed files, making updates faster
rsync -avz --delete
dist/node-adapter/
node_modules/
package.json
yarn.lock
start.sh
root@YOUR_VPS_IP:/opt/logseq-sync/
# SSH into VPS and restart the service
ssh root@YOUR_VPS_IP << 'EOF'
cd /opt/logseq-sync
pm2 restart logseq-sync
pm2 logs logseq-sync --lines 20
EOF
# Or restart manually:
# ssh root@YOUR_VPS_IP
# cd /opt/logseq-sync
# pm2 restart logseq-sync
# pm2 logs logseq-sync --lines 20 Note: rsync only transfers changed files, making updates much faster than re-uploading everything. The
--deleteflag removes files on the server that no longer exist in your local build.
Checking Sync Health
# VPS
pm2 status
curl http://localhost:3001/health
pm2 logs logseq-sync --lines 20
# Check storage usage
du -sh /opt/logseq-sync/data/ Conclusion
You now have a fully self-hosted Logseq sync server! Your data stays under your control, and you can customize the setup to your needs.
Questions or issues? Check:
Contributing: Found an issue with this guide? Submit a PR or open an issue on the Logseq repository.
Quick Reference
Essential Commands:
# VPS Management
pm2 status # Check service status
pm2 logs logseq-sync # View logs
pm2 restart logseq-sync # Restart service
pm2 stop logseq-sync # Stop service
# Health Checks
curl http://localhost:3001/health
systemctl status nginx
nginx -t
# Debugging
tail -f /var/log/nginx/error.log
pm2 logs logseq-sync --err
ss -tlnp | grep 3001 Configuration Files:
- PM2: Check process with
pm2 list - Environment:
/opt/logseq-sync/start.sh - Nginx:
/etc/nginx/sites-available/logseq-sync - Data:
/opt/logseq-sync/data/ - Logs:
pm2 logsor/var/log/nginx/
Client Source Files to Modify:
src/main/frontend/config.cljs(lines 27-66)- Cognito configuration
- Server URLs (db-sync-ws-url, db-sync-http-base)
src/main/frontend/handler/user.cljs(line ~278)- Add
config/db-sync-local?to rtc-group? function
- Add
deps/db/src/logseq/db.cljs(line ~110)- Add
:rtc-tx?to validation skip list
- Add
Build Commands:
# Server
cd deps/db-sync
yarn build:node-adapter
# Client
ENABLE_DB_SYNC_LOCAL=true yarn release-electron This post is AI-assisted, might have mistakes.