Self-hosted handwritten notes for your own NAS.
NexaNote is a privacy-focused note-taking app for people who want to keep their notes on hardware they control. It pairs a Flutter client (Linux desktop and Android) with a small, self-hosted Python (FastAPI) backend that stores everything as plain files on your own server or NAS.
Your notes never touch a third-party cloud. You own the storage, the sync server, and the data.
- Markdown note storage — typed notes are saved as plain
.mdfiles you can open in Obsidian or any text editor. - Stylus & drawing support — handwritten notes with pen, highlighter, eraser, and pressure sensitivity (mouse works too).
- Self-hosted backend — runs on your NAS or any always-on machine.
- WebDAV sync — sync notes between devices through your own WebDAV server.
- Docker deployment — a single multi-arch image for
amd64andarm64. - Android APK — installable builds published via GitHub Releases.
- Offline-first direction — the app is built to keep working without a network connection and sync when one is available.
NexaNote is under active development. It's already usable for everyday testing, but please set expectations accordingly:
- Core features (notes, drawings, notebooks, WebDAV sync) work today.
- Sync reliability is improving but is not yet bulletproof.
- Not yet recommended for critical notes without your own backups. Keep a copy of anything you can't afford to lose.
If you hit problems, the in-app diagnostics and sync logs (see below) make it much easier to report what went wrong.
The easiest way to run the backend is the prebuilt Docker image
thezupzup/nexanote:latest. Create a docker-compose.yml:
services:
nexanote-backend:
image: thezupzup/nexanote:latest
container_name: nexanote-backend
ports:
- "8766:8766" # REST API (used by the app)
- "8765:8765" # WebDAV (used for sync)
volumes:
- ./data:/data
restart: unless-stoppedThen start it:
docker compose up -dThe image is published as a multi-arch manifest (linux/amd64 +
linux/arm64), so x86 servers and ARM NAS units pull the right variant
automatically.
Want a single hostname / one TLS certificate instead of two ports? See docs/docker.md for reverse-proxy examples (Nginx, Caddy, Cloudflare Tunnel).
On a NAS, point the volume at a path on your storage pool instead of ./data.
Common examples:
# Synology
volumes:
- /volume1/docker/nexanote/data:/data
# Ugreen (or a second pool)
volumes:
- /volume2/docker/nexanote/data:/dataTested with Ugreen NAS (UGOS Pro); it should work on any system that can run
Docker. The mounted data directory holds all your notes, so back it up like
any other important folder.
Open Settings → Backend in the app and enter your backend URL.
Backend (REST API) URL:
http://NAS_IP:8766
https://nexanote.example.com
WebDAV URL:
http://NAS_IP:8765
https://webdav-nexanote.example.com
If you run behind a reverse proxy that serves both on one hostname, the app can derive the WebDAV URL automatically — see docs/docker.md. Otherwise, set both URLs explicitly.
There is no Play Store listing yet. To install on Android:
- Go to the GitHub Releases page.
- Download the latest
NexaNote-Android-*.apkasset. - Open the downloaded file to install it manually.
- If prompted, allow install from unknown sources for your browser or file manager.
The Android build is functional but still being polished, so expect rough edges.
- Python 3.10+
- Flutter 3.10+
pip install -r requirements.txt
python main.pyThis starts two servers:
| Service | URL | Purpose |
|---|---|---|
| REST API | http://127.0.0.1:8766 | Used by the Flutter app |
| WebDAV | http://127.0.0.1:8765 | Sync |
| API docs | http://127.0.0.1:8766/docs | Interactive Swagger UI |
cd app
flutter pub get
flutter run -d linuxpython -m pytest tests/ -vEverything lives under your data directory as plain files:
data/
├── notes/ # Markdown note bodies (+ YAML frontmatter)
├── notebooks/ # Notebook metadata (YAML)
├── drawings/ # Stylus strokes (JSON, one file per note)
└── sync_logs/ # Latest sync report (for diagnostics)
Because notes are plain Markdown on disk, you can read or edit them with any Markdown editor, and back them up with ordinary file tools.
When sync misbehaves, NexaNote gives you a few ways to see what happened:
- Sync logs — the most recent sync writes a report to
data/sync_logs/latest.json(note ids/titles and outcomes), also available from the backend API. - Dry-run — trigger a sync with
?dry_run=trueto preview what would change without writing anything. - Copy diagnostics — the app's connection screen has a button that builds a copy/paste-able diagnostic summary (with credentials redacted), handy for bug reports.
- Stronger sync reliability
- Better Android polish
- A clearer conflict-resolution UI
- F-Droid readiness
- Optional desktop packaging
Contributions are welcome.
- Open an issue to report bugs or discuss ideas before large changes.
- Keep pull requests small and focused — one change per PR.
- Don't modify unrelated files.
- Tests are appreciated.
See CONTRIBUTING.md for branch and PR conventions.
MPL-2.0 — modifications must remain open-source, and commercial use is allowed. See LICENSE.