Deployment guide

QuestDB

Overview

QuestDB is a high-performance, open-source time-series database built for fast ingestion and low-latency SQL analytics. It speaks standard SQL with time-series extensions (SAMPLE BY, LATEST ON, ASOF JOIN), and ingests over the InfluxDB Line Protocol, REST and the PostgreSQL wire protocol. It ships an interactive web console for writing queries and visualising results.

This SecureAnyCloud image installs QuestDB 9.4.3 (with its bundled Java runtime) at /opt/questdb and runs it as a dedicated questdb system user. Every listener is bound to loopback behind an nginx reverse proxy on TCP 80 with HTTP Basic auth.

What you get:

  • QuestDB 9.4.3 at /opt/questdb with a bundled Java runtime (no system Java required)
  • The interactive web console and the REST query and import API, fronted by nginx on :80
  • nginx HTTP Basic auth, because open-source QuestDB has no built-in authentication, with a unique web password generated on the first boot of every VM and stored in a root-only file
  • A dedicated Azure data disk at /var/lib/questdb that holds the QuestDB data root (db, conf, log). It is separate from the OS disk and re-provisioned with every VM
  • The PostgreSQL wire protocol (8812) and InfluxDB Line Protocol (9009), bound to loopback for local tooling and SSH tunnels
  • questdb.service and nginx.service as systemd units, enabled and active
  • 24/7 SecureAnyCloud support

Prerequisites

  • An active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region
  • NSG inbound rules that allow 22/tcp from your management network and 80/tcp for the web console and REST API. Front the console with TLS for public exposure (see Enabling HTTPS)

Standard_B2ms (2 vCPU, 8 GiB RAM) is a good starting point. Scale up for higher ingestion rates.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal. Choose Create a resource, search the Marketplace for QuestDB by SecureAnyCloud, 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).

Review the dedicated data disk on the Disks tab. Then select Review + create and Create.

Step 2 - Deploy from the Azure CLI

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:questdb-ubuntu-24-04:default:latest
az vm create \
  --resource-group <your-rg> \
  --name questdb \
  --image sac1647283583153:questdb-ubuntu-24-04:default:latest \
  --size Standard_B2ms \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name questdb --port 80 --priority 1010

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the services are running

systemctl is-active questdb.service nginx.service

Both services report active. QuestDB starts in a few seconds and opens its loopback listeners. The REST API also reports the running build version.

Step 5 - Retrieve your web password

The admin password is generated uniquely on the first boot of your VM and written to a root-only file:

sudo cat /root/questdb-credentials.txt

This file contains QUESTDB_ADMIN_USER (admin) and QUESTDB_ADMIN_PASSWORD, plus the URLs for the web console and REST API. Store the password in a safe place.

Step 6 - Check the health endpoint

nginx serves an unauthenticated health endpoint for load balancers and probes. Request it:

curl -s http://localhost/health

The endpoint returns ok. Every other path is gated by nginx Basic auth, and QuestDB's own listeners stay bound to loopback.

Step 7 - Open the QuestDB web console

Browse to http://<vm-public-ip>/ and sign in as admin with the password from Step 5. The web console is the built-in SQL workbench. Type a query in the editor and run it to get a results grid. A schema explorer sits on the left, and an import panel handles CSV files.

Create a table, insert a few rows, and select them back. The result grid shows the returned rows.

The schema explorer on the left lists every table with its columns and types.

The Import panel lets you drag and drop a CSV file straight into a new or existing table.

Step 8 - Run SQL from the command line

The REST /exec endpoint runs SQL behind the same Basic auth and returns JSON. Confirm that QuestDB is serving and report its version:

curl -s -u admin:<QUESTDB_ADMIN_PASSWORD> 'http://localhost/exec?query=SELECT%20version()' | head -c 300; echo

The response is JSON with a dataset array that carries the running version string.

Step 9 - Create a table and ingest data

QuestDB accepts SQL DDL and DML over the same REST endpoint. Create a time-series table, insert a few rows, and query them back:

curl -s -u admin:<QUESTDB_ADMIN_PASSWORD> -G 'http://localhost/exec' --data-urlencode "query=CREATE TABLE IF NOT EXISTS readings (ts TIMESTAMP, sensor SYMBOL, value DOUBLE) TIMESTAMP(ts) PARTITION BY DAY WAL" >/dev/null
curl -s -u admin:<QUESTDB_ADMIN_PASSWORD> -G 'http://localhost/exec' --data-urlencode "query=INSERT INTO readings VALUES (now(), 'sensor-a', 12.5), (now(), 'sensor-b', 13.1)" >/dev/null
curl -s -u admin:<QUESTDB_ADMIN_PASSWORD> -G 'http://localhost/exec' --data-urlencode "query=SELECT count() FROM readings" | head -c 300; echo

The count query returns a dataset with the number of rows. A full REST round trip creates a table, inserts rows and runs an aggregation.

For high-throughput ingestion, point an InfluxDB Line Protocol client at 127.0.0.1:9009 over an SSH tunnel, or connect a PostgreSQL client to 127.0.0.1:8812. Both listeners are loopback-only (see the next section).

Step 10 - Confirm data lives on the dedicated disk

The QuestDB data root is stored on the dedicated Azure data disk, so it survives OS changes and can be resized independently:

findmnt /var/lib/questdb

The mount is backed by a separate Azure data disk. The disk is captured into the image and re-provisioned on every VM. The QuestDB db, conf and log directories live on it.

Connecting over PostgreSQL wire and ILP

QuestDB also exposes the PostgreSQL wire protocol on 127.0.0.1:8812 and the InfluxDB Line Protocol on 127.0.0.1:9009. Both are bound to loopback, so they are not exposed to the network. Reach them from your workstation with an SSH tunnel, for example:

ssh -L 8812:127.0.0.1:8812 -L 9009:127.0.0.1:9009 azureuser@<vm-public-ip>

Then connect a psql client to localhost:8812 (default user admin) or stream metrics to localhost:9009 with any ILP client.

Enabling HTTPS

The nginx reverse proxy terminates plain HTTP on port 80. For public exposure, put a certificate in front of it. Add a DNS name for the VM, and then choose one of two options:

  • Use the companion SecureAnyCloud nginx-ssl-certbot image as a TLS reverse proxy.
  • Install certbot and extend the existing nginx site with a listen 443 ssl; server block and your certificate paths.

Keep QuestDB itself bound to loopback, so that the only public surface is the authenticated, TLS-terminated proxy.

Maintenance

  • Configuration: server settings live in /var/lib/questdb/conf/server.conf (all listeners are bound to loopback). Edit it, and then run sudo systemctl restart questdb to apply the change.
  • Backups: snapshot the /var/lib/questdb data disk, or use QuestDB's SNAPSHOT SQL for consistent backups.
  • Upgrades: replace the /opt/questdb runtime with a newer release and restart the service. The on-disk data format is forward-compatible.
  • Security patches: unattended-upgrades remains enabled, so the OS continues to receive security updates automatically.

Support

SecureAnyCloud provides 24/7 expert support for this image. Contact sales@secureanycloud.com.

Deploy on Azure

Launch QuestDB 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

Deploy on Azure