SpiffArena on Ubuntu 24.04 on Azure User Guide
Overview
SpiffArena is the web application built around the SpiffWorkflow engine, and it turns a diagram into a running system. Analysts and subject matter experts draw a business process in the browser using standard BPMN notation, express the decisions inside it as DMN decision tables, and save it into a version controlled process model repository. The same diagram is then what actually executes: SpiffArena starts process instances from it, drives them through their tasks, gathers input through generated forms, evaluates the decision tables, runs script steps and records the full history of every instance. Because the model is the implementation, the gap between the process a team agreed and the process the software runs never opens up.
The cloudimg image installs SpiffArena from the pinned upstream release as a single self contained appliance: the workflow engine and API, the BPMN and DMN editor, the administrator interface, the background scheduler and a bundled database, all on one VM with nothing to assemble. Backed by 24/7 cloudimg support.
What is included:
- SpiffArena, installed from the pinned upstream tag v3.0.0 (LGPL-2.1), together with the SpiffWorkflow engine (LGPL-3.0) that upstream pins from source. Exact upstream URLs and refs for both are recorded on the VM at
/opt/spiffarena/SOURCE.txt - The React interface built at image build time and served as static files by nginx on port 80; the API runs under gunicorn with uvicorn workers bound to
127.0.0.1only - A bundled single file SQLite database in WAL mode, so there is no external database to run, and the background scheduler as its own service, so process instances advance with no message broker
- A git backed process model repository at
/var/lib/spiffarena/process-models, seeded with a runnable Hello World example you can open in the editor and execute immediately - No usable default login. SpiffArena's development sign in publishes a set of demo accounts (
admin/admin,nelson/nelson,malala/malala,oskar/oskarand more). This image deletes those files entirely and ships with no credential at all; a unique administrator username and password, OpenID signing key, client secret, session key and encryption key are generated on the first boot of each VM spiffarena-selftest.shshipped as a tool, so you can prove at any time that the workflow engine executes and that no publicly known credential works- Fully patched base with unattended security upgrades enabled
- 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 the recommended size and runs the API, the scheduler, nginx and the database comfortably; choose a larger size if you expect many concurrent modellers or long running processes with large data payloads. NSG inbound: allow 22/tcp from your management network and 80/tcp for the web interface. Nothing else needs to be open, because the API process is bound to loopback and is not reachable from the network.
The appliance serves plain HTTP on port 80 on the VM's own IP address so you can evaluate it immediately. Add a real hostname and a TLS certificate before you put business processes or their data on it - see "Set your own domain and TLS".
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for SpiffArena by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size (Standard_B2s); under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create followed by Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name spiff-arena \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
Then open the web port on the VM's NSG:
az vm open-port --resource-group <your-rg> --name spiff-arena --port 80 --priority 900
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 - Confirm the services are running
On first boot the image generates this VM's administrator credential and every application secret, points the application at the VM's own address, lays the database down from a pristine template, and starts the stack. Confirm all three services are active:
systemctl is-active nginx spiffarena spiffarena-scheduler
Expected output:
active
active
active
spiffarena is the API and workflow engine, spiffarena-scheduler is the background processor that advances waiting process instances, and nginx serves the web interface and proxies the API. First boot takes roughly thirty seconds; if a service is still starting, wait and re-run.

Step 5 - Check the health endpoint
The API exposes an unauthenticated readiness endpoint that queries the database, so a 200 here means more than "a port answered":
curl -s http://localhost/v1.0/status
Expected output:
{"ok":true,"can_access_frontend":true}
There is also a plain liveness endpoint for load balancer probes:
curl -s http://localhost/healthz
Expected output:
ok
Step 6 - Retrieve the per-VM administrator credentials
Every secret on this VM was generated on its first boot and exists nowhere else. Read them with:
sudo cat /root/spiffarena-credentials.txt
Expected output (your username, password and URL will differ - they are unique to your VM):
# SpiffArena on Ubuntu 24.04 LTS by cloudimg
# Generated on first boot 2026-08-07T09:22:41Z. Unique to this VM.
SPIFFARENA_URL=http://<vm-public-ip>
SPIFFARENA_ADMIN_USERNAME=spiffadmin-613eb2
SPIFFARENA_ADMIN_PASSWORD=************************
SPIFFARENA_OPENID_CLIENT_SECRET=************************
The administrator username is randomly generated as well as the password. That is deliberate, and Step 7 explains why.

Step 7 - Confirm SpiffArena's published demo logins do not work
SpiffArena's development sign in ships a set of accounts whose passwords are published in the project's own configuration: admin/admin, nelson/nelson, malala/malala, oskar/oskar, and several more in the local development profile. This image deletes those files from the installed package outright, so none of them exists. Prove it:
for u in admin nelson malala oskar jason kevin dan jon; do
R=$(curl -s -o /dev/null -w '%{redirect_url}' -X POST \
http://127.0.0.1:7000/openid/form_submit \
--data-urlencode "Uname=$u" --data-urlencode "Pass=$u" \
--data-urlencode 'redirect_uri=http://127.0.0.1/v1.0/login_return')
case "$R" in
*code=*) printf '%-16s -> ACCEPTED\n' "$u/$u" ;;
*) printf '%-16s -> rejected\n' "$u/$u" ;;
esac
done
Expected output - every one rejected:
admin/admin -> rejected
nelson/nelson -> rejected
malala/malala -> rejected
oskar/oskar -> rejected
jason/jason -> rejected
kevin/kevin -> rejected
dan/dan -> rejected
jon/jon -> rejected
There is a second, more important hardening in this image. SpiffArena's built in sign in is described by its own authors as "definitely not a production ready system", and its back channel token endpoint issues a signed administrator token without ever checking the password. That endpoint is only ever called by the API itself over loopback, so this image blocks it at nginx and it is unreachable from the network:
curl -s -o /dev/null -w 'POST /openid/token -> HTTP %{http_code}\n' -X POST http://localhost/openid/token
curl -s -o /dev/null -w 'POST /openid/refresh -> HTTP %{http_code}\n' -X POST http://localhost/openid/refresh
Expected output:
POST /openid/token -> HTTP 403
POST /openid/refresh -> HTTP 403
Between those two things - no account whose name or password is published anywhere, and no reachable endpoint that skips the password check - there is nothing on this VM that an attacker can guess. The sign in page itself, which does verify the password, is unaffected and works normally.

Step 8 - Sign in to the web interface
Open http://<vm-public-ip>/ in a browser. You are redirected to the sign in form. Enter the username and password from Step 6.
After signing in you land on the home screen, with Processes, Process Instances, Data Stores, Messages and Configuration in the navigation. Choose Processes to see the process model repository, which ships with the cloudimg Examples group.

Step 9 - Open the example model in the BPMN editor
Expand cloudimg Examples, choose the Hello World model, open the Files tab and select hello-world.bpmn.
The bundled BPMN editor opens with the full modelling palette on the left and a properties panel on the right. The example is three nodes: a start event, a script task named Set greeting that assigns two process variables, and an end event. Drag new elements from the palette, connect them, edit their properties, and press Save - the model is written back to the git repository on the VM, so every change to a business process is versioned.

Step 10 - Run a process instance
Return to the Hello World model page and press Start. SpiffArena creates a process instance, runs it, and takes you to the instance view.
The instance page shows the status, who started it, when it started and finished, and the git revision of the model that was executed. The Diagram tab renders the same BPMN diagram with every completed element shaded, so you can see exactly which path the instance took. Milestones, Events, Messages and Tasks hold the rest of the audit trail, including the data each task produced.

Step 11 - Run the shipped self test
The image ships a self test that exercises the whole appliance end to end - the health endpoint, the rejection of every published demo login, your own credential, the blocked token endpoint, the loopback binding, and a real process instance created, executed and checked for the value its script task computed:
sudo /usr/local/sbin/spiffarena-selftest.sh
Expected output:
process instance 6 ran to completion as 'spiffadmin-613eb2'
OK
Anything other than OK on the final line is a failure with the reason printed above it. Run it after any configuration change.

Step 12 - Add your own process models
Process models live in a git repository on the VM. You can create them entirely from the web interface - Processes then Add Process Group, then add a model and a BPMN file inside it - or work directly on disk:
sudo ls -R /var/lib/spiffarena/process-models
Expected output:
/var/lib/spiffarena/process-models:
cloudimg-demo
/var/lib/spiffarena/process-models/cloudimg-demo:
hello-world
process_group.json
/var/lib/spiffarena/process-models/cloudimg-demo/hello-world:
hello-world.bpmn
process_model.json
Each group directory holds a process_group.json, and each model directory a process_model.json naming its primary BPMN file and process id. Because the directory is a git working tree, you can inspect the history of every model:
sudo -u spiffarena git -C /var/lib/spiffarena/process-models log --oneline
Expected output:
8276a04 cloudimg: seed example process models
To pull a library of models in from your own repository, add it as a remote and merge, or clone into a subdirectory - the application picks up whatever is on disk.
Step 13 - Back up
Everything that is yours lives in two places: the database and the process model repository.
sudo install -d -m 0700 /var/backups/spiffarena
sudo sqlite3 /var/lib/spiffarena/db.sqlite3 ".backup '/var/backups/spiffarena/db-$(date +%F).sqlite3'"
sudo tar -czf /var/backups/spiffarena/process-models-$(date +%F).tar.gz -C /var/lib/spiffarena process-models
sudo ls -lh /var/backups/spiffarena
.backup takes a consistent snapshot with the application running, so no downtime is needed. Copy both files off the VM - Azure Blob Storage or any object store is fine - and keep /etc/spiffarena/spiffarena.env with them if you want a byte for byte restore, since it holds this VM's encryption key.
Step 14 - Change the administrator password, or add users
Accounts for the built in sign in are defined in one file:
sudo cat /etc/spiffarena/permissions.yml
Edit the password: value under your administrator to change it, or add another user and put their email address into a group. Then restart the API so the change is picked up:
sudo systemctl restart spiffarena.service
The groups: and permissions: blocks control who can do what; uri: /* with actions: [all] is full administration, and finer grained rules can be scoped to individual process groups and models. Upstream documents the full permission URL syntax.
For anything beyond a small team, point SpiffArena at a real identity provider instead. It speaks standard OpenID Connect: set SPIFFWORKFLOW_BACKEND_AUTH_CONFIGS__0__uri, __client_id and __client_secret in /etc/spiffarena/spiffarena.env to your Entra ID, Keycloak or Okta tenant and restart. Pointing that URI anywhere other than this VM's own /openid also removes the built in sign in from the application entirely.
Set your own domain and TLS
The image serves plain HTTP so it works the moment it boots. Before putting real business processes on it, give it a hostname and a certificate.
Point a DNS A record at the VM's public IP, then:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d spiff.example.com --redirect --agree-tos -m you@example.com
Certbot edits the nginx site in place and installs a renewal timer. Then tell the application its new address, because the sign in redirect and the browser bundle both use it:
sudo sed -i \
-e 's|^SPIFFWORKFLOW_BACKEND_URL=.*|SPIFFWORKFLOW_BACKEND_URL=https://spiff.example.com|' \
-e 's|^SPIFFWORKFLOW_BACKEND_URL_FOR_FRONTEND=.*|SPIFFWORKFLOW_BACKEND_URL_FOR_FRONTEND=https://spiff.example.com|' \
-e 's|^SPIFFWORKFLOW_BACKEND_CORS_ALLOW_ORIGINS=.*|SPIFFWORKFLOW_BACKEND_CORS_ALLOW_ORIGINS=https://spiff.example.com|' \
-e 's|^SPIFFWORKFLOW_BACKEND_AUTH_CONFIGS__0__uri=.*|SPIFFWORKFLOW_BACKEND_AUTH_CONFIGS__0__uri=https://spiff.example.com/openid|' \
/etc/spiffarena/spiffarena.env
sudo sed -i "s|BACKEND_BASE_URL = '[^']*'|BACKEND_BASE_URL = 'https://spiff.example.com/v1.0'|" \
/opt/spiffarena/frontend/index.html
sudo systemctl restart spiffarena.service spiffarena-scheduler.service
Then tighten the NSG so port 80 only serves the certbot challenge and redirect, and restrict port 22 to your management network.
Maintenance and production notes
Patching. Unattended security upgrades are enabled, so the OS keeps itself current. The application is pinned to upstream tag v3.0.0; to move to a newer release, take a backup first, then follow upstream's release notes.
Sizing. On Standard_B2s the whole appliance idles at well under 1 GiB of RAM, leaving comfortable headroom. Scale up if you run many concurrent process instances or models that carry large data payloads. The image deliberately ships no swap, because Azure manages swap on the ephemeral resource disk.
Where things live. Application at /opt/spiffarena, configuration at /etc/spiffarena, database and process models at /var/lib/spiffarena, logs via journalctl -u spiffarena.
Scaling beyond one VM. This image is upstream's own minimal deployment, hardened. If you outgrow it, upstream's next steps are an external MySQL or PostgreSQL database and a Redis or RabbitMQ broker for Celery. Both are configured in /etc/spiffarena/spiffarena.env - point SPIFFWORKFLOW_BACKEND_DATABASE_URI at the external database and set SPIFFWORKFLOW_BACKEND_CELERY_ENABLED=true with a broker URL - so the appliance is a starting point rather than a dead end.
Support. Every cloudimg image comes with 24/7 support. Contact us through cloudimg.co.uk.