istSOS 4 on Ubuntu 24.04 on Azure User Guide
Overview
This guide covers the deployment and configuration of istSOS 4 on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. istSOS is an open source server for collecting, managing and publishing environmental and monitoring sensor data over open OGC standards. It implements the SensorThings API and the Sensor Observation Service data model, so you register sensors, things, observed properties and datastreams, then load and serve the time series observations they produce through a clean REST API and an interactive API explorer.
The image ships istSOS 4 (istsos4 v1.0.0) as a FastAPI application served by uvicorn behind nginx, backed by PostgreSQL 16 with the PostGIS spatial extension and a local Redis 7 instance used for token handling. The SensorThings API is served under the versioned path /istsos4/v1.1, with an interactive Swagger explorer at /istsos4/v1.1/docs. On every fresh customer virtual machine, istsos-firstboot.service rotates the istSOS administrator password, the PostgreSQL account passwords and the JWT signing key to per-VM strong values before the API is reachable, and the upstream default administrator login is proven dead. A demo network of five sensor stations, two observed properties and 1680 time series observations is seeded at build time, so the API and its explorer show real data the moment you launch.
What is included:
-
istSOS 4 (istsos4 v1.0.0) served by uvicorn from a Python 3.12 virtual environment at
/opt/istsos/venv -
istsos-api.service(uvicorn, 2 workers) fronted by nginx on TCP 80 -
istsos-firstboot.servicesystemd oneshot that rotates the admin password, the PostgreSQL passwords and the JWT signing key on first boot -
PostgreSQL 16 with the PostGIS 3.4 spatial extension as the SensorThings datastore (database
istsos) -
Redis 7 from the Ubuntu 24.04 main repo for token handling
-
The SensorThings API served at
/istsos4/v1.1with an interactive Swagger explorer at/istsos4/v1.1/docs -
Anonymous read access to the sensor collections for easy browsing, with every write requiring the administrator token
-
A seeded demo network: five sensor stations, two observed properties (rainfall and temperature), ten datastreams and 1680 observations
-
Ubuntu 24.04 LTS base with latest security patches applied at build time
-
Azure Linux Agent for seamless cloud integration and SSH key injection
-
24/7 cloudimg support with guaranteed 24 hour response SLA
Prerequisites
-
An active Azure subscription
-
A subscription to the istSOS 4 on Ubuntu 24.04 listing on Azure Marketplace
-
An SSH public key for VM authentication
-
A virtual network and subnet in the target region
Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) is sufficient for evaluation and small monitoring networks. Scale up to Standard_D2s_v5 or larger for production networks ingesting high frequency observations from many stations.
Step 1: Deploy from the Azure Portal
Navigate to Marketplace in the Azure Portal, search for istSOS 4, select the cloudimg publisher entry, and click Create.
On the Networking tab attach a network security group that allows inbound TCP 22 from your management IP range and TCP 80 from your trusted client networks. Do not expose port 80 to the public internet without TLS — the SensorThings API ships on plain HTTP by design; terminate TLS at a reverse proxy (see Step 12).
Click Review + create, wait for validation, then Create. Deployment takes around two minutes.
Step 2: Deploy from the Azure CLI
RG="istsos-prod"
LOCATION="eastus"
VM_NAME="istsos-01"
ADMIN_USER="azureuser"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/istsos-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az network vnet create \
--resource-group "$RG" \
--name istsos-vnet --address-prefix 10.96.0.0/16 \
--subnet-name istsos-subnet --subnet-prefix 10.96.1.0/24
az network nsg create --resource-group "$RG" --name istsos-nsg
az network nsg rule create \
--resource-group "$RG" --nsg-name istsos-nsg \
--name allow-ssh --priority 100 \
--source-address-prefixes "<your-mgmt-cidr>" \
--destination-port-ranges 22 --access Allow --protocol Tcp
az network nsg rule create \
--resource-group "$RG" --nsg-name istsos-nsg \
--name allow-http --priority 110 \
--source-address-prefixes "<your-mgmt-cidr>" \
--destination-port-ranges 80 --access Allow --protocol Tcp
az vm create \
--resource-group "$RG" --name "$VM_NAME" \
--image "$GALLERY_IMAGE_ID" \
--size Standard_B2s --storage-sku StandardSSD_LRS \
--admin-username "$ADMIN_USER" --ssh-key-values "$SSH_KEY" \
--vnet-name istsos-vnet --subnet istsos-subnet --nsg istsos-nsg \
--public-ip-sku Standard
Step 3: Connect via SSH
ssh azureuser@<vm-ip>
istsos-api.service and nginx will already be running and istsos-firstboot.service will already have rotated the administrator password to a per-VM value.
Step 4: Verify the istSOS Services
for s in istsos-api postgresql redis-server nginx; do printf '%-16s %s\n' "$s" "$(systemctl is-active $s)"; done
Expected output (one line per service, all active):
istsos-api active
postgresql active
redis-server active
nginx active
Confirm the listeners (nginx on 80, uvicorn on 8018, PostgreSQL and Redis on loopback):
sudo ss -tln | grep -E ':80 |:8018 |:5432 |:6379 '

Step 5: Retrieve the Administrator Credentials
The per-VM administrator password, PostgreSQL passwords and JWT signing key are written to a root-only file at first boot:
sudo cat /stage/scripts/istsos-credentials.log
You will see (values redacted in the example below — your VM has unique per-VM values):
admin_username=admin
admin_password=<ADMIN_PASSWORD>
db_superuser_password=<DB_SUPERUSER_PASSWORD>
secret_key=<SECRET_KEY>
ISTSOS_URL=http://<vm-ip>/istsos4/v1.1
The same file is written to /root/istsos-credentials.txt. Both are mode 0600 (root only). Store the administrator password in your secret store.
Step 6: The SensorThings API Service Root
The SensorThings API is served under /istsos4/v1.1. The service root lists every entity collection:
curl -s http://localhost/istsos4/v1.1 | python3 -m json.tool | head -22
You will see the eight SensorThings collections (Things, Sensors, Datastreams, Observations, ObservedProperties, Locations, HistoricalLocations, FeaturesOfInterest), each with its URL:

Step 7: The Interactive API Explorer
Open http://<vm-ip>/istsos4/v1.1/docs in your browser to reach the Swagger explorer. Every SensorThings operation is documented and can be tried from the browser. Read operations (the GET endpoints) are open for anonymous browsing; write operations (POST, PATCH, DELETE, shown with a padlock) require the administrator token, which you obtain through the Authorize button:

Step 8: Browse the Seeded Sensor Stations
A fresh VM already holds a demo network so you can explore istSOS immediately. List the seeded sensor stations (Things):
curl -s "http://localhost/istsos4/v1.1/Things?\$count=true" | python3 -m json.tool | head -20
The response lists the five seeded stations. You can confirm the full seeded dataset directly in the database:
sudo -u postgres psql -d istsos -P pager=off -c "SELECT 'Things' AS entity, count(*) FROM sensorthings.\"Thing\" UNION ALL SELECT 'Datastreams', count(*) FROM sensorthings.\"Datastream\" UNION ALL SELECT 'Observations', count(*) FROM sensorthings.\"Observation\" ORDER BY 1;"


Step 9: View the Time Series Observations
Observations are the individual measurements recorded by each datastream. Fetch the most recent observations, newest first:
curl -s "http://localhost/istsos4/v1.1/Observations?\$top=5&\$orderby=phenomenonTime%20desc&\$count=true" | python3 -m json.tool | head -30
Each record carries a phenomenonTime and a numeric result. The seeded network holds 1680 observations spanning seven days of hourly readings for rainfall and temperature:

Step 10: Authenticate and Write
Reads are anonymous; writes require an administrator JSON Web Token. Log in with the per-VM administrator credentials to obtain a token, then use it to create a new Thing. The command below reads the administrator password from the credentials file, logs in, and creates an example station (writes also require a commit-message header):
BASE="http://localhost/istsos4/v1.1"
ADMIN_PW="$(sudo sed -n 's/^admin_password=//p' /stage/scripts/istsos-credentials.log | head -n1)"
TOKEN="$(curl -s -X POST "$BASE/Login" --data-urlencode 'username=admin' --data-urlencode "password=$ADMIN_PW" | python3 -c 'import sys,json;print(json.load(sys.stdin)["access_token"])')"
curl -s -o /dev/null -w 'create example-station -> HTTP %{http_code}\n' -X POST "$BASE/Things" \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-H 'commit-message: example station from the cloudimg guide' \
-d '{"name":"example-station","description":"Created from the cloudimg guide"}'
You should see create example-station -> HTTP 201. A write attempted without the token is rejected with HTTP 401, and the upstream default admin / admin login no longer works — both are proven on every VM.
Step 11: Add Your Own Sensor Network
The SensorThings model links entities together: a Thing (a physical station) has Locations and Datastreams; each Datastream links a Sensor to an ObservedProperty and holds Observations. Using the token from Step 10, you can build a full station with a single deep-insert POST to /Things, embedding its Locations and Datastreams, then push observations to the created datastream. The interactive explorer at /istsos4/v1.1/docs documents the exact request bodies for each entity, and the OGC SensorThings API specification describes the full data model. For bulk historical loads, POST a SensorThings dataArray to /CreateObservations.
Step 12: Put TLS in Front (Production)
istSOS ships behind nginx on plain HTTP by design. For production, terminate TLS. Two common paths:
Path A — enable TLS on the on-box nginx: obtain a certificate for your DNS name and add a listen 443 ssl; server block that proxies to http://127.0.0.1:8018;. The nginx site file lives at /etc/nginx/sites-available/istsos.
Path B — Azure Application Gateway in front: create an Application Gateway with TLS termination and route the backend pool at the VM private IP on port 80. Restrict the NSG to allow inbound 80 from the App Gateway subnet only.
The cloudimg nginx-ssl-certbot-ubuntu-24-04 image is purpose-built for Path A — deploy it on a separate small VM and point it at this istSOS VM's private IP.
Step 13: Server Components
| Component | Path |
|---|---|
| Application code | /opt/istsos/app/ |
| Python venv | /opt/istsos/venv/ |
| Demo data generator | /opt/istsos/dummy_data/generator.py |
| Environment file | /etc/istsos/istsos.env |
| API systemd unit | /etc/systemd/system/istsos-api.service |
| Firstboot script | /usr/local/sbin/istsos-firstboot.sh |
| Firstboot service | /etc/systemd/system/istsos-firstboot.service |
| nginx site | /etc/nginx/sites-available/istsos |
| Credentials file | /stage/scripts/istsos-credentials.log and /root/istsos-credentials.txt |
| Firstboot sentinel | /var/lib/cloudimg/istsos-firstboot.done |
| PostgreSQL data | /var/lib/postgresql/16/main/ |
| PostgreSQL istSOS settings | /etc/postgresql/16/main/conf.d/istsos.conf |
Step 14: Managing the Services
Status:
sudo systemctl status istsos-api.service --no-pager | head -n 15
Stop / Start / Restart:
sudo systemctl restart istsos-api.service
View application logs:
sudo tail -n 50 /var/log/istsos/uvicorn.log
View firstboot log:
sudo tail -n 30 /var/log/istsos/firstboot.log 2>/dev/null || echo "firstboot log is written on first boot"
Step 15: Backup and Restore
The SensorThings datastore lives in PostgreSQL 16 on the same VM, in the istsos database. Back it up with pg_dump:
sudo -u postgres pg_dump -Fc istsos > /var/backups/istsos.dump
Restore on a new VM with pg_restore after stopping the API service. Substitute <backup-dir> for the directory holding your dump file:
sudo systemctl stop istsos-api.service
sudo -u postgres pg_restore -d istsos --clean --if-exists <backup-dir>/istsos.dump
sudo systemctl start istsos-api.service
For production, schedule a nightly pg_dump to Azure Blob Storage with azcopy.
Step 16: Troubleshooting
Cannot reach the API on port 80
-
Confirm services running:
sudo systemctl is-active istsos-api.service nginx -
Confirm listeners bound:
sudo ss -tln | grep -E ':80 |:8018 ' -
Check log:
sudo tail -n 50 /var/log/istsos/uvicorn.log -
Confirm the NSG allows TCP 80 from your client source IP
Login returns 401
-
Confirm you are using the per-VM password from
/stage/scripts/istsos-credentials.log, not the upstream default -
Confirm Redis is running:
sudo systemctl is-active redis-server
A write returns 422
- Writes require both the
Authorization: Bearerheader and acommit-messageheader — include both (see Step 10)
Service fails to start
-
Check journal:
sudo journalctl -u istsos-api.service --no-pager -n 100 -
Verify the datastore is reachable:
sudo -u postgres psql -d istsos -c '\dt sensorthings.*' | head
Step 17: Security Recommendations
-
Rotate the administrator password again after first login and store it in your secret store
-
Restrict port 22 to your management IP ranges only
-
Restrict port 80 to trusted client networks only; pair with TLS via Step 12 for any non-trivial deployment
-
Keep read access in mind — the SensorThings collections are readable anonymously by design for easy integration; front the VM with a firewall or gateway if your observation data is sensitive
-
Schedule nightly backups of the PostgreSQL datastore (Step 15) to Azure Blob Storage
-
Shred the credentials file once the password is stored in your secret store:
sudo shred -u /stage/scripts/istsos-credentials.log /root/istsos-credentials.txt
Step 18: Support and Licensing
istSOS is licensed under the Apache License 2.0 and is developed by SUPSI. There is no per-user, per-sensor, or per-server fee.
cloudimg provides commercial support for this image separately from the upstream project.
-
Email: support@cloudimg.co.uk
-
Website: www.cloudimg.co.uk
-
Support hours: 24/7 with guaranteed 24 hour response SLA
Deploy on Azure
Launch istSOS 4 on Ubuntu 24.04 with 24/7 support from cloudimg.
View on Marketplace
Need Help?
Our support team is available 24/7.
support@cloudimg.co.uk