Spin on Ubuntu 24.04 on Azure User Guide
Overview
This guide covers the deployment and configuration of Spin on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Spin is an open source runtime for building and running WebAssembly serverless applications and microservices. It is a single self contained binary that loads a WebAssembly component and serves it over HTTP, so you run event driven microservices with the fast cold starts, strong sandboxing and tiny footprint of WebAssembly, without managing a language runtime or a container for each service.
The image installs Spin 4.0.2 from the official prebuilt release binary, run under systemd as an unprivileged spin user. The Spin runtime is bound to the loopback interface on port 3000, and nginx terminates HTTPS on port 443 and reverse proxies requests to it, so the WebAssembly application itself is never directly exposed to the network. Plain HTTP on port 80 permanently redirects to HTTPS. A small prebuilt demo WebAssembly HTTP component ships in the application directory so a freshly launched instance immediately demonstrates end to end WebAssembly execution.
Secure by default. The Spin application is bound to loopback only, so the sole network reachable surface is the nginx TLS reverse proxy. The demo component runs inside the Spin sandbox with no ambient access to the host. On the first boot of every VM a one shot service generates a fresh per instance self signed TLS certificate bound to that instance address, so the server answers over HTTPS immediately. Spin has no login, so no shared or default credential of any kind ships in the image.
What is included:
-
Spin 4.0.2 from the official prebuilt release binary, run under systemd as the unprivileged
spinuser (spin.service), bound to127.0.0.1:3000 -
nginx terminating HTTPS on port 443 and reverse proxying to the Spin runtime, with an HTTP to HTTPS permanent redirect on port 80 (
nginx.service) -
A demo WebAssembly HTTP component at
/opt/spin-app/index.wasmthat echoes your request path and reports a value it computes at request time, proving the Spin runtime executes the component rather than serving a static file (replace it with your own component) -
The whole runtime in a single prebuilt binary with no separate language runtime to install and no container image to build for each microservice
-
The Spin application bound to loopback only, so only the nginx TLS proxy is network reachable
-
A per instance self signed TLS certificate generated on first boot, ready for you to front with your own domain and a trusted certificate
-
The Spin application manifest in one readable file,
/opt/spin-app/spin.toml, that you version control
Prerequisites
-
Active Azure subscription, SSH public key, VNet + subnet in target region
-
Subscription to the Spin listing on Azure Marketplace
-
Network Security Group rules allowing TCP 22 (admin) from your management network, and TCP 443 (HTTPS) plus TCP 80 (HTTP redirect) from the clients that will reach your application
Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) is ample for a demo or a small application. Size up for production microservice workloads and higher request concurrency.
Step 1: Deploy from the Azure Portal
- Open the Spin offer on the Azure Marketplace and select Get It Now, then Create.
- Choose your subscription, resource group and region.
- Set the VM size (
Standard_B2sor larger), your SSH public key and the admin usernameazureuser. - On the Networking tab, attach a Network Security Group that allows inbound TCP 22 from your management network and TCP 443 and TCP 80 from your users.
- Review and create. When the deployment completes, note the VM public IP address.
Step 2: Deploy from the Azure CLI
az vm create \
--resource-group my-rg \
--name my-spin \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_rsa.pub \
--public-ip-sku Standard
# open HTTPS (443), the HTTP redirect (80) and SSH (22)
az vm open-port --resource-group my-rg --name my-spin --port 443 --priority 1001
az vm open-port --resource-group my-rg --name my-spin --port 80 --priority 1002
Step 3: First boot
On the first boot the spin-firstboot.service one shot unit generates a fresh per instance self signed TLS certificate (bound to this VM's IP address), writes an informational note to /root/spin-info.txt, then starts spin.service and nginx.service. This takes a few seconds. SSH in as azureuser:
ssh azureuser@<vm-public-ip>
Step 4: Confirm the services are running
Confirm spin.service and nginx.service are active, check the Spin version, and confirm the runtime is bound to loopback on port 3000 while nginx is listening on port 443 (HTTPS) and port 80 (the redirect):
sudo systemctl is-active spin nginx
/usr/local/bin/spin --version
sudo ss -tln | grep -E '127.0.0.1:3000|:443 |:80 '
You should see active for both services, a version line reporting spin 4.0.2, the Spin runtime listening on 127.0.0.1:3000, and nginx listening on *:443 and *:80.

Step 5: Verify WebAssembly execution over HTTPS
The demo component at /opt/spin-app/index.wasm is executed by the Spin runtime for every request: it echoes the request path back to you and reports a value it computes at request time (6 x 7), so you can prove the Spin runtime is executing the WebAssembly component rather than serving a static file. Fetch it over HTTPS (the -k flag accepts the per instance self signed certificate). Note that the path you request is echoed back in the response, which a static file server cannot do:
curl -ks https://127.0.0.1/hello-from-cloudimg | sed -e 's|</tr>|\n|g' -e 's/<[^>]*>//g' | grep -iE 'produced by a WebAssembly|Request path|wasm-exec-check'
curl -ksI https://127.0.0.1/ | grep -i x-cloudimg-spin
The response reports that it was produced by a WebAssembly component executed by the Spin runtime, echoes the request path /hello-from-cloudimg, and shows the WASM execution check (6 x 7) evaluating to 42 on the server, plus an x-cloudimg-spin: executed-42 response header.

Open https://<vm-public-ip>/ in a browser (your browser will warn about the self signed certificate until you front the instance with a trusted certificate in Step 8). The demo landing page confirms the WebAssembly runtime and echoes your request path:

Step 6: HTTP to HTTPS redirect and the loopback bound runtime
Plain HTTP on port 80 permanently redirects (301) to HTTPS. Confirm the redirect, then confirm the Spin application port 3000 is bound to loopback only, so the runtime itself is not reachable from the network (only the nginx TLS proxy is):
curl -ksI http://127.0.0.1/ | grep -iE 'HTTP/|location'
sudo ss -tln | grep ':3000 '
You should see HTTP/1.1 301 Moved Permanently with a Location: header pointing at the HTTPS URL, and the Spin runtime listening only on 127.0.0.1:3000.

Step 7: Deploy your own WebAssembly component
The application directory is /opt/spin-app. Build a WebAssembly component (Spin supports Rust, JavaScript, TypeScript, Python, Go and more), copy the compiled .wasm into the application directory, point the Spin manifest at it, then restart the service:
# copy your compiled component into the application directory
sudo cp ./my-component.wasm /opt/spin-app/index.wasm
# (optional) edit /opt/spin-app/spin.toml to change the route or add components
# apply the change
sudo systemctl restart spin
The manifest maps an HTTP route to a component with the [[trigger.http]] and [component.*] sections, so you can host multiple components on one runtime and route between them by path.
Step 8: Front your instance with a trusted certificate
The image ships with a per instance self signed certificate so it answers over HTTPS immediately. For production, point a domain's DNS A record at this instance and replace the self signed certificate that nginx serves with a trusted one (for example from your own certificate authority or an ACME client such as Certbot). The nginx site is at /etc/nginx/sites-available/cloudimg-spin; update the ssl_certificate and ssl_certificate_key paths to your trusted certificate and reload nginx:
sudo nano /etc/nginx/sites-available/cloudimg-spin
sudo nginx -t && sudo systemctl reload nginx
Step 9: Review the manifest and info note
The Spin application manifest is one readable file, and the first boot note records this instance's endpoint details:
sudo cat /opt/spin-app/spin.toml
sudo cat /root/spin-info.txt
The manifest shows the [[trigger.http]] route mapped to the demo component with source = "index.wasm". The info note records the HTTPS and HTTP URLs for this instance, confirms that the Spin runtime is bound to loopback only behind nginx, and confirms that Spin has no login and no shared credential.
Step 10: Security recommendations
- Restrict the management port. Allow inbound TCP 22 only from your management network, never
0.0.0.0/0. - Front your users with HTTPS only. Expose TCP 443 to your users and TCP 80 only so the redirect works. Switch to a trusted certificate for your own domain (Step 8) so browsers do not warn.
- Keep the runtime on loopback. This image binds the Spin application to
127.0.0.1:3000so only the nginx TLS proxy is network reachable. Leave it that way; terminate TLS at nginx rather than exposing the runtime port directly. - Keep the OS patched. The image ships fully patched with
unattended-upgradesenabled, so security updates continue automatically. Reboot after kernel updates. - Run your components as least privilege.
spin.servicealready runs as the unprivilegedspinuser with systemd sandboxing, and WebAssembly components run inside the Spin sandbox with no ambient host access.
Step 11: Support and Licensing
Spin is open source software distributed under the Apache License 2.0. This cloudimg image bundles Spin 4.0.2 on Ubuntu 24.04 LTS with nginx TLS termination, fully patched and hardened. cloudimg provides the packaging, configuration, security hardening and support; it does not modify the upstream Spin licence.
Deploy on Azure
Launch Spin on Ubuntu 24.04 from the cloudimg Azure Marketplace listing and follow this guide to deploy your WebAssembly component in minutes.
Need Help?
cloudimg provides 24/7 technical support for this Spin image. Contact support@cloudimg.co.uk for help with building WebAssembly components in Rust, JavaScript, Python and Go, the Spin manifest and HTTP routing, TLS and certificate options, and Spin version upgrades.
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.