SciNote ELN on Ubuntu 24.04 on Azure User Guide
Overview
This guide covers the deployment and configuration of SciNote on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. SciNote is an open source Electronic Lab Notebook (ELN) and laboratory inventory used by life science and research teams. It replaces paper notebooks and scattered spreadsheets with one structured, searchable record of the work: projects hold experiments, experiments hold tasks, and tasks hold protocol steps, results, tables and file attachments, so the reasoning behind an experiment and its data live together. Alongside the notebook it keeps a sample and reagent inventory, reusable protocol repositories, label printing, full text search, and PDF and project export, and teams collaborate on one instance with roles and permissions.
The cloudimg image ships the free and open source, Mozilla Public License 2.0 licensed SciNote 1.49.1, deployed the officially supported way using SciNote's own production Docker build, with PostgreSQL 18 as its database and a dedicated background worker for exports and notifications. Everything is captured into the VM, so your instance starts in seconds. The image ships with no database and no administrator account: on first boot each instance generates its own application encryption key, attachment signing secret, database password and administrator password, creates the administrator directly with that per-VM password so the well known upstream default account (admin@scinote.net) is never created, and proves the new administrator can sign in before the public port is opened. Backed by 24/7 cloudimg support.
SciNote is a trademark of its respective owner. This image is produced by cloudimg and is not affiliated with, endorsed by, or sponsored by BioSistemika or the SciNote project. It ships the free and open source self-hosted software, built from the official release.

What is included:
- SciNote 1.49.1 (the MPL-2.0 licensed Electronic Lab Notebook), built from the official release using SciNote's own production Docker image
- PostgreSQL 18, created empty on first boot and stored on a persistent volume, reachable only from the application containers
- A dedicated background jobs worker (delayed_job) for exports, notifications and long running tasks - there is no Redis and no Sidekiq to run or secure
- Docker Engine running the containers on a private network, with the app published to loopback only
- Host nginx as the single public listener on port
80, proxying with the full client host so browser sign-in works scinote.service,scinote-firstboot.serviceandscinote-postboot.serviceas systemd units, enabled and active on boot- A per-VM application encryption key, attachment signing secret, database password and administrator password, generated on first boot and never baked into the image
- The administrator created directly with the per-VM password, so the well known upstream default account is never created; proven to sign in before port
80opens - No open self-registration: further accounts are created by the administrator from the team settings
- No default login, no shipped secret and an empty database on first boot
- Ubuntu 24.04 LTS base with latest security patches applied at build time
- Azure Linux Agent for seamless cloud integration and SSH key injection
- 24/7 cloudimg support with guaranteed 24 hour response SLA
Prerequisites
- Active Azure subscription, SSH public key, VNet + subnet in target region
- Subscription to the SciNote listing on Azure Marketplace
Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) is a sensible starting point for a lab or small team. The image is tuned to fit it: the web server runs a single application worker, the background jobs worker runs one process, and PostgreSQL is sized small. For many concurrent users or heavy document conversion and report export, use Standard_D2s_v5 or larger. NSG inbound: allow 22/tcp from your management network and 80/tcp for the web app from the networks that use it.
Step 1: Deploy from the Azure Portal
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for SciNote 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). Then Review + create and Create.
Step 2: Deploy from the Azure CLI
RG="scinote-prod"; LOCATION="eastus"; VM_NAME="scinote-01"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/scinote-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az network vnet create -g "$RG" --name scinote-vnet --address-prefix 10.100.0.0/16 --subnet-name scinote-subnet --subnet-prefix 10.100.1.0/24
az network nsg create -g "$RG" --name scinote-nsg
az network nsg rule create -g "$RG" --nsg-name scinote-nsg --name allow-ssh --priority 100 \
--source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp
az network nsg rule create -g "$RG" --nsg-name scinote-nsg --name allow-http --priority 110 \
--destination-port-ranges 80 --access Allow --protocol Tcp
az vm create -g "$RG" --name "$VM_NAME" --image "$GALLERY_IMAGE_ID" \
--size Standard_B2s --storage-sku StandardSSD_LRS \
--admin-username azureuser --ssh-key-values "$SSH_KEY" \
--vnet-name scinote-vnet --subnet scinote-subnet --nsg scinote-nsg --public-ip-sku Standard
Replace <sub-id> and <version> with your subscription id and the image version shown on the listing, and <your-mgmt-cidr> with the network you administer from.
Step 3: First boot and per-instance credentials
On the first boot of each VM, SciNote generates its own per-instance secrets, creates its database, and creates the administrator account directly with a password unique to this VM before the web port is opened. Nothing is baked into the image. Give the instance a few minutes after first boot for the database to be created and migrated, then SSH in and read the credentials file (it is 0600 root:root):
sudo cat /root/scinote-credentials.txt

The file contains everything unique to this instance:
SCINOTE_ADMIN_EMAIL/SCINOTE_ADMIN_PASSWORD— the administrator login you use in the web UI. The administrator has full access, including team and user management.SCINOTE_URL— the web address of this instance, resolved from its public IP on first boot.POSTGRES_PASSWORD— the database role password. PostgreSQL is not published to any host port, so this is only used inside the private container network.
Confirm the services and containers are healthy:
sudo systemctl is-active docker scinote-firstboot scinote scinote-postboot nginx
active
active
active
active
active
sudo docker compose -f /opt/scinote/docker-compose.production.yml ps --format 'table {{.Service}}\t{{.Status}}\t{{.Ports}}'
SERVICE STATUS PORTS
db Up About a minute (healthy) 5432/tcp
jobs Up 57 seconds
web Up 57 seconds (healthy) 127.0.0.1:3000->3000/tcp
The web container publishes to loopback only and PostgreSQL has no host port at all; host nginx on port 80 is the single public door.
Step 4: The security model
The upstream SciNote seed data creates a default administrator (admin@scinote.net) with a publicly documented password when no administrator details are supplied. On a VM with a public IP that would be a real risk, so cloudimg closes it: on first boot the image supplies its own per-VM administrator name, email and password to the seed, so the administrator is created directly with a password unique to this VM and the well known default account is never created. The application encryption key (SECRET_KEY_BASE), which signs sessions and cookies, and the attachment signing secret are generated per-VM as well, so no two instances share a secret. A start-up guard refuses to launch the application at all if the encryption key or database password is empty, too short or a known default, and host nginx — the only public listener — does not start until first boot has proven the administrator can sign in.

ss -tln | grep -E ':(80|3000|5432) ' | awk '{print $1, $4}'
LISTEN 127.0.0.1:3000
LISTEN 0.0.0.0:80
LISTEN [::]:80
Exactly one account exists on a fresh instance, it is confirmed and can sign in, and the upstream default administrator is absent:
sudo docker exec scinote_db_production psql -w -U postgres -d scinote_production -tAc "SELECT email, CASE WHEN confirmed_at IS NULL THEN 'unconfirmed' ELSE 'confirmed' END FROM users" </dev/null
admin@scinote.local|confirmed
Step 5: Verify the end-to-end round-trip
The image ships a prover that exercises the whole security model through the public front door on port 80: it confirms the sign-in page answers, that the per-VM administrator signs in through the real login form and reads back an authenticated page, that a wrong password is rejected, and that the upstream default admin@scinote.net credential is rejected.
sudo /usr/local/sbin/scinote-roundtrip.sh

Step 6: Sign in to the web app
Open http://<vm-ip>/ in your browser and sign in with the SCINOTE_ADMIN_EMAIL and SCINOTE_ADMIN_PASSWORD from the credentials file.

Once signed in, your first stop should be your account settings, where you can change the administrator email and password from the per-VM values to your own. Then the projects dashboard is your home view. A project is the top level of the SciNote structure: it holds experiments, and each experiment holds tasks. Use New project to start one — the screenshot below shows a freshly created project, Antibody Validation Study 2026, sitting alongside the SciNote Examples project that ships as a ready made walk-through of the notebook.

Open a project and you see its experiments. The SciNote Examples project ships with a full set of worked examples — polymerase chain reaction, CRISPR transformations, antibody purification, SDS-PAGE and more — so you can see how a real notebook is organised before building your own.

Open an experiment and its tasks are laid out on a canvas, connected in the order they are carried out. Each task holds its protocol steps, results, tables and file attachments, and can be assigned a status and an owner. This is where the day to day work of the lab is recorded.

Adding your team
There is no open self-registration on this image, so nobody can create an account on your instance. To add a colleague, sign in as the administrator, open Team from the settings, and add the person there with a role — owner, technician, normal user or viewer. Email invitations require SMTP to be configured (see below).
Configuring email (SMTP)
SMTP is optional and ships unconfigured, so the notebook is fully usable without it; email is only needed for invitations and notifications. To enable it, add your mail server settings to the environment file and restart the service. Edit /opt/scinote/production.env and add the SMTP keys for your provider, for example with <your-smtp-host> and the account details from your provider, then restart:
sudo systemctl restart scinote
The available keys are SMTP_ADDRESS, SMTP_PORT, SMTP_DOMAIN, SMTP_USERNAME, SMTP_PASSWORD and MAIL_FROM.
Using your own domain
On first boot the instance sets its own address, WEB_SERVER_URL, to its public IP. SciNote uses that value to build links in emails and elsewhere, so if you reach the VM by a DNS name rather than its IP address you should update it. Point a hostname at the VM, then set WEB_SERVER_URL in /opt/scinote/production.env to that host — for example scinote.example.com if you terminate TLS on a reverse proxy in front — and restart the service:
sudo systemctl restart scinote
The same applies if the VM's public IP changes, for example after a deallocate and start.
Managing the service
# view recent application logs
sudo docker compose -f /opt/scinote/docker-compose.production.yml logs --tail 50 web
# restart the whole stack
sudo systemctl restart scinote
The complete corresponding source for the version shipped here, and the MPL-2.0 licence text, are recorded on the VM at /usr/share/doc/scinote/.
Support
cloudimg provides 24/7 support with a guaranteed 24 hour response SLA. Contact support@cloudimg.co.uk for assistance with this image.