ZenNotes stores notes as plain .md files on disk. The server doesn't
encrypt them — by design, you should be able to read them with any text
editor and back them up with any tool. That puts the at-rest encryption
choice in your hands as the operator.
This guide lays out three approaches, in order of effort. Pick one based on your threat model and where the vault lives.
Reasonable threats for a self-hosted notes vault:
- VPS snapshot exfiltration — the hosting provider is compelled or compromised; the disk image leaves your control intact.
- Stolen laptop / NAS — physical theft of the host.
- Backup leakage — backup ends up in S3, Backblaze, a USB drive, or someone's email.
- Sloppy handoff — you decommission a server but the disk goes back into a pool without secure erase.
What this guide does not try to solve:
- A live, running ZenNotes server compromised at runtime. If the attacker is root on the box while ZenNotes is reading the vault, the decryption key is also in memory. End-to-end encryption (notes encrypted by the client before they reach the server) is the only defense for that case, and ZenNotes does not yet ship it.
The cheapest, highest-value step. If the only realistic exposure is backup leakage, encrypted backups are usually enough.
BorgBackup or restic both offer authenticated encryption with a passphrase. Either one wraps your full vault into immutable, encrypted snapshots that are safe to push to any object store.
# Borg, daily
borg init --encryption=repokey-blake2 /backup/vault.borg
borg create --stats /backup/vault.borg::"$(date +%F)" /workspace
borg prune --keep-daily 14 --keep-weekly 8 /backup/vault.borg# restic, with a remote bucket
export RESTIC_REPOSITORY=s3:s3.amazonaws.com/your-bucket
export RESTIC_PASSWORD_FILE=~/.restic-pass
restic backup /workspace
restic forget --keep-daily 14 --keep-weekly 8 --pruneThe vault itself stays plaintext on the live host. That keeps ZenNotes fast and lets you grep the files directly. Anyone who steals only a backup gets ciphertext.
If you also want protection against host-disk seizure or VPS snapshots, put the vault on an encrypted block device or filesystem and unlock it at boot.
# Create an encrypted block device on /dev/sdX (or a loopback file).
sudo cryptsetup luksFormat /dev/sdX
sudo cryptsetup luksOpen /dev/sdX zenvault
sudo mkfs.ext4 /dev/mapper/zenvault
sudo mkdir -p /srv/zenvault
sudo mount /dev/mapper/zenvault /srv/zenvaultPoint Docker at the mounted directory:
CONTENT_ROOT=/srv/zenvault make upAfter a reboot, you must re-unlock the volume before starting the
container — typically with cryptsetup luksOpen and a passphrase or a
keyfile from a hardware token.
If the host is on ZFS, native encryption is simpler than LUKS:
sudo zfs create -o encryption=on -o keylocation=prompt -o keyformat=passphrase tank/zenvault
sudo zfs mount tank/zenvaultThen bind-mount or symlink that dataset as the ZenNotes vault.
hdiutil create -encryption AES-256 -fs APFS -size 50g vault.sparsebundle
gives you an encrypted disk image that auto-mounts when unlocked in
Finder. Useful for desktop-only setups.
The recommended posture for a public VPS deployment:
- Volume encryption (LUKS or ZFS) so a snapshot is ciphertext.
- Encrypted off-site backups (Borg or restic) so a host loss doesn't take notes with it.
- Token rotation in your deployment source of truth (see below) so a session leak doesn't grant indefinite access.
If ZenNotes owns the token in host config and you suspect the bootstrap auth token leaked, rotate it without restarting the container:
curl -X POST https://notes.example.com/api/session/rotate-token \
-H "Authorization: Bearer $CURRENT" \
-H "Content-Type: application/json" \
-d '{"currentToken":"'"$CURRENT"'","newToken":"'"$NEW"'"}'All existing sessions are invalidated; you'll need to log in again with the new token.
If Docker, systemd, or another orchestrator supplies the token via
ZENNOTES_AUTH_TOKEN or ZENNOTES_AUTH_TOKEN_FILE, rotate it there
instead. For the default Docker make up workflow, edit
./data/auth-token and restart the container.
Two things worth being explicit about:
- No end-to-end encryption. Notes leave the server-side encryption layer (LUKS, etc.) and become plaintext the moment ZenNotes reads them. The browser sees plaintext. The desktop app sees plaintext. A compromised running server sees plaintext.
- No per-note keys. If you mount only part of the vault to a collaborator, they see everything in their slice. The unit of access is the vault.
If those gaps matter to you, the right answer is to keep ZenNotes on a trusted local-network or single-user host and rely on the layered mitigations above.