Note Mark on Ubuntu 24.04 on Azure User Guide
Overview
Note Mark is a fast, minimal, multi-user self-hosted Markdown notes server. A Go backend API and a SolidJS single-page web app are shipped together in one official container image; every note is stored as portable Markdown organised into books, so individuals and teams get a private place to write, organise and share notes - with optional OIDC single sign-on - instead of relying on a hosted service. The cloudimg image runs the pinned official Note Mark container behind nginx over HTTPS: the container is published on the loopback interface only, and nginx terminates TLS in front of it. Backed by 24/7 cloudimg support.
What is included:
- Note Mark 1.0.3 (AGPL-3.0) from the official pinned container image
ghcr.io/enchant97/note-mark:1.0.3, baked into the image and published on loopback127.0.0.1:8080 - nginx terminating TLS on
:443in front of Note Mark, with:80redirecting to HTTPS - A per-VM administrator account and a per-VM token signing secret generated on first boot - no default or shared credential ships in the image
- Open self-registration disabled by default, so a fresh public VM cannot be signed up by a stranger
docker.service,note-mark.serviceandnginx.serviceas systemd units, enabled and active- 24/7 cloudimg support
Secure by default - a per-VM administrator credential
This image ships with no default or shared login: no user accounts and a blank token signing secret ship in the image. On first boot a one-shot service generates a unique AUTH_TOKEN__SECRET (the JWT signing key) for your VM, resolves your public URL, regenerates a per-VM TLS certificate, and seeds exactly one administrator account (admin) with a unique random password, which it writes to a root-only file. Note Mark is deliberately held back until that account exists, so there is never a window in which a fresh VM is reachable without an administrator. You retrieve the password over SSH and change it after your first login.

Prerequisites
An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a comfortable starting point; scale up for large teams or note volumes. NSG inbound: allow 22/tcp from your management network, and 80/tcp + 443/tcp from wherever you browse Note Mark (:80 only redirects to :443).
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Note Mark by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22), HTTP (80) and HTTPS (443). Then Review + create -> Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name note-mark \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name note-mark --port 80 --priority 1010
az vm open-port --resource-group <your-rg> --name note-mark --port 443 --priority 1020
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
The message of the day shows your VM's Note Mark URL and administrator username.
Step 4 - Confirm the services are running
Note Mark runs as three systemd units - the Docker engine, the Note Mark container and nginx:
systemctl is-active docker.service note-mark.service nginx.service
active
active
active
The Note Mark container is published only on the loopback interface; nginx is the only service bound to public ports (:80 and :443):
sudo ss -tln | grep -E ':(80|443|8080) '
LISTEN 0 511 0.0.0.0:443 0.0.0.0:*
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:8080 0.0.0.0:*
The web UI is served over HTTPS, and plain HTTP redirects to it:
curl -sk -o /dev/null -w 'https:// -> %{http_code}\n' https://127.0.0.1/
curl -s -o /dev/null -w 'http:// -> %{http_code}\n' http://127.0.0.1/
https:// -> 200
http:// -> 301

Step 5 - Read the per-VM administrator credential
On first boot the VM writes a root-only file with your Note Mark URL, the administrator username and the unique password generated for this VM:
sudo grep -E '^NOTEMARK_URL=|^NOTEMARK_ADMIN_USERNAME=' /root/note-mark-credentials.txt
NOTEMARK_URL=https://<vm-public-ip>
NOTEMARK_ADMIN_USERNAME=admin
The NOTEMARK_ADMIN_PASSWORD line in that file holds your unique password. Only the per-VM password authenticates - a wrong or blank password is rejected:
U=$(sudo grep '^NOTEMARK_ADMIN_USERNAME=' /root/note-mark-credentials.txt | cut -d= -f2-)
P=$(sudo grep '^NOTEMARK_ADMIN_PASSWORD=' /root/note-mark-credentials.txt | cut -d= -f2-)
curl -sk -o /dev/null -w 'per-VM password -> HTTP %{http_code}\n' -X POST https://127.0.0.1/api/auth/o/token \
-H 'Content-Type: application/json' --data "{\"grant_type\":\"password\",\"username\":\"$U\",\"password\":\"$P\"}"
curl -sk -o /dev/null -w 'wrong password -> HTTP %{http_code}\n' -X POST https://127.0.0.1/api/auth/o/token \
-H 'Content-Type: application/json' --data "{\"grant_type\":\"password\",\"username\":\"$U\",\"password\":\"wrong-nope\"}"
per-VM password -> HTTP 200
wrong password -> HTTP 401

Step 6 - First login
Open Note Mark in your browser (accept the self-signed certificate warning, or install a trusted certificate first - see below):
https://<vm-public-ip>/
Click Login, enter the administrator username (admin) and the NOTEMARK_ADMIN_PASSWORD from Step 5, then sign in. Open your profile to change the password immediately after your first sign-in.
Writing and rendering notes
Notes are organised into books (notebooks). Create a book, then add notes inside it. Note Mark uses Markdown, so headings, bold, lists, tables, task checklists and fenced code all render as you write, with your books and notes in the sidebar.

Markdown tables and task lists render inline, so structured notes stay readable:

Every note is plain, portable Markdown - easy to read, search and export:

Step 7 - The official pinned container image
Note Mark runs from the official upstream container image, pinned to an exact version and baked into the VM image (no runtime pull), published on the loopback interface only:
sudo docker ps --format 'table {{.Image}}\t{{.Status}}\t{{.Ports}}'
IMAGE STATUS PORTS
ghcr.io/enchant97/note-mark:1.0.3 Up 6 minutes 127.0.0.1:8080->8080/tcp

Adding more users
Note Mark is multi-user. Because open self-registration is disabled by default, you add accounts from the VM with the built-in user add command (each user gets their own books):
sudo docker run --rm --env-file /etc/note-mark/note-mark.env \
-v /var/lib/note-mark/data:/data ghcr.io/enchant97/note-mark:1.0.3 \
user add --username alice --password '<choose-a-strong-password>'
To let users register themselves instead, set ENABLE_INTERNAL_SIGNUP=true in /etc/note-mark/note-mark.env and run sudo systemctl restart note-mark. Note Mark also supports OIDC single sign-on - add your provider's settings to the same env file and restart the service.
Security updates
The image is captured fully patched (including Ubuntu phased updates) and unattended-upgrades stays enabled, so security patches keep flowing on your VM. There should be no held-back packages:
apt-mark showhold
The note-mark and nginx packages are managed by systemd units; the application itself is the pinned container image described above.

Your data
Note Mark stores its SQLite database and Markdown note files under /var/lib/note-mark/data:
sudo du -sh /var/lib/note-mark/data
Snapshot the VM's OS disk in Azure to back up your notes, or copy /var/lib/note-mark/data to external storage on a schedule.
Enabling a trusted TLS certificate
The image ships a per-VM self-signed certificate so HTTPS works out of the box; browsers will warn until you install a trusted certificate. For production, point a DNS A record at the VM's public IP, ensure 443/tcp is open in the NSG, then install certbot and let it manage the nginx certificate. Replace the placeholders with your own domain and email:
sudo apt-get update
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.com -m you@example.com --agree-tos
certbot configures the HTTPS server block and sets up automatic renewal. After it completes, set PUBLIC_URL=https://your-domain.example.com in /etc/note-mark/note-mark.env and run sudo systemctl restart note-mark, and Note Mark is available at your domain with a trusted certificate.
Upgrading Note Mark
Note Mark runs from a pinned container image. To upgrade within the 1.x line, edit the image tag in /etc/systemd/system/note-mark.service, then sudo docker pull ghcr.io/enchant97/note-mark:<new-tag>, sudo systemctl daemon-reload and sudo systemctl restart note-mark. Always snapshot the OS disk first, as the schema migrates on start. cloudimg support can assist with planning and performing upgrades.
Support
This image is backed by 24/7 cloudimg support covering deployment, upgrades, integrations, TLS termination and user administration. Contact us by email and chat.
Note Mark is a trademark of its respective owner. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.