Sirius Scan on Ubuntu 24.04 on Azure User Guide
Overview
Sirius Scan is an open source general purpose vulnerability scanner. Point it at a host or an address range and it discovers what is there, identifies the operating systems and services it finds, matches them against a CVE backed vulnerability database, and presents the result as a risk picture you can work through: severity across your estate, a per host inventory of open ports and detected software, and per finding detail with scoring and remediation guidance. Everything the console does is also available over a documented REST API, so vulnerability assessment can be scheduled and wired into an existing security workflow rather than run by hand.
The cloudimg image runs the complete Sirius Scan v1.1.0 release stack on one virtual machine: the Next.js web console, the Go REST API, the Go scan engine with a bundled Nmap 7.95, a PostgreSQL database for findings, a message broker that carries scan jobs to the engine, and a cache that tracks live scan progress. Every container is pinned to the exact published v1.1.0 release build by immutable digest, so the image is reproducible and cannot drift onto a moving tag. The upstream project publishes a working sample value for every shared secret in the stack, so this image generates its own on the first boot of each machine: the console administrator password, the database password, the session signing key and the internal service API key. None of them is baked into the image, the database ships empty with no administrator, and each published default is actively refused by the running services. Backed by 24/7 cloudimg support.
What is included:
- Sirius Scan v1.1.0, every component pinned by immutable digest to the published release build
- The web console on
:80behind nginx, with a per VM administrator account - The REST API on
:9001, authenticated with a per VMX-API-Key, plus an unauthenticated readiness probe for load balancer health checks - The scan engine with a bundled Nmap 7.95 and the
NET_RAWcapability it needs for host discovery - The agent gRPC listener on
:50051for remote scanning agents - PostgreSQL 15 holding hosts, ports, software inventory and vulnerability findings
- A message broker carrying scan jobs to the engine, and a Valkey cache holding scan state, templates and live progress, both confined to a private container network with no host port
- A per VM administrator password, database password, session signing key and internal service API key, generated on first boot and never shared between machines
docker.service,sirius-scan.serviceandnginx.serviceas systemd units, enabled and active- 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 whole stack comfortably. NSG inbound: allow 22/tcp from your management network and 80/tcp for the web console. Allow 9001/tcp as well if you intend to use the REST API or the console's system monitoring panels from a browser, and 50051/tcp only if you deploy remote scanning agents.
Sirius Scan is an active scanner: it sends real probe traffic to whatever you point it at. Only scan systems you own or have explicit written authorisation to test. Microsoft does not require pre approval to run penetration tests against your own Azure resources, but the Microsoft Cloud Unified Penetration Testing Rules of Engagement still apply, and scanning anything outside your own estate may breach your provider's terms or the law in your jurisdiction.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Sirius Scan 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 sirius-scan \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_rsa.pub \
--public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name sirius-scan --port 80 --priority 1001
az vm open-port --resource-group <your-rg> --name sirius-scan --port 9001 --priority 1002
Find the public address of the machine you just created:
az vm show --resource-group <your-rg> --name sirius-scan -d --query publicIps -o tsv
Step 3 - Confirm the stack is running
SSH to the VM as azureuser. On the very first boot the appliance generates its own secrets and starts the stack, which takes a little under two minutes; after that every boot is immediate. Check the three systemd units first:
systemctl is-active docker sirius-scan nginx
Expected output:
active
active
active
Then check the containers themselves. sirius-compose is a small wrapper that runs docker compose against this appliance's compose file with the per VM environment already wired in, so you never have to remember the paths:
sudo sirius-compose ps --format 'table {{.Service}}\t{{.Status}}'
Expected output:
SERVICE STATUS
sirius-api Up 9 minutes (healthy)
sirius-engine Up 9 minutes (healthy)
sirius-postgres Up 9 minutes (healthy)
sirius-rabbitmq Up 9 minutes (healthy)
sirius-ui Up 9 minutes (healthy)
sirius-valkey Up 9 minutes (healthy)
The seventh service, sirius-migrate, is a one shot schema migrator. It applies the core database schema and exits before the API and the engine are allowed to start, so it is correct for it not to appear in the running list.
Step 4 - Retrieve the per VM credentials
Every secret this appliance uses was generated for your machine alone on its first boot and written to a file readable only by root.
sudo cat /root/sirius-scan-credentials.txt
The file records the console URL, the administrator username and password, the REST API URL and key, and the PostgreSQL user and password. Nothing in it exists in the published image, and no two machines launched from this image share a value.

Read individual values without printing the whole file:
sudo grep -E '^SIRIUS_(URL|ADMIN_USERNAME)=' /root/sirius-scan-credentials.txt
Step 5 - Sign in to the web console
Open http://<vm-ip>/ in a browser. The sign in page asks for a username and a password: use the SIRIUS_ADMIN_USERNAME and SIRIUS_ADMIN_PASSWORD values from the credentials file.

After signing in you land on the Security Command Center, the dashboard that summarises total vulnerabilities, critical issues, active hosts and severity breakdown across everything you have scanned.

You can confirm the same credential works from the shell before you open a browser:
curl -s -c /tmp/sirius.jar -b /tmp/sirius.jar http://127.0.0.1/api/auth/csrf | head -c 40; echo
Step 6 - Run your first scan
Open Scan Dashboard from the left hand rail. Type a target into the target box and press Enter to commit it. Sirius accepts single addresses, CIDR ranges such as 10.0.0.0/24, dash ranges such as 192.168.1.1-10, and domain names. Choose a scan profile, then click Start Scan.
The dashboard tracks the scan live: elapsed time, the number of hosts discovered so far, and a severity tally that fills in as findings arrive.

You can also watch the scan from the shell, which is useful when driving scans from automation:
sudo bash -c 'K=$(grep -E "^SIRIUS_API_KEY=" /root/sirius-scan-credentials.txt | cut -d= -f2-); curl -s -m 20 -H "X-API-Key: $K" http://127.0.0.1:9001/api/v1/scans/status | head -c 200'; echo
Step 7 - Read the host inventory
Environment lists every host Sirius knows about, with its detected operating system, its open port count, its vulnerability count and a risk level. This is the working inventory the scanner builds up over time.

Click a host to open its detail page. The overview gives you the vulnerability count, the package count, the number of open ports and a risk score, with the discovered ports listed on the right and separate tabs for vulnerabilities, system detail, network detail and scan history.

The same inventory is available over the REST API:
sudo bash -c 'K=$(grep -E "^SIRIUS_API_KEY=" /root/sirius-scan-credentials.txt | cut -d= -f2-); curl -s -m 20 -H "X-API-Key: $K" http://127.0.0.1:9001/host/ | head -c 400'; echo
Step 8 - Use the REST API
The API listens on :9001. Every route except the readiness probe requires the per VM key in an X-API-Key header, so a caller without the key gets 401 rather than data.
curl -s -o /dev/null -w 'GET /host/ without a key -> HTTP %{http_code}\n' -m 15 http://127.0.0.1:9001/host/
Expected output:
GET /host/ without a key -> HTTP 401
With the key, the same route answers normally:
sudo bash -c 'K=$(grep -E "^SIRIUS_API_KEY=" /root/sirius-scan-credentials.txt | cut -d= -f2-); curl -s -o /dev/null -w "GET /host/ with the per VM key -> HTTP %{http_code}\n" -m 15 -H "X-API-Key: $K" http://127.0.0.1:9001/host/'
Expected output:
GET /host/ with the per VM key -> HTTP 200
To call the API from another machine, read the key out of the credentials file and send it as a header. Replace <vm-ip> with your machine's address:
curl -s -H "X-API-Key: <SIRIUS_API_KEY>" http://<vm-ip>:9001/host/
Step 9 - Health checks and load balancer probes
Two health endpoints exist and they answer different questions. /health is unauthenticated and reports only that the API process is up, which is exactly what an Azure Load Balancer probe should ask:
curl -s -m 10 http://127.0.0.1:9001/health
Expected output:
{"status":"healthy","timestamp":"2026-08-07T10:53:43.032454986Z","service":"sirius-api","version":"1.1.0"}
/api/v1/system/health is the one to use when you actually want to know whether the appliance is working. It requires the API key and reports each of the six services separately, so a dead database or a dead broker shows up instead of hiding behind a green process check:
sudo bash -c 'K=$(grep -E "^SIRIUS_API_KEY=" /root/sirius-scan-credentials.txt | cut -d= -f2-); curl -s -m 25 -H "X-API-Key: $K" http://127.0.0.1:9001/api/v1/system/health | head -c 400'; echo

Step 10 - Attach a remote scanning agent
The console's Get Agent button downloads the Sirius agent for your platform. Agents connect back to the engine's gRPC listener on port 50051 and let one console scan networks it cannot reach directly, such as a segmented subnet or a branch office.
If you deploy agents, open 50051/tcp in the network security group and restrict it to the source addresses your agents connect from:
az vm open-port --resource-group <your-rg> --name sirius-scan --port 50051 --priority 1003
Confirm the listener is up before you deploy an agent:
ss -tln | grep -E ':50051 ' || echo "agent gRPC listener not found"
Server Components
| Component | Version | Purpose |
|---|---|---|
| Sirius Scan | 1.1.0 | Vulnerability scanner: console, REST API and scan engine |
| Nmap | 7.95 | Host, port and service discovery, bundled inside the scan engine |
| PostgreSQL | 15 | Hosts, ports, software inventory and vulnerability findings |
| RabbitMQ | 3.12 | Scan job queue between the API and the scan engine |
| Valkey | 7.2 | Scan state, scan templates and live scan progress |
| nginx | 1.24 | Reverse proxy fronting the web console on port 80 |
| Docker Engine | 29.x | Container runtime for the whole stack |
| Ubuntu | 24.04 LTS | Base operating system |
Port Reference
| Port | Bound to | Purpose |
|---|---|---|
| 22 | all interfaces | SSH |
| 80 | all interfaces | Web console, through nginx |
| 9001 | all interfaces | REST API, authenticated with X-API-Key |
| 50051 | all interfaces | Agent gRPC listener for remote scanning agents |
| 5432 | 127.0.0.1 | PostgreSQL, protected by the per VM password |
| 3000 | 127.0.0.1 | The Next.js console process, reached through nginx |
| broker and cache | private container network | No host listener at all; reachable only by the Sirius services |

Filesystem Layout
| Mount | Size | Purpose |
|---|---|---|
/ |
29 GB | Root filesystem, holding the container images and the PostgreSQL volume |
/boot |
881 MB | Operating system kernel files |
/boot/efi |
105 MB | UEFI boot partition (Gen2 Hyper V) |
/mnt |
7.8 GB | Azure temporary resource disk, used for swap |
Key directories:
| Path | Purpose |
|---|---|
/etc/sirius-scan/compose.yaml |
The appliance compose file, with every image pinned by digest |
/etc/sirius-scan/sirius-scan.env |
The per VM secrets consumed by compose (0600 root:root) |
/etc/sirius-scan/secrets/sirius_api_key.txt |
The internal service API key mounted into the containers |
/etc/sirius-scan/rabbitmq.conf |
The message broker configuration |
/root/sirius-scan-credentials.txt |
The per VM credentials, readable only by root (0600 root:root) |
/var/lib/docker/volumes/sirius_postgres_data |
The PostgreSQL data directory holding your findings |
Managing the services
systemctl status sirius-scan --no-pager | head -5
Stop and start the whole stack through systemd:
sudo systemctl stop sirius-scan
sudo systemctl start sirius-scan
sudo systemctl restart sirius-scan
Work with individual containers through the wrapper:
sudo sirius-compose ps
sudo sirius-compose logs -f sirius-engine
sudo sirius-compose restart sirius-ui
Follow the appliance logs:
sudo journalctl -u sirius-scan -f
sudo journalctl -u sirius-scan-firstboot -n 50
Connect to the findings database with the per VM password:
PGPASSWORD='<SIRIUS_POSTGRES_PASSWORD>' psql -h 127.0.0.1 -U postgres -d sirius -c '\dt'
Scripts and Log Files
| Path | Purpose |
|---|---|
/usr/local/sbin/sirius-scan-firstboot.sh |
Generates every per VM secret and brings the stack up on first boot |
/usr/local/sbin/sirius-scan-roundtrip.sh |
Proves the per VM credentials work and every published default is refused |
/usr/local/bin/sirius-compose |
Runs docker compose against the appliance with its environment wired in |
/usr/local/sbin/cloudimg-mnt-swap.sh |
Creates swap on the ephemeral resource disk on every boot |
/var/lib/cloudimg/sirius-scan-firstboot.done |
First boot sentinel; its presence stops firstboot re running |
/var/lib/cloudimg/sirius-scan-bootstrap.ready |
Bootstrap marker the application unit is gated on |
journalctl -u sirius-scan |
Appliance service log |
sudo sirius-compose logs <service> |
Per container logs |
/var/log/nginx/access.log |
nginx access log |
On Startup
On the very first boot, sirius-scan-firstboot.service runs before the application. It resolves the machine's address, generates the administrator password, the database password, the session signing key and the internal service API key, writes them to /etc/sirius-scan/sirius-scan.env and /root/sirius-scan-credentials.txt, brings the stack up, proves the datastores are genuinely reachable and that the published default credentials are refused, and only then creates /var/lib/cloudimg/sirius-scan-bootstrap.ready.
That last step matters. sirius-scan.service carries ConditionPathExists on that marker, so on a first boot it physically cannot start until the secrets exist. The image ships without the marker, which is what guarantees the stack never comes up against an upstream default credential. On every subsequent boot the sentinel is present, firstboot exits immediately, and the containers restart normally.
You can confirm the gate is in place on your own machine:
grep -c ConditionPathExists /etc/systemd/system/sirius-scan.service
Expected output:
1
Troubleshooting
The console does not answer straight after launch. On a first boot the appliance is generating its secrets and initialising the database, broker and cache. Give it two minutes, then check sudo journalctl -u sirius-scan-firstboot -n 50.
systemctl status sirius-scan shows the unit as inactive with a condition failure. The bootstrap marker is missing, which means firstboot has not completed. Check the firstboot journal as above. Once firstboot succeeds the marker appears and the unit starts.
The console returns 502. nginx is up but the Next.js console is not yet listening. Check sudo sirius-compose ps and sudo sirius-compose logs --tail 60 sirius-ui.
/health says healthy but nothing works. /health reports only that the API process is alive. Ask the dependency aware endpoint instead, as in Step 9, and look for any service that is not up.
Sign in is refused. Use the username and password from /root/sirius-scan-credentials.txt on this machine. Credentials are unique per machine and none of the upstream sample credentials will work by design. sudo bash /usr/local/sbin/sirius-scan-roundtrip.sh walks the whole chain and prints which part is failing.
A scan starts but never produces findings. The engine consumes scan jobs off the message broker, so check it is running and attached with sudo sirius-compose ps and sudo sirius-compose logs --tail 60 sirius-engine.
Scanning a remote network finds nothing. Confirm the network security groups and firewalls between this machine and the target allow the probe traffic, and remember the engine scans from this machine's own network position. For networks it cannot reach directly, deploy a remote agent as in Step 10.
Security Recommendations
- Restrict the NSG so that
22/tcp,80/tcp,9001/tcpand50051/tcpare reachable only from the networks that need them. A vulnerability scanner holds a map of what is weak in your estate, so treat it as sensitive infrastructure. - Put the console behind your own TLS terminating load balancer or reverse proxy, or install a certificate on this machine, before exposing it beyond a trusted network.
- Change the console password from the account menu after your first sign in, and create named accounts for your team rather than sharing the administrator.
- Back up
/root/sirius-scan-credentials.txtsomewhere safe, then consider removing it from the machine. The appliance reads its secrets from/etc/sirius-scan/sirius-scan.env, not from that file. - Keep PostgreSQL on loopback and leave the broker and cache unpublished. Neither needs to be reachable from the network for this appliance to work.
- Treat the API key as a full access credential and rotate it if it is ever exposed.
- Only scan systems you own or are explicitly authorised to test, and keep a record of that authorisation.
- Leave unattended security updates enabled so the operating system keeps receiving patches.
Support
cloudimg provides 24/7 support for this image. Contact support@cloudimg.co.uk with your Azure subscription id and the VM name. For questions about Sirius Scan itself, see the Sirius Scan GitHub repository.