SiriDB on Ubuntu 24.04 on Azure User Guide
Overview
SiriDB is an open source time series database written in C. It is built for workloads that write a great many timestamped points and then read them back over ranges: infrastructure and application metrics, sensor and industrial telemetry, financial ticks, and any other measurement that arrives continuously and is asked about by time. Recent points are held in an in-memory buffer for high ingest rates and then written into compressed shards on disk, so storage stays compact without giving up query speed. Instead of bolting time onto general-purpose SQL, SiriDB has its own compact query language covering selection, filtering, grouping and aggregating by time window, merging and downsampling.
The cloudimg image installs SiriDB Server 2.0.54 built from the pinned upstream source, plus the two official command line tools — siridb-prompt for queries and imports and siridb-admin for service accounts and database management. It is built as an internal database appliance: both listeners, the native client port (127.0.0.1:9000) and the server backend port (127.0.0.1:9010), bind to loopback, and the network firewall opens SSH (22) only, so the database is never reachable directly from the internet. You reach it over an SSH tunnel or from within your own virtual network.
SiriDB publishes well-known default passwords for its service account and for the first user of every new database. This image never ships them. Each VM generates its own long random secrets at first boot and replaces every published default before the database port is opened at all, then re-verifies at run time that no account still accepts a published or guessable password. The database itself is created fresh and completely empty on every VM. Backed by 24/7 cloudimg support.
What is included:
- SiriDB Server 2.0.54, built from upstream commit
b4d290bf07d6ccc0c936b6c7e6195215d8a95d2b, running as thesiridb-server.servicesystemd unit - The official
siridb-prompt(2.1.12) andsiridb-admin(1.2.0) command line tools - An empty database named
cloudimg, created on this VM at first boot with second time precision - Per-VM credentials for both the
saservice account and theirisdatabase user, written to a root-only file - Loopback-only binding on the client port
9000and the backend port9010; both optional HTTP services left off - A bootstrap-ready gate that keeps the database port closed until every credential has been rotated
- The C build toolchain removed from the finished image
- 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet and subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a good starting point; scale up for higher ingest rates or larger retention. Network security group inbound: allow 22/tcp from your management network only. You do not open the database port — SiriDB binds to loopback and you reach it over an SSH tunnel, so 9000 and 9010 are never exposed.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for SiriDB 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) only. Then Review + create, then Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name siridb \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
Only SSH (22) needs to be reachable. Do not add an inbound rule for 9000 or 9010.
Step 3 - Read your per-VM credentials
SSH in as azureuser. The first-boot service writes this VM's credentials to a root-only file, including the exact SSH tunnel command for this machine:
sudo cat /root/siridb-credentials.txt
Expected output (your addresses and passwords will differ - the two passwords are 48-character random hex strings generated on this VM):
# SiriDB - Per-VM Credentials
# Generated: Sun Jul 26 07:37:19 UTC 2026
#
# SiriDB listens on the LOOPBACK interface only (client 127.0.0.1:9000,
# backend 127.0.0.1:9010) and the firewall opens SSH (22) only, so the database
# is not reachable from the internet. Reach it over an SSH tunnel:
#
# ssh -N -L 9000:127.0.0.1:9000 azureuser@20.0.0.1
#
# Both passwords below were generated on THIS virtual machine at first boot.
SIRIDB_VERSION=2.0.54
SIRIDB_HOST=127.0.0.1
SIRIDB_CLIENT_PORT=9000
SIRIDB_DATABASE=cloudimg
SIRIDB_DB_USER=iris
SIRIDB_DB_PASSWORD=...
SIRIDB_SERVICE_ACCOUNT=sa
SIRIDB_SERVICE_PASSWORD=...
SIRIDB_DB_PASSWORD is what you use for queries and inserts. SIRIDB_SERVICE_PASSWORD is the server-level account used to create and drop databases.
Step 4 - Check the service and the network posture
sudo systemctl is-active siridb-server.service siridb-firstboot.service
siridb-server --version
sudo ss -ltn | grep -E ':(9000|9010)\b'
Expected output:
active
active
SiriDB Server 2.0.54
Contributers: https://github.com/SiriDB/siridb-server/contributors
Home-page: https://siridb.net
LISTEN 0 128 127.0.0.1:9010 0.0.0.0:*
LISTEN 0 128 127.0.0.1:9000 0.0.0.0:*
Both listen addresses are 127.0.0.1. Nothing on this VM listens for database traffic on a routable address.

The database unit is also gated so it cannot start before first boot has finished locking the machine down:
sudo systemctl cat siridb-server.service | grep -E 'ConditionPathExists|^User=|^Group='
sudo ls -l /var/lib/cloudimg/
Expected output:
ConditionPathExists=/var/lib/cloudimg/siridb-bootstrap-ready
User=siridb
Group=siridb
total 0
-rw------- 1 root root 0 Jul 26 07:37 siridb-bootstrap-ready
-rw------- 1 root root 0 Jul 26 07:37 siridb-firstboot.done
siridb-bootstrap-ready is created only after every upstream default password has been replaced, so the client port cannot open before that has happened. The daemon runs as the unprivileged siridb account, not as root.
Step 5 - Confirm no published default password works
SiriDB's upstream defaults are sa / siri for the service account and iris / siri for the first user of a new database. Both are replaced on this VM. Prove it:
for CRED in "service sa siri" "db iris siri"; do
set -- $CRED
if sudo /usr/local/sbin/siridb-authprobe "$1" 127.0.0.1:9000 "$2" "$3" cloudimg 2>/dev/null; then
echo "PROBLEM: upstream default $2/$3 still authenticates"
else
echo "upstream default $2/$3 -> rejected"
fi
done
Expected output:
upstream default sa/siri -> rejected
upstream default iris/siri -> rejected
The image also re-checks this generically at run time. Rather than testing a fixed list, it asks the server which accounts it actually has and probes each one:
sudo /usr/local/sbin/siridb-assert-no-default-creds
Expected output:
probed 1 service account(s) [sa] and 1 database user(s) [iris]
NO_DEFAULT_CREDENTIALS_OK

Step 6 - Open an SSH tunnel from your workstation
From your own machine, forward the SiriDB client port over SSH. Leave this running in its own terminal:
ssh -N -L 9000:127.0.0.1:9000 azureuser@<vm-ip>
Any SiriDB client - siridb-prompt, the Python, Go, C or Node.js connectors, or the Grafana data source - then connects to 127.0.0.1:9000 on your workstation as if the database were local.
Step 7 - Run your first queries
On the VM, siridb-q is a small root-only wrapper that reads this machine's credentials and runs a query for you. Use it for quick checks:
sudo /usr/local/sbin/siridb-q 'show dbname, time_precision, version, uptime'
sudo /usr/local/sbin/siridb-q 'count series'
sudo /usr/local/sbin/siridb-q 'list users'
sudo /usr/local/sbin/siridb-q 'list servers'
Expected output:
{"data":[{"name":"dbname","value":"cloudimg"},{"name":"time_precision","value":"s"},{"name":"version","value":"2.0.54"},{"name":"uptime","value":85}]}
{"series":0}
{"columns":["name","access"],"users":[["iris","full"]]}
{"columns":["name","pool","version","online","status"],"servers":[["127.0.0.1:9010",0,"2.0.54",true,"running"]]}
count series is 0 because the image ships an empty database - no demo data is baked in.
To use the official interactive client instead, read the credentials and run siridb-prompt directly. The same command works from your workstation through the tunnel from Step 6:
DB=$(sudo /usr/local/sbin/siridb-cred SIRIDB_DATABASE)
USER=$(sudo /usr/local/sbin/siridb-cred SIRIDB_DB_USER)
PASS=$(sudo /usr/local/sbin/siridb-cred SIRIDB_DB_PASSWORD)
siridb-prompt -d "$DB" -u "$USER" -p "$PASS" -s 127.0.0.1:9000 --json -r 'show status' </dev/null
Expected output:
{"data":[{"name":"status","value":"running"}]}
Run siridb-prompt -d "$DB" -u "$USER" -p "$PASS" -s 127.0.0.1:9000 with no -r to get the full-screen interactive client with history and syntax help.
Step 8 - Insert time series data
SiriDB inserts points over its native protocol. siridb-prompt accepts a JSON or CSV file with the import command. The JSON form is a map of series name to a list of [timestamp, value] pairs; this database uses second precision, so timestamps are plain Unix seconds.
NOW=$(date -u +%s); BASE=$(( NOW - 3600 ))
python3 - "$BASE" > /tmp/siridb-demo.json <<'PY'
import json, sys
b = int(sys.argv[1])
cpu = [[b + i*60, round(0.20 + 0.35*((i*7) % 11)/11, 3)] for i in range(60)]
mem = [[b + i*60, 1024 + (i*13) % 256] for i in range(60)]
json.dump({"cpu.load.web01": cpu, "mem.used_mb.web01": mem}, sys.stdout)
PY
sudo /usr/local/sbin/siridb-q "import /tmp/siridb-demo.json"
sudo /usr/local/sbin/siridb-q 'list series'
sudo /usr/local/sbin/siridb-q 'count series length'
Expected output:
{"success_msg":"Successfully inserted 120 point(s)."}
{"columns":["name"],"series":[["cpu.load.web01"],["mem.used_mb.web01"]]}
{"series_length":120}
Series are created implicitly by the first point written to them - there is no schema to declare.
Step 9 - Select, aggregate and downsample
Selections take a time window and an optional aggregation with a group-by interval:
sudo /usr/local/sbin/siridb-q "select * from 'cpu.load.web01' after now - 5m"
sudo /usr/local/sbin/siridb-q "select mean(15m) from 'cpu.load.web01' after now - 1h"
sudo /usr/local/sbin/siridb-q "select max(1h) prefix 'max-', min(1h) prefix 'min-' from 'cpu.load.web01' after now - 1h"
sudo /usr/local/sbin/siridb-q "select mean(30m) from /.*web01/ after now - 1h"
Expected output (values will differ):
{"cpu.load.web01":[[1785051248,0.2],[1785051308,0.423],[1785051368,0.295],[1785051428,0.518],[1785051488,0.391]]}
{"cpu.load.web01":[[1785048300,0.34850000000000003],[1785049200,0.36546666666666666],[1785050100,0.3463333333333333],[1785051000,0.37393333333333334],[1785051900,0.35555555555555557]]}
{"max-cpu.load.web01":[[1785049200,0.518],[1785052800,0.518]],"min-cpu.load.web01":[[1785049200,0.2],[1785052800,0.2]]}
{"cpu.load.web01":[[1785049200,0.3606190476190476],[1785051000,0.36013333333333336],[1785052800,0.35555555555555557]],"mem.used_mb.web01":[[1785049200,1141.8095238095239],[1785051000,1135.6333333333334],[1785052800,1227]]}
Points to note:
after now - 1his the time window;between ... and ...andbefore ...also work.mean(15m)downsamples into 15-minute buckets.max,min,sum,count,median,variance,first,lastand others are available with the same interval syntax.- When you select with more than one aggregation in a single statement, give each one a
prefixorsuffixso the returned series names stay unique. - A
/regex/in place of a series name selects every matching series at once. merge as '<name>' using mean(1)collapses several series into one.
timeit in front of any query reports how long each server spent on it:
sudo /usr/local/sbin/siridb-q "timeit select mean(30m) from 'cpu.load.web01' after now - 1h"
Expected output:
{"__timeit__":[{"server":"127.0.0.1:9010","time":0.000135402}],"cpu.load.web01":[[1785049200,0.3606190476190476],[1785051000,0.36013333333333336],[1785052800,0.35555555555555557]]}

Remove the demo series again when you are finished with them. SiriDB refuses a drop that would remove every series in a pool unless you say so explicitly:
sudo /usr/local/sbin/siridb-q "drop series /.*web01/ set ignore_threshold true"
sudo /usr/local/sbin/siridb-q 'count series'
sudo rm -f /tmp/siridb-demo.json
Expected output:
{"success_msg":"Successfully dropped 2 series."}
{"series":0}
Step 10 - Users, access and additional databases
Database users are managed with SiriDB queries. Access is granted from the set read, write, select, show, list, count, create, alter, drop, grant, revoke and full. Choose a strong password of your own:
sudo /usr/local/sbin/siridb-q "create user 'reporting' set password '<new-password>'"
sudo /usr/local/sbin/siridb-q "grant select, list, count, show to user 'reporting'"
sudo /usr/local/sbin/siridb-q 'list users'
To see the current users and their access levels at any time:
sudo /usr/local/sbin/siridb-q 'list users'
Expected output:
{"columns":["name","access"],"users":[["iris","full"]]}
Server-level operations - creating and dropping databases, and managing service accounts - use siridb-admin with the sa account:
SA=$(sudo /usr/local/sbin/siridb-cred SIRIDB_SERVICE_ACCOUNT)
SAPW=$(sudo /usr/local/sbin/siridb-cred SIRIDB_SERVICE_PASSWORD)
siridb-admin -u "$SA" -p "$SAPW" -s 127.0.0.1:9000 get-databases </dev/null
siridb-admin -u "$SA" -p "$SAPW" -s 127.0.0.1:9000 get-accounts </dev/null
Expected output:
[cloudimg]
[sa]
Create a second database with, for example, millisecond precision using siridb-admin -u "$SA" -p "$SAPW" -s 127.0.0.1:9000 new-database -d metrics_ms -t ms. Note that every new database is seeded with the upstream default user iris / siri - change it immediately with alter user 'iris' set password '<new-password>' against the new database, exactly as this image does for the database it creates at first boot.
Step 11 - Optional: enabling the HTTP API
SiriDB can also expose an HTTP API (insert and query over JSON) and an HTTP status endpoint. Both are switched off in this image, which is also how upstream ships them. The reason matters: SiriDB 2.0.54 binds those two servers to every interface and offers no setting to restrict them to loopback, so turning one on without a firewall rule would publish it.
If you want the HTTP API, set http_api_port = 9020 in /etc/siridb/siridb.conf, restart siridb-server.service, and restrict the port to your own network before anything else - for example with a host firewall rule scoped to your management range:
sudo nft add rule inet filter input ip saddr <your-mgmt-cidr> tcp dport 9020 accept
Keep the Azure network security group closed to 9020 as well. The tunnel in Step 6 remains the recommended access path.
Step 12 - Backups
SiriDB keeps everything under /var/lib/siridb. The safest snapshot is taken with the service stopped, which is quick for a single node:
sudo systemctl stop siridb-server.service
sudo tar -C /var/lib -czf /var/backups/siridb-$(date -u +%Y%m%d).tar.gz siridb
sudo systemctl start siridb-server.service
sudo /usr/local/sbin/siridb-wait-port 9000 60
sudo ls -lh /var/backups/ | tail -2
Expected output ends with READY and a listing of the new archive.
For a logical export instead, select the series you care about and dump the result: siridb-prompt writes the last result to a file with dump <file>.json or dump <file>.csv in the interactive client. Copy backups off the VM to Azure Blob Storage or another account on a schedule that matches your retention policy.
Step 13 - Maintenance
The image ships fully patched and keeps Ubuntu's unattended security upgrades enabled, so operating system fixes continue to arrive. SiriDB itself is a single binary at /usr/bin/siridb-server; to move to a newer upstream release, back up as in Step 12, replace the binary and restart the unit.
sudo systemctl status siridb-server.service --no-pager | head -6
sudo journalctl -u siridb-server.service --no-pager -n 10
Configuration lives in /etc/siridb/siridb.conf. Useful knobs: buffer_sync_interval (milliseconds between buffer fsyncs; 0 means fsync every insert), optimize_interval (seconds between shard optimisation runs), and enable_shard_compression. Restart siridb-server.service after any change.

Troubleshooting
siridb-server.service will not start and reports a failed condition. The bootstrap-ready marker is missing. Check that first boot completed: sudo systemctl status siridb-firstboot.service and sudo journalctl -u siridb-firstboot.service. Re-run it with sudo systemctl restart siridb-firstboot.service - it is safe to run again and will rebuild the marker.
A client cannot connect from my workstation. The database only listens on loopback by design. Confirm the tunnel from Step 6 is still running, then point the client at 127.0.0.1:9000 on your own machine rather than at the VM's address.
Authentication fails. Read the current values with sudo cat /root/siridb-credentials.txt. The passwords are unique to this VM and are not the upstream defaults.
A drop is refused with a message about a percentage of series. SiriDB guards against dropping an entire pool by accident. Add set ignore_threshold true to the statement when that is genuinely what you want.
Ingest slows under heavy write load. Raise buffer_sync_interval in /etc/siridb/siridb.conf (for example to 500) so the buffer is flushed on an interval rather than on every insert, and restart the service.
Support
24/7 support is included with this image. Contact cloudimg at support@cloudimg.co.uk or see www.cloudimg.co.uk.