Sign inSign up

adibhanna/zennotes

By adibhanna

โ€ขUpdated 2 days ago

Keyboard-first, Vim-friendly Markdown notes โ€” self-hosted web app backed by a Go server.

Image
Content management system
0

50K+

adibhanna/zennotes repository overview

โ ZenNotes (self-hosted)

Keyboard-first, Vim-friendly Markdown notes. Your notes stay as ordinary .md files on disk โ€” no database, no lock-in. This image runs the self-hosted web app + Go server: open it in a browser and edit a vault mounted from your host.

โ Quick start

The server binds to 0.0.0.0 inside the container, so it requires an auth token โ€” it refuses to start without one. Generate a token and pass it in:

TOKEN=$(openssl rand -hex 32)

docker run -d --name zennotes \
  -p 127.0.0.1:7878:7878 \
  -e ZENNOTES_AUTH_TOKEN="$TOKEN" \
  -v "$HOME/Notes/ZenNotesVault:/workspace" \
  -v "$HOME/zennotes-data:/data" \
  --read-only --tmpfs /tmp \
  --cap-drop ALL --security-opt no-new-privileges \
  adibhanna/zennotes:latest

echo "Open http://localhost:7878 and log in with: $TOKEN"

Paste the token into the browser when prompted. After login the browser uses a session cookie, so you won't need to re-enter it on refresh.

Prefer a persisted token? Write one to a file and point the server at it with -e ZENNOTES_AUTH_TOKEN_FILE=/data/auth-token plus openssl rand -hex 32 > ./zennotes-data/auth-token.

โ docker compose

services:
  zennotes:
    image: adibhanna/zennotes:latest
    container_name: zennotes
    restart: unless-stopped
    ports:
      - "127.0.0.1:7878:7878"
    environment:
      ZENNOTES_AUTH_TOKEN: "change-me-to-a-long-random-string"
    volumes:
      - ./vault:/workspace   # your notes (plain Markdown)
      - ./data:/data         # server config
    read_only: true
    tmpfs:
      - /tmp
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true

โ Volumes

PathPurpose
/workspaceYour vault โ€” plain Markdown files on the host
/dataServer config (server.json)

โ Configuration

VariablePurposeDefault
ZENNOTES_AUTH_TOKENLogin token. Required for this image (non-loopback bind).โ€“
ZENNOTES_AUTH_TOKEN_FILERead the token from a file instead (Docker/K8s secrets).โ€“
ZENNOTES_BEHIND_TLSSet 1 behind a TLS proxy (Secure cookies + HSTS).0
ZENNOTES_TRUSTED_PROXIESCIDR list of trusted reverse proxies.โ€“
ZENNOTES_ALLOWED_ORIGINSExtra cross-origin clients (same-origin always works).same-origin
ZENNOTES_BROWSE_ROOTSDirectories allowed as vault candidates./workspace
ZENNOTES_BASE_PATHServe under a subpath behind a reverse proxy.โ€“
ZENNOTES_MAX_NOTE_BYTES / ZENNOTES_MAX_ASSET_BYTESPer-request size caps.10 MiB / 50 MiB

โ Tags

  • latest โ€” most recent release
  • X.Y.Z / X.Y โ€” pinned versions (e.g. 2.0.1, 2.0)

โ Features

Plain-file Markdown vault ยท Vim mode & leader-key flows ยท command palette ยท edit / split / preview modes ยท tasks, tags, search, archive, trash ยท daily notes ยท KaTeX math, Mermaid, TikZ, JSXGraph diagrams ยท wiki links & callouts ยท first-party MCP server for AI tools.

โ Security

The image runs as a non-root user (uid 65532) by default. The quick-start and compose examples above also run it read-only, with /tmp on tmpfs and all Linux capabilities dropped โ€” recommended.

It requires an auth token and warns periodically if exposed on a non-loopback bind over plain HTTP. If you expose it beyond your LAN, put it behind a reverse proxy, terminate TLS there, and set ZENNOTES_BEHIND_TLS=1 plus ZENNOTES_TRUSTED_PROXIES.

Full guide: Self-Host with Dockerโ 

Tag summary

Content type

Image

Digest

sha256:9f90fd2d9โ€ฆ

Size

41.4 MB

Last updated

2 days ago

docker pull adibhanna/zennotes