Spin
Overview
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. You get the fast cold starts, strong sandboxing and small footprint of WebAssembly, with no language runtime to manage and no container to build for each service.
This SecureAnyCloud image runs Spin 4.0.2 on Ubuntu 24.04 on Azure, installed from the official prebuilt release binary and run under systemd as an unprivileged spin user. A small demo WebAssembly component ships in the image, so a new instance demonstrates end-to-end WebAssembly execution straight away.
Secure by default. The Spin runtime is bound to loopback only, so the nginx TLS reverse proxy is the sole network-reachable surface. The demo component runs inside the Spin sandbox with no ambient access to the host. Spin has no login, so no shared or default credential of any kind ships in the image.
What you get:
- Spin 4.0.2 from the official prebuilt release binary, run by systemd as the unprivileged
spinuser (spin.service) and bound to127.0.0.1:3000 - nginx terminating HTTPS on port 443 and reverse proxying to the Spin runtime, with a permanent HTTP to HTTPS 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, which shows that 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 reachable from the network
- A per-instance self-signed TLS certificate created by a one-shot service on the first boot of every VM and bound to that VM's address, ready for you to front with your own domain and a trusted certificate
- The application manifest in one readable file,
/opt/spin-app/spin.toml, that you can keep in version control
Prerequisites
You need an active Azure subscription, an SSH public key, a VNet with a subnet in the target region, and a subscription to the Spin listing on Azure Marketplace.
Configure the Network Security Group (NSG) to allow inbound TCP 22 from your management network. Also allow TCP 443 (HTTPS) and TCP 80 (HTTP redirect) from the clients that will reach your application.
Recommended VM size: Standard_B2s (2 vCPU, 4 GB RAM) is ample for a demo or a small application. Choose a larger size for production microservice workloads and higher request concurrency.
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 an NSG that allows inbound TCP 22 from your management network and TCP 443 and TCP 80 from your users.
- Select Review + create. When the deployment completes, note the VM public IP address.
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 1002First boot
On the first boot, the spin-firstboot.service one-shot unit generates a fresh self-signed TLS certificate bound to this VM's IP address. It then writes an informational note to /root/spin-info.txt and starts spin.service and nginx.service. This takes a few seconds. SSH in as azureuser:
ssh azureuser@<vm-public-ip>Confirm the services are running
Confirm that spin.service and nginx.service are active and check the Spin version. Then confirm that the runtime is bound to loopback on port 3000 while nginx listens 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 and a version line reporting spin 4.0.2. The Spin runtime listens on 127.0.0.1:3000, and nginx listens on *:443 and *:80.
Verify WebAssembly execution over HTTPS
The Spin runtime executes the demo component at /opt/spin-app/index.wasm for every request. The component echoes the request path and reports a value it computes at request time (6 x 7). This shows that Spin is running the WebAssembly component rather than serving a static file.
Fetch the page over HTTPS. The -k flag accepts the per-instance self-signed certificate. The response echoes the path you request, which a static file server cannot do:
curl -ks https://127.0.0.1/hello-from-sac | 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-sac-spinThe response states that a WebAssembly component executed by the Spin runtime produced it and echoes the request path /hello-from-sac. It shows the WASM execution check (6 x 7) evaluating to 42 on the server, and it carries an x-sac-spin: executed-42 response header.
Now open https://<vm-public-ip>/ in a browser. Your browser warns 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.
HTTP to HTTPS redirect and the loopback-bound runtime
Plain HTTP on port 80 permanently redirects (301) to HTTPS. Confirm the redirect, then confirm that Spin's port 3000 is bound to loopback only. 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 that points at the HTTPS URL. The Spin runtime listens only on 127.0.0.1:3000.
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) and 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 spinThe [[trigger.http]] and [component.*] sections of the manifest map an HTTP route to a component. You can host several components on one runtime and route between them by path.
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 certificate that nginx serves with a trusted one. You can get one from your own certificate authority or from an ACME client such as Certbot.
The nginx site is at /etc/nginx/sites-available/sac-spin. Update the ssl_certificate and ssl_certificate_key paths to your trusted certificate, then reload nginx:
sudo nano /etc/nginx/sites-available/sac-spin
sudo nginx -t && sudo systemctl reload nginxReview the manifest and info note
The Spin application manifest is a single 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.txtThe manifest maps the [[trigger.http]] route to the demo component with source = "index.wasm". The info note records the HTTPS and HTTP URLs for this instance. It also confirms that the Spin runtime is bound to loopback only behind nginx and that Spin has no login and no shared credential.
Security recommendations
- Restrict the management port. Allow inbound TCP 22 only from your management network, never
0.0.0.0/0. - Serve users over HTTPS only. Expose TCP 443 to your users, and expose 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:3000, so only the nginx TLS proxy is reachable from the network. Leave it that way, and terminate TLS at nginx rather than exposing the runtime port. - Keep the OS patched. The image is patched at build time and ships with
unattended-upgradesenabled, so security updates continue automatically. Reboot after kernel updates. - Run your components with least privilege.
spin.servicealready runs as the unprivilegedspinuser with systemd sandboxing. WebAssembly components run inside the Spin sandbox with no ambient host access.
Support and Licensing
Spin is open source software distributed under the Apache License 2.0. This SecureAnyCloud image bundles Spin 4.0.2 on Ubuntu 24.04 LTS with nginx TLS termination, patched at build time and hardened. SecureAnyCloud provides the packaging, configuration, security hardening and support. It does not modify the upstream Spin license.
Deploy on Azure
Launch Spin on Ubuntu 24.04 from the SecureAnyCloud Azure Marketplace listing, then follow this guide to deploy your WebAssembly component.
Need Help?
SecureAnyCloud provides 24/7 technical support for this Spin image. Contact sales@secureanycloud.com 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.
Deploy on Azure
Launch Spin 4.0.2 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
Overview
Spin is an open source runtime for building and running WebAssembly applications and microservices. It is a single, self-contained binary that loads a WebAssembly component and serves it over HTTP. You get the fast cold starts, strong sandboxing and small footprint of WebAssembly, with no language runtime to manage and no container image to build for each service.
This SecureAnyCloud image runs Spin 4.0.2 on AWS, installed from the official prebuilt release binary and run under systemd as an unprivileged spin user. A small demo WebAssembly component ships in the image, so a new instance demonstrates end-to-end WebAssembly execution straight away.
Secure by default. The Spin runtime is bound to loopback only, so the nginx TLS reverse proxy is the sole network-reachable application surface. Components run inside the WebAssembly sandbox with no ambient access to the host. Spin has no login, so no shared or default credential of any kind ships in the image.
What you get:
- Spin 4.0.2 from the official prebuilt release binary, run by systemd as the unprivileged
spinuser (spin.service) and bound to127.0.0.1:3000 - nginx terminating HTTPS on port 443 and reverse proxying to the Spin runtime, with a permanent HTTP to HTTPS 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, which shows that 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
- A dedicated EBS data volume mounted at
/opt/spin-app, so your components and their assets grow on a disk you can resize independently of the operating system disk - A per-instance self-signed TLS certificate created by a one-shot service on the first boot of every instance and bound to that instance's address, ready for you to front with your own domain and a trusted certificate
- The application manifest in one readable file,
/opt/spin-app/spin.toml, that you can keep in version control
Prerequisites
You need an active AWS account, a subscription to the Spin listing on AWS Marketplace, and an EC2 key pair in your target region. If your users reach the instance directly, you also need a VPC subnet with a route to the internet.
Attach a security group that allows inbound TCP 22 (administration) from your management network only. Also allow TCP 443 (HTTPS) and TCP 80 (the HTTP redirect) from the clients that will reach your application.
Recommended instance type: m5.large (2 vCPU, 8 GB RAM). Choose a larger size for higher request concurrency or heavier components.
Connecting to your instance
Connect over SSH on port 22 using the EC2 key pair you selected at launch. The login user depends on the operating system variant you launched.
Operating system variant SSH login user Example
Ubuntu 24.04 ubuntu ssh -i my-key.pem ubuntu@<public-ip>
Spin has no login and no password, so there is no credential to retrieve. The first boot writes an informational note to /root/spin-info.txt that records this instance's endpoint details.
Launch the instance
- Open the Spin listing on AWS Marketplace and choose Continue to Subscribe, then Continue to Configuration and Continue to Launch.
- Choose the region, the
m5.largeinstance type (or larger), your VPC subnet and your EC2 key pair. - Attach a security group that allows inbound TCP 22 from your management network and TCP 443 and TCP 80 from your users.
- Launch the instance, then note its public IPv4 address.
First boot
On the first boot, the spin-firstboot.service one-shot unit resolves this instance's address from EC2 instance metadata. It generates a fresh self-signed TLS certificate bound to that address and writes the informational note to /root/spin-info.txt. Then systemd starts spin.service and nginx.service. This takes a few seconds.
SSH in as the login user for your variant:
ssh -i my-key.pem ubuntu@<public-ip>Confirm the services are running
Confirm that spin.service and nginx.service are active and check the Spin version. Then confirm that the runtime is bound to loopback on port 3000 while nginx listens 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 'Example output from a running instance:
active active spin 4.0.2 (bfc7543 2026-06-23) LISTEN 0 511 0.0.0.0:80 0.0.0.0:* LISTEN 0 511 0.0.0.0:443 0.0.0.0:* LISTEN 0 1024 127.0.0.1:3000 0.0.0.0:* LISTEN 0 511 [::]:80 [::]:* LISTEN 0 511 [::]:443 [::]:*Both services report active and the binary reports spin 4.0.2. The Spin runtime listens on 127.0.0.1:3000 only, while nginx listens on ports 443 and 80 on all interfaces.
Verify WebAssembly execution over HTTPS
The Spin runtime executes the demo component at /opt/spin-app/index.wasm for every request. The component echoes the request path and reports a value it computes at request time (6 x 7). This shows that Spin is running the WebAssembly component rather than serving a static file.
Fetch the page over HTTPS. The -k flag accepts the per-instance self-signed certificate:
curl -ks https://127.0.0.1/hello-from-sac \
| sed -e 's|<style>.*</style>||' -e 's|</title>|\n|g' -e 's|</h1>|\n|g' \
-e 's|</p>|\n|g' -e 's|</th>|: |g' -e 's|</tr>|\n|g' -e 's/<[^>]*>//g' \
| grep -iE 'produced by a WebAssembly|^Runtime|^Request path|^WASM execution'
curl -ksI https://127.0.0.1/ | grep -i x-sac-spinReal output from a running instance:
This response was produced by a WebAssembly component executed by the Spin runtime. Runtime: Fermyon Spin (WebAssembly component) Request path (echoed live): /hello-from-sac WASM execution check (6 x 7): wasm-exec-check: 42 x-sac-spin: executed-42The response echoes the path you requested, which a static file server cannot do. The 6 x 7 check evaluates to 42 on the server, and the response carries an x-sac-spin: executed-42 header.
Now open https://<public-ip>/ in a browser. Your browser warns about the self-signed certificate until you front the instance with a trusted certificate (Step 11). Accept the warning to continue. The demo landing page confirms the runtime and echoes your request path.
Request a different path and the component echoes that path instead, live, on every request.
The shipped manifest maps the wildcard route /... to the demo component, so any path is routed to it, including deep API-style paths.
HTTP to HTTPS redirect
Plain HTTP on port 80 permanently redirects (301) to HTTPS. A user who types the bare address still lands on the encrypted endpoint.
curl -ksI http://127.0.0.1/ | grep -iE 'HTTP/|location'Real output from a running instance:
HTTP/1.1 301 Moved Permanently Location: https://127.0.0.1/Confirm the runtime is not reachable from the network
The Spin application listens on 127.0.0.1:3000 only, so the plaintext runtime port is never exposed. Only the nginx TLS proxy is reachable. Confirm the binding, then check that the port refuses a connection on the instance's own network address:
sudo ss -tln | grep ':3000 '
HOSTIP=$(hostname -I | awk '{print $1}') if curl -s --max-time 5 -o /dev/null "http://${HOSTIP}:3000/"; then echo "UNEXPECTED: the Spin application port answered off loopback"; exit 1 else echo "refused (exit $?) - correct: the Spin runtime is bound to loopback only" fiReal output from a running instance:
LISTEN 0 1024 127.0.0.1:3000 0.0.0.0:* refused (exit 7) - correct: the Spin runtime is bound to loopback onlyKeep it that way. Terminate TLS at nginx rather than exposing the runtime port directly.
Review the application directory and manifest
The application directory /opt/spin-app is a dedicated EBS volume, separate from the operating system disk. You can resize it as your components and assets grow. The Spin manifest is a single readable file:
findmnt -no SOURCE,TARGET,FSTYPE,SIZE /opt/spin-app
sudo cat /opt/spin-app/spin.tomlReal output from a running instance:
/dev/nvme1n1 /opt/spin-app ext4 19.5G spin_manifest_version = 2 [application] name = "sac-spin-demo" version = "0.1.0" authors = ["sac"] description = "sac Spin demo WebAssembly HTTP component (proves WASM execution)" [[trigger.http]] route = "/..." component = "sac-spin-demo" [component.sac-spin-demo] source = "index.wasm" allowed_outbound_hosts = []The [[trigger.http]] section maps an HTTP route to a component, and [component.*] points at the compiled WebAssembly file. You can host several components on one instance and route between them by path.
Deploy your own WebAssembly component
Build a WebAssembly component (Spin supports Rust, JavaScript, TypeScript, Python, Go and more) and copy the compiled .wasm onto the application volume. Point the manifest at it, then restart the service:
# copy your compiled component onto the dedicated application volume
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 spinThe image ships without a language toolchain. Build your component on your own workstation or CI runner, then copy the compiled file across.
Review the first boot note
The first boot records this instance's endpoint details. The note holds no secret, because Spin has no login:
sudo grep -vE '^[[:space:]]*$' /root/spin-info.txt \
| sed '/127\.0\.0\.1/!s/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/INSTANCE-IP/g'Example output from a running instance. The sed above masks this instance's own public address as INSTANCE-IP. On your instance, those lines carry its real address:
# Spin (sac AWS Marketplace image)
# Spin is a WebAssembly application runtime with NO login and NO password.
# This note is informational and holds no secret - it is unique to THIS instance. spin.version=4.0.2 spin.binary=/usr/local/bin/spin spin.app_manifest=/opt/spin-app/spin.toml spin.app_component=/opt/spin-app/index.wasm spin.listen=127.0.0.1:3000 (loopback only; nginx terminates TLS in front) nginx.tls_cert=/etc/nginx/sac/certs/site.crt (per-instance self-signed; SAN=INSTANCE-IP) site_ip=INSTANCE-IP HTTPS_URL=https://INSTANCE-IP/ HTTP_URL=http://INSTANCE-IP/ (permanently redirects to the HTTPS URL)
#
# Secure by default: the Spin application server listens on loopback only; the only
# network-reachable application surface is the nginx TLS reverse proxy on :443.
#
# Deploy your own WebAssembly component: build a WASM component, copy it into
# /opt/spin-app, point /opt/spin-app/spin.toml at it, then:
sudo systemctl restart spin
# User guide: https://secureanycloud.com/stacks/spin/Confirm the certificate is unique to this instance
Every instance generates its own certificate at first boot, so no certificate is baked into the image and no two instances share one. Confirm the certificate subject and validity window:
sudo openssl x509 -in /etc/nginx/sac/certs/site.crt -noout -subject -dates \
| sed 's/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/INSTANCE-IP/g'Real output from a running instance, with the address masked by the sed above:
subject=O = sac, CN = INSTANCE-IP notBefore=Jul 26 02:17:48 2026 GMT notAfter=Jul 23 02:17:48 2036 GMTThe common name is this instance's own address, generated at its own first boot.
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 record at this instance and replace the certificate that nginx serves with a trusted one. You can get one from your own certificate authority or from an ACME client such as Certbot. Alternatively, terminate TLS at an AWS Application Load Balancer in front of the instance.
The nginx site is at /etc/nginx/sites-available/sac-spin. Update the ssl_certificate and ssl_certificate_key paths, then reload nginx:
sudo nano /etc/nginx/sites-available/sac-spin
sudo nginx -t && sudo systemctl reload nginxSecurity recommendations
- Restrict the management port. Allow inbound TCP 22 only from your management network, never
0.0.0.0/0. - Serve users over HTTPS only. Expose TCP 443 to your users, and expose TCP 80 only so the redirect works. Switch to a trusted certificate for your own domain (Step 11) so browsers do not warn.
- Keep the runtime on loopback. This image binds the Spin application to
127.0.0.1:3000, so only the nginx TLS proxy is reachable from the network. Leave it that way. - Keep the OS patched. The image ships fully patched with
unattended-upgradesenabled, so security updates continue automatically. Reboot after kernel updates. - Run your components with least privilege.
spin.servicealready runs as the unprivilegedspinuser with systemd sandboxing. WebAssembly components run inside the Spin sandbox with no ambient host access. Declare only the outbound hosts a component needs inallowed_outbound_hosts. - Snapshot the application volume.
/opt/spin-appis a dedicated EBS volume. Include it in your EBS snapshot policy, and enable encryption at rest if your policy requires it.
Support and Licensing
Spin is open source software distributed under the Apache License 2.0. This SecureAnyCloud image bundles Spin 4.0.2 with nginx TLS termination, fully patched and hardened. SecureAnyCloud provides the packaging, configuration, security hardening and support. It does not modify the upstream Spin license.
Deploy on AWS
Launch Spin from the SecureAnyCloud AWS Marketplace listing, then follow this guide to deploy your WebAssembly component.
Need Help?
SecureAnyCloud provides 24/7 technical support for this Spin image. Contact sales@secureanycloud.com for help with building WebAssembly components in Rust, JavaScript, TypeScript, Python and Go, the Spin manifest and HTTP routing, TLS and certificate options, data volume sizing, 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.
Deploy on AWS
Launch Spin 4.0.2 WebAssembly Application Runtime on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com