Pluto.jl on Ubuntu 24.04 on Azure User Guide
Overview
Pluto.jl is a reactive notebook environment for the Julia programming language. Unlike a traditional notebook that remembers whatever you ran in whatever order, Pluto builds a dependency graph of your cells: when you change a value, every cell that uses it re-runs immediately and automatically, and when you delete a cell its variables disappear with it. The result is a notebook with no hidden state, that always reflects the code in front of you, and that a colleague can open and reproduce exactly.
The cloudimg image ships Pluto as a ready-to-use notebook server, not a bare Julia install. A pinned Julia runtime and a pinned Pluto are installed and precompiled into the image, so the notebook UI answers quickly on first launch with no toolchain to assemble. Pluto runs as a systemd service bound to the loopback connector 127.0.0.1:1234 behind an nginx reverse proxy on port 80. nginx protects the whole app with HTTP Basic Auth, and a unique login password is generated on the first boot of every VM, so the notebook UI is never open to anonymous access. A small reactive welcome notebook ships with the image so you can see reactivity working the moment you sign in. Backed by 24/7 cloudimg support.
What is included:
- Pluto.jl running as a systemd service (
pluto.service), the reactive notebook server for Julia - A pinned Julia runtime with Pluto precompiled into the image for a fast first launch
- The Pluto web UI on
:80, fronted by nginx with HTTP Basic Auth (useradmin) - A unique Basic Auth login password generated on first boot and recorded in a root-only file
- Pluto bound to the loopback connector
127.0.0.1:1234- never exposed directly to the network - A reactive welcome notebook preloaded under
/var/lib/pluto/notebooks pluto.service+nginx.serviceas systemd units, enabled and active- An unauthenticated
/healthzendpoint for Azure Load Balancer health probes - A websocket-friendly nginx proxy configuration for Pluto's live cell updates
- 24/7 cloudimg support
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 reasonable starting point for interactive notebooks; step up to a larger size for heavier numerical work. NSG inbound: allow 22/tcp from your management network and 80/tcp. Pluto serves plain HTTP on port 80; for production, terminate TLS in front of it with your own domain and consider adding 443/tcp (see Maintenance).
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Pluto.jl 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 -> Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name pluto \
--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 pluto --port 80 --priority 1010
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 - Confirm the services are running
systemctl is-active pluto.service nginx.service
Both systemd units report active. Pluto (the Julia notebook server) is bound to the loopback connector 127.0.0.1:1234, and nginx fronts it on port 80 behind HTTP Basic Auth:
ss -tlnp | grep ':1234' | sed 's/users:.*//'

Step 5 - Retrieve your login credentials
The appliance's network access gate is HTTP Basic Auth (user admin). The password is generated uniquely on the first boot of your VM and written to a root-only file:
sudo cat /root/pluto-credentials.txt
This file contains PLUTO_URL, PLUTO_USERNAME and PLUTO_PASSWORD. Store the password somewhere safe - it is the key to the reverse proxy gate in front of your notebooks.

Step 6 - Confirm the health endpoint
nginx serves an unauthenticated health endpoint for load balancers and probes:
curl -s http://localhost/healthz
It returns ok. This endpoint never requires authentication, so it is safe for an Azure Load Balancer health probe.
Step 7 - Confirm the login password is enforced
Because a password is set on first boot, an unauthenticated request to the notebook UI is rejected (HTTP 401), so nobody reaches your notebooks without the password:
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/
It prints 401. A wrong password is also rejected with 401; only the per-VM password from Step 5 returns 200:
curl -s -o /dev/null -w '%{http_code}\n' -u 'admin:<PLUTO_PASSWORD>' http://127.0.0.1/
This prints 200.

Step 8 - Sign in to Pluto
Browse to http://<vm-public-ip>/. Your browser first prompts for HTTP Basic Auth credentials - enter admin and the password from Step 5. Behind that gate you reach the Pluto welcome page, where you can create a new notebook or open an existing one.

Step 9 - Open the reactive welcome notebook
Under Open a notebook, enter the path /var/lib/pluto/notebooks/welcome.jl and select Open, then choose Run notebook code (Pluto opens a notebook in a safe preview first). The notebook runs and each cell shows its result: x = 3, y = 6 (because y = x * 2), and squares = [1, 4, 9]. The final line is a summary that reads its values from the cells above.

Step 10 - See reactivity in action
Change x in the first cell - for example to 8 - and press Shift+Enter to run it. You do not touch any other cell, yet y immediately becomes 16, squares becomes [1, 4, 9, 16, 25, 36, 49, 64], and the summary updates to say the squares sum to 204. That automatic re-run of every dependent cell is Pluto's reactivity: the notebook has no hidden state, so what you see always matches the code.

Step 11 - Create your own notebook
Return to the welcome page (the Pluto logo) and choose Create a new notebook to start from a blank canvas. Add a cell, type any Julia expression, and press Shift+Enter; add another cell that refers to a variable from the first, and watch it stay in step as you edit. Each notebook is saved as a plain .jl file under /var/lib/pluto/notebooks, so it reads as normal Julia source and works with version control.

Step 12 - Confirm where your notebooks live
Your notebooks are stored on the VM under /var/lib/pluto/notebooks, owned by the unprivileged pluto service user:
sudo ls -l /var/lib/pluto/notebooks
The shipped welcome.jl is there; every notebook you create or open by path is saved alongside it.
Maintenance
- Login password: the Basic Auth password in front of Pluto is set on first boot. To change it, run
sudo sh -c 'printf "admin:%s\n" "$(openssl passwd -apr1 NEW_PASSWORD)" > /var/lib/pluto/.htpasswd' && sudo systemctl reload nginx. - Notebooks: your work lives under
/var/lib/pluto/notebooksas plain.jlfiles - back up that directory to preserve your notebooks, and usegiton it if you want version history. - Restarting Pluto:
sudo systemctl restart pluto.servicerestarts the notebook server; open notebooks reconnect when you reload the page. - Julia packages: notebooks manage their own package environments inside the notebook file (Pluto's built-in package manager), so a notebook installs its own dependencies when it runs. The system-wide Julia lives under
/opt/juliaand its shared, precompiled depot is at/opt/julia/depot. - TLS: Pluto serves plain HTTP on port 80; front it with TLS (for example certbot with your own domain) before production use.
- Security patches: unattended-upgrades remains enabled so the OS continues to receive security updates automatically.
- License: Pluto.jl is MIT licensed and Julia is MIT licensed. This image ships the open source Pluto; the cloudimg charge covers packaging, security patching, image maintenance and support.
Support
cloudimg provides 24/7 expert support for this image. Contact support@cloudimg.co.uk.