Nightscout CGM Remote Monitor on Ubuntu 24.04 on Azure User Guide
Overview
This image runs Nightscout (cgm-remote-monitor) 15.0.7, the open source, self-hosted CGM remote monitor, on Ubuntu 24.04 LTS. Nightscout stores continuous glucose monitoring data in a private database and presents it on a live web dashboard, with a REST API that uploaders (xDrip+, AndroidAPS, Loop and others) write to and that caregivers can read from. It is run from /opt/nightscout on the Node.js 22 LTS runtime by an unprivileged nightscout system account under a systemd service that starts it on boot and restarts it on failure. A local MongoDB 7.0 Community database stores every glucose entry, treatment and profile.
Nightscout listens on the loopback address 127.0.0.1:1337 by design and is never exposed directly. nginx is installed as a reverse proxy on port 80 that forwards every request to Nightscout. Visitors reach the dashboard on the standard HTTP port.
Secured from first boot. Two secrets are generated fresh and unique on the first boot of every VM and are never baked into the image: the API secret (Nightscout's write/admin passphrase) and the MongoDB password. Access is set to denied by default, so the dashboard and API return nothing until the API secret is supplied - no default login ever ships.
What is included:
- Nightscout 15.0.7, run by systemd as the unprivileged
nightscoutuser - The Nightscout dashboard and REST API on
:80(nginx reverse proxy to127.0.0.1:1337) - A per-VM API secret and MongoDB password set on first boot and recorded in a root-only file
- MongoDB 7.0 Community, bound to loopback with authentication enabled, its data directory on a dedicated Azure data disk at
/var/lib/nightscout mongod.service,nightscout.serviceandnginx.serviceas systemd units, enabled and active- An unauthenticated
/healthzendpoint for Azure Load Balancer health probes - 24/7 cloudimg 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 for many concurrent followers. NSG inbound: allow 22/tcp from your management network and 80/tcp for the dashboard and API. Nightscout serves plain HTTP on port 80; for production, terminate TLS in front of it with your own domain, since glucose data is sensitive and uploaders should connect over HTTPS.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Nightscout 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) and HTTP (80). Review the dedicated data disk on the Disks tab, then Review + create then Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name nightscout \
--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 nightscout --port 80 --priority 1010
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 - Confirm the services are running
Nightscout is served by nginx on port 80, which reverse-proxies to the Node.js app on 127.0.0.1:1337, which in turn talks to MongoDB on 127.0.0.1:27017. Confirm all three services are active and the ports are bound:
systemctl is-active mongod nightscout nginx nightscout-firstboot
sudo ss -tlnp | grep -E ':80 |:1337 |:27017 '
Expected output:
active
active
active
active
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=...))
LISTEN 0 511 127.0.0.1:1337 0.0.0.0:* users:(("node",pid=...))
LISTEN 0 4096 127.0.0.1:27017 0.0.0.0:* users:(("mongod",pid=...))
LISTEN 0 511 [::]:80 [::]:* users:(("nginx",pid=...))

Step 5 - Retrieve your API secret
On the first boot of every VM, nightscout-firstboot.service generates a unique API secret and MongoDB password and writes them - together with the dashboard URL - into a root-only file. Read it with:
sudo cat /root/nightscout-credentials.txt
The API secret unlocks the dashboard and authorises writes to the REST API. Uploaders authenticate by sending the SHA1 hash of this secret as the api-secret HTTP header. Keep this file safe; anyone with the API secret can read and write your glucose data.

Step 6 - Confirm the stack and health endpoint
nginx serves an unauthenticated /healthz endpoint for load balancer probes. Confirm the Node.js and MongoDB versions and that the dashboard responds:
node -v
mongod --version | head -1
curl -sI http://127.0.0.1/healthz | head -1
curl -s http://127.0.0.1/ | grep -o '<title>[^<]*</title>'
Expected output:
v22.23.1
db version v7.0.37
HTTP/1.1 200 OK
<title>Nightscout</title>

Step 7 - Open the dashboard and authenticate
Browse to http://<vm-public-ip>/. Because access is denied by default, the dashboard first loads locked - no data is shown until you authenticate.

Click the padlock icon (or open the menu and choose Authentication Status), paste the API secret from Step 5, and confirm. Your browser stores the hashed secret locally and the dashboard unlocks, showing the glucose chart and the current reading.

Step 8 - Upload and read a glucose entry over the REST API
Uploaders and integrations use the Nightscout REST API v1. Authentication is the SHA1 hash of your API secret, sent as the api-secret header. The example below computes the hash, posts a sample SGV (sensor glucose value) entry, and reads it back. Run it on the VM (or from any host, replacing 127.0.0.1 with the VM's address):
SECRET=$(sudo grep '^NIGHTSCOUT_API_SECRET=' /root/nightscout-credentials.txt | cut -d= -f2-)
HASH=$(printf '%s' "$SECRET" | sha1sum | cut -d' ' -f1)
TS=$(date +%s%3N)
# An unauthenticated write is rejected (secure by default):
curl -s -o /dev/null -w 'unauthenticated POST -> HTTP %{http_code}\n' \
-X POST http://127.0.0.1/api/v1/entries.json -H 'Content-Type: application/json' \
--data "[{\"type\":\"sgv\",\"sgv\":120,\"date\":${TS},\"direction\":\"Flat\"}]"
# An authenticated write succeeds and echoes the stored entry:
curl -s -X POST http://127.0.0.1/api/v1/entries.json \
-H "api-secret: ${HASH}" -H 'Content-Type: application/json' \
--data "[{\"type\":\"sgv\",\"sgv\":120,\"date\":${TS},\"direction\":\"FortyFiveUp\",\"device\":\"guide-demo\"}]"
# Read it back:
curl -s -H "api-secret: ${HASH}" 'http://127.0.0.1/api/v1/entries.json?count=1'
Expected output:
unauthenticated POST -> HTTP 401
[{"type":"sgv","sgv":120,"date":1784599618173,"direction":"FortyFiveUp","device":"guide-demo","utcOffset":0,"sysTime":"2026-07-21T02:06:58.173Z","_id":"6a5ed4429f073ee2f61195b9"}]
[{"_id":"6a5ed4429f073ee2f61195b9","sysTime":"2026-07-21T02:06:58.173Z","type":"sgv","date":1784599618173,"device":"guide-demo","direction":"FortyFiveUp","sgv":120,"utcOffset":0,"mills":1784599618173}]
Point your CGM uploader app at http://<vm-public-ip> with the same API secret and your live readings will begin to flow onto the dashboard.
Step 9 - Review glucose history with Reports
The Reports page renders longer-term views of your data - daily glucose profiles, distribution, day-to-day charts and more - across a date range you choose. Open it from the menu, or browse to http://<vm-public-ip>/report/.

Step 10 - Configure your treatment profile
The Profile Editor is where you set the parameters Nightscout uses to interpret your data - insulin duration (DIA), insulin-to-carb ratios, insulin sensitivity factor, basal rates and your target glucose range. Open it from the menu, or browse to http://<vm-public-ip>/profile/. Adjust the values for your care plan and save.

Step 11 - Confirm data lives on the dedicated disk
All MongoDB data - every glucose entry, treatment and profile - lives under /var/lib/nightscout, which is a dedicated Azure data disk captured into the image and re-provisioned on every VM:
df -h /var/lib/nightscout | tail -1
findmnt -no SOURCE,TARGET,FSTYPE /var/lib/nightscout
sudo ls /var/lib/nightscout/mongodb | head -5
Expected output:
/dev/sdc 20G 301M 19G 2% /var/lib/nightscout
/dev/sdc /var/lib/nightscout ext4
WiredTiger
WiredTiger.lock
WiredTiger.turtle
WiredTiger.wt
WiredTigerHS.wt

Maintenance
- Back up your data: everything Nightscout stores lives in MongoDB's data files under
/var/lib/nightscout. Snapshot the data disk, or usemongodump/mongorestorefor logical backups. - Rotate the API secret: the API secret lives in the Nightscout process environment at
/etc/nightscout/nightscout.env(readable by root only). EditAPI_SECRET, thensudo systemctl restart nightscout. Re-enter the new secret in each browser and uploader. - Add followers and roles: Nightscout supports named access tokens with fine-grained roles from the Admin Tools page, so followers do not need the full API secret. See the upstream documentation.
- Security updates: unattended security upgrades are enabled, so the OS keeps itself patched. Reboot when a new kernel is installed.
- TLS: for production, front Nightscout with your own domain and a TLS-terminating reverse proxy or Azure Application Gateway - glucose data is sensitive and should travel over HTTPS.
Support
cloudimg provides 24/7/365 expert technical support for this image. Contact support@cloudimg.co.uk. Nightscout (cgm-remote-monitor) is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). This image is provided by cloudimg; additional charges apply for build, maintenance and 24/7 support.