Deployment guide

Notesnook

Overview

This image runs the Notesnook Sync Server, the self-hostable backend behind the Notesnook note-taking apps, on Ubuntu 24.04 LTS. Notes are encrypted on your device before they are sent, so the server only ever stores ciphertext that it cannot read.

Running the server yourself means your notes sync between your devices through infrastructure you own. They never touch the vendor's cloud.

What you get:

  • The full Notesnook self-host stack, run under Docker Compose through a notesnook.service systemd unit, from images pinned by digest. It consists of these services:
    • notesnook-server: the sync server proper, on :5264. It exposes a REST API plus the SignalR hub at /hubs/sync/v2 that carries every encrypted note.
    • identity-server: accounts and tokens, on :8264
    • sse-server: server-sent events, so a change on one device pushes to the others in real time, on :7264
    • monograph-server: renders notes you explicitly choose to publish, on :6264
    • notesnook-db: MongoDB 7.0 as a single-node replica set, holding the encrypted note catalogue and the account store. It has no host port and is reachable only from inside the appliance.
    • notesnook-s3: a MinIO object store holding your encrypted attachment blobs, on :9000. It is published because the Notesnook clients upload and download attachments directly, using server-issued presigned URLs.
  • nginx on port 80, serving an appliance status page that lists your endpoint URLs and answering an unauthenticated /healthz for load balancer probes. The client-facing APIs stay on their native upstream ports, so the URLs you enter in the app are exactly the ones upstream documents.
  • Every server secret generated fresh per-VM on first boot: the API signing secret, the object-store root credentials and the instance name. No baked default survives into the image.
  • Account registration closed by default. Upstream ships open self-registration; this image does not. You open a one-shot window with sudo notesnook-claim, and it closes itself again the moment your account exists.
  • A fail-closed secret guard that refuses to open the public listeners if a known upstream default credential is ever live
  • sudo notesnook-selftest, which pushes a real encrypted note through the sync protocol, fetches it back on a second device and asserts that it returns byte-identical
  • The encrypted note catalogue and attachment blobs on a dedicated Azure data disk at /var/lib/notesnook, captured into the image
  • An unauthenticated /healthz endpoint for Azure Load Balancer health probes
  • 24/7 SecureAnyCloud support

Prerequisites

  • An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region
  • Standard_B2s (2 vCPU / 4 GiB RAM) is a sensible starting point. Size up if you have many devices or large attachments.
  • NSG inbound: allow 22/tcp from your management network. From the devices you sync with, allow 5264/tcp (sync), 8264/tcp (identity), 7264/tcp (events), 6264/tcp (published notes) and 9000/tcp (encrypted attachments). Port 80/tcp serves the status page and the health probe.
  • Assign a static public IP. The endpoint URLs are derived from the VM's public IP on first boot, and your apps are configured with them. If the IP later changes, your apps point at the wrong address until you update them.
  • The appliance serves plain HTTP. For production, put your own domain and TLS in front of it.

Step 1 - Deploy from the Azure Marketplace

In the Azure portal, search the Marketplace for Notesnook Sync Server on Ubuntu 24.04 LTS by SecureAnyCloud and select Create.

Choose your subscription, resource group and region. Pick Standard_B2s or larger. Select SSH public key authentication with username azureuser.

On the Networking tab, open the ports listed in the prerequisites. Leave the data disk that ships with the image attached. It holds your encrypted note store.

Step 2 - Deploy from the Azure CLI

Accept the image terms, create the VM, and open the Notesnook ports on its network security group:

az vm image terms accept --urn sac1647283583153:notesnook-ubuntu-24-04:default:latest
az vm create \
  --resource-group my-notes-rg \
  --name notesnook-01 \
  --image sac1647283583153:notesnook-ubuntu-24-04:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard
az network nsg rule create --resource-group my-notes-rg \
  --nsg-name notesnook-01NSG --name notesnook-ports --priority 1010 \
  --destination-port-ranges 80 5264 6264 7264 8264 9000 --access Allow --protocol Tcp

First boot generates every per-VM secret and starts the stack. Wait two to three minutes before the endpoints answer.

Step 3 - Connect to your VM

Connect to the VM over SSH:

ssh azureuser@<vm-ip>

Step 4 - Confirm the services are running

Check that the four systemd units are active:

systemctl is-active docker nginx notesnook notesnook-firstboot

Expected output:

active active active active

List the Compose services:

sudo docker compose -f /opt/notesnook/compose.yaml ps --format 'table {{.Service}}\t{{.Status}}\t{{.Ports}}'

Expected output: six services, all Up and healthy.

SERVICE STATUS PORTS identity-server Up 2 minutes (healthy) 0.0.0.0:8264->8264/tcp monograph-server Up 2 minutes (healthy) 0.0.0.0:6264->3000/tcp notesnook-db Up 2 minutes (healthy) 27017/tcp notesnook-s3 Up 2 minutes (healthy) 0.0.0.0:9000->9000/tcp notesnook-server Up 2 minutes (healthy) 0.0.0.0:5264->5264/tcp sse-server Up 2 minutes (healthy) 0.0.0.0:7264->7264/tcp

Each server answers its own health endpoint. The appliance also answers an unauthenticated /healthz on port 80, which you can point an Azure Load Balancer probe at:

for s in 'sync 5264/health' 'identity 8264/health' 'sse 7264/health' 'monograph 6264/api/health'; do n=${s%% *}; p=${s#* } printf ' %-11s -> HTTP %s\n' "$n" "$(curl -s -o /dev/null -w '%{http_code}' -m 6 http://127.0.0.1:$p)" done printf ' %-11s -> HTTP %s\n' 'appliance' "$(curl -s -o /dev/null -w '%{http_code}' -m 6 http://127.0.0.1/healthz)"

Expected output:

sync -> HTTP 200 identity -> HTTP 200 sse -> HTTP 200 monograph -> HTTP 200 appliance -> HTTP 200

Account registration is closed until you claim the instance. The server itself refuses a signup attempt:

curl -s -m 20 -X POST http://127.0.0.1:5264/users \
  --data-urlencode 'email=someone-else@example.com' \
  --data-urlencode 'password=hunter2hunter2' \
  --data-urlencode 'client_id=notesnook'

Expected output:

{"error":"Creating new accounts is not allowed."}

Step 5 - Retrieve your endpoint URLs and per-VM secrets

Print the credentials file:

sudo cat /root/notesnook-credentials.txt

Expected output (your values will differ, because every secret is unique to your VM):

# Notesnook Sync Server (self-hosted) - generated on first boot by
# notesnook-firstboot.service. These per-VM secrets are unique to THIS VM. NOTESNOOK_INSTANCE_NAME=sac-a6ad157c NOTESNOOK_MINIO_ACCESS_KEY=notesnook-s3-******** NOTESNOOK_MINIO_SECRET_KEY=****************************
# --- Point your Notesnook app at this server -------------------------------
# In the Notesnook app: Settings -> Servers -> Custom server URLs NOTESNOOK_SYNC_URL=http://20.85.247.166:5264 NOTESNOOK_IDENTITY_URL=http://20.85.247.166:8264 NOTESNOOK_SSE_URL=http://20.85.247.166:7264 NOTESNOOK_MONOGRAPH_URL=http://20.85.247.166:6264 NOTESNOOK_ATTACHMENTS_URL=http://20.85.247.166:9000

The object-store credentials are there for administration and backup. You do not need them to use Notesnook.

Step 6 - Claim the instance and create your account

Upstream's self-host default leaves registration open. Anyone who can reach your identity server could create an account on it. This image ships with registration closed and gives you a one-shot window instead:

sudo notesnook-claim

Expected output:

Opening the registration window on this instance... Registration is now OPEN on this instance for up to 60 minutes. Create your account now from the Notesnook app (desktop, mobile or web): Settings -> Servers -> Custom server URLs, then Sign up. The window closes automatically the moment your account exists - you do not need to run anything else. To close it immediately:
sudo /usr/local/sbin/notesnook-signup-guard.sh --close-now

Open Notesnook on your device. Go to Settings -> Servers -> Custom server URLs and enter the four URLs from Step 5 (sync, identity, events and published-notes). Save, then choose Sign up. Create your account with your e-mail address and a strong password.

Your password derives your encryption key, and it never leaves your device. There is no server-side password reset that can recover your notes. If you lose the password, nobody can decrypt the ciphertext on the server, including you. Save the password and your recovery key somewhere safe.

Within about twenty seconds of your account being created, the signup guard closes the window again. You can read the current state at any time:

grep '^DISABLE_SIGNUPS=' /opt/notesnook/.env

While the claim window is open, the command reports DISABLE_SIGNUPS=false. Once your account exists and the guard has closed the window, it reports DISABLE_SIGNUPS=true. Your instance should stay in that state from then on:

DISABLE_SIGNUPS=true

If you later need to add a second account, re-open the window deliberately with sudo notesnook-claim --force.

Step 7 - Prove that sync actually works

The image ships a self-test that exercises the real sync protocol, rather than just checking that the servers answer. It registers a throwaway account, pushes an encrypted note from one device, fetches it back as a different device, and asserts that the ciphertext returns byte-identical.

It then deletes the throwaway account and restores your registration setting:

sudo notesnook-selftest

Expected output:

[notesnook-selftest] registered throwaway account sac-selftest-....@notesnook.invalid [notesnook-selftest] pushed encrypted note sac-selftest-.... from device A [notesnook-selftest] fetched from device B (server metadata: {'synced': True}) [notesnook-selftest] ciphertext and iv round-tripped byte-identically (44 bytes of cipher) [notesnook-selftest] PASS: encrypted note synced and returned byte-identical [notesnook-selftest] throwaway account deleted

This is the check to run after any change to networking, DNS or TLS in front of the appliance.

Step 8 - Connect your other devices

Repeat the server-URL configuration from Step 6 on every device. Notesnook desktop, iOS, Android and web all support custom server URLs. Sign in with the same account, and your notes sync through your own server.

Each device registers itself with the sync server. A new device therefore performs a full reset-sync and pulls your whole encrypted note history down on first login.

Attachments upload directly from your device to the object store on port 9000, so that port must be reachable from every device, not just from the server.

Step 9 - Optional: enable e-mail for password reset and 2FA

Signup and sign-in work with no mail server configured, because a self-hosted instance marks new accounts confirmed on creation. E-mail is only needed for self-service password reset and e-mail two-factor codes.

To enable it, add your SMTP settings and restart the stack. First list the current settings:

sudo grep -n '^SMTP_' /opt/notesnook/.env

Then fill in SMTP_HOST, SMTP_PORT, SMTP_USERNAME and SMTP_PASSWORD, then run sudo systemctl restart notesnook.service.

Health checks and troubleshooting

Point Azure Load Balancer or Application Gateway health probes at http://<vm-ip>/healthz, which returns 200 ok without authentication.

curl -s -o /dev/null -w 'appliance /healthz -> HTTP %{http_code}\n' http://127.0.0.1/healthz

If a server is not answering, check its logs:

sudo docker compose -f /opt/notesnook/compose.yaml logs --tail 40 notesnook-server

First boot writes its progress to the journal. Check it if the endpoint URLs or the credentials file are missing:

sudo journalctl -u notesnook-firstboot.service --no-pager | tail -30

The appliance refuses to open its public listeners if a known upstream default credential is ever live in its configuration. Run that guard by hand:

sudo /usr/local/sbin/notesnook-secret-guard.sh

Expected output:

[...] notesnook-secret-guard: OK - no known upstream default credential is live (5 deny-list entries checked)

If your VM's public IP changes, the endpoint URLs stored in /opt/notesnook/.env and in the credentials file still refer to the old address.

Update AUTH_SERVER_PUBLIC_URL, MONOGRAPH_PUBLIC_URL and ATTACHMENTS_SERVER_PUBLIC_URL in /opt/notesnook/.env. Restart with sudo systemctl restart notesnook.service, and re-enter the new URLs in your apps.

Licensing

This image bundles the Notesnook Sync Server, which is licensed under the GNU Affero General Public License v3.0. MongoDB (SSPL-1.0) and MinIO (AGPL-3.0) are included as internal dependencies of the appliance. The full notice, with the exact pinned image digests and upstream locations, ships in the image:

cat /usr/share/doc/sac/third-party-licences/BUNDLED-COMPONENTS-NOTICE.txt

The Notesnook client apps live in a separate repository under GPL-3.0. They are not part of this image, which ships the server only.

Support

SecureAnyCloud provides 24/7 support for this image. Contact sales@secureanycloud.com with your Azure subscription ID and the VM name.

Deploy on Azure

Launch Notesnook Sync Server on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure