This guide is for people who just cloned the repo and want to run ZenNotes quickly.
It covers:
- the desktop app
- the self-hosted web app
- the easiest Docker path for a home server or remote server
If you want the full repo and architecture details, read README.md. This file is the short version.
Use this if:
- you want the desktop app on your own machine: Desktop
- you want ZenNotes in a browser on your home server: Self-hosted with Docker
- you want to run the browser version from source without Docker: Self-hosted from source
- Node.js 22+
- npm
npm ci
npm run dev:desktopOr:
make desktopWhat this does:
- installs the monorepo dependencies
- starts the Electron desktop app in development mode
If you want to build it instead of running dev mode:
npm run build:prod
npm run packPlatform-specific desktop builds:
npm run dist:mac
npm run dist:win
npm run dist:linuxThis is the easiest path for most home-server users.
- Docker
- Docker Compose
make upThen open:
- http://localhost:7878 if you are on the same machine
http://YOUR_SERVER_IP:7878if you are running ZenNotes on another machine
Important:
- Docker now binds to
127.0.0.1by default - on first run, ZenNotes creates a bootstrap auth token at
./data/auth-token - the browser asks for that token once, then uses a secure session cookie
If you want to intentionally disable auth for a trusted local setup:
ALLOW_INSECURE_NOAUTH=1 make upBy default, Docker mounts:
- host
./vault-> container at the same absolute host path - host
./data-> container/data
That means:
- your notes live in
./vault - ZenNotes server config lives in
./data - the server sees your vault as a real host path, not
/workspace - Docker is serving your host files, not storing notes inside the container
If your notes already live somewhere else, mount that folder instead:
CONTENT_ROOT="$HOME/Library/Mobile Documents/com~apple~CloudDocs" make upAnother example:
CONTENT_ROOT="$HOME/Documents/MyVault" make upPaths with spaces are supported.
The default Docker setup also:
- runs the container with your local UID/GID
- uses a read-only root filesystem
- drops Linux capabilities
- enables
no-new-privileges
The intended deployment model is:
- private network
- VPN / Tailscale
- or ZenNotes behind a reverse proxy
Treat direct public exposure as an advanced setup, not the default path.
make downmake rebuildmake logsUse this if you do not want Docker and you are okay running both the frontend and backend locally.
- Node.js 22+
- npm
- Go 1.22+
Install dependencies:
npm ciRun both the web client and Go server together:
make web-stackOr run them separately:
Terminal 1:
npm run dev:serverTerminal 2:
npm run dev:webThen open the local web URL shown by Vite in your browser.
In source dev mode, the browser UI and the Go server are separate processes.
That means:
- frontend changes usually only need the web process
- backend changes need the Go server restarted
If you want a built server binary instead of dev mode:
npm ci
make server-build
./apps/server/bin/zennotes-serverThen open:
- http://localhost:7878
- or
http://YOUR_SERVER_IP:7878
The built server embeds the web app, so you do not need to run dev:web for this path.
When you first open the browser version, ZenNotes asks you to choose a vault folder.
Important detail:
- in the web version, you are browsing the server's filesystem
- not the browser machine's filesystem
- by default, you can only browse configured allowed roots, not the whole machine
So:
- if ZenNotes is running on your home server, the picker shows folders on that server
- if ZenNotes is running in Docker, the picker only sees folders mounted into the container
- if auth is enabled, the browser prompts for the bootstrap token before it can browse or edit
Docker can only see mounted folders.
Fix:
- mount the folder you want with
CONTENT_ROOT=... make up
Example:
CONTENT_ROOT="$HOME/Documents/ObsidianVault" make upIf you need broader browsing than the mounted content root, set ZENNOTES_BROWSE_ROOTS explicitly or use ZENNOTES_ALLOW_UNSCOPED_BROWSE=1 only if you understand the security tradeoff.
This usually means:
- the web client is newer than the running Go server
Fix:
- stop the server
- restart it with
npm run dev:serverormake server-dev - reload the page
For Docker:
- you must mount the iCloud folder into the container
Example:
CONTENT_ROOT="$HOME/Library/Mobile Documents/com~apple~CloudDocs" make upFor non-Docker source runs:
- the server can browse any folder the server process has permission to read
Make sure the Go server is running.
For source runs:
npm run dev:server- if Docker auth is enabled, open
./data/auth-tokenand use that token when ZenNotes asks for it - plus either
npm run dev:webormake web-stack
For Docker:
make up
make desktopmake web-stackmake upmake logsmake downmake helpRecommended defaults:
- just want to try the desktop app locally:
make desktop - want ZenNotes on a home server:
make up - want to develop the browser version:
make web-stack
If you are unsure, start with Docker for self-hosting. It is the simplest setup for most users.