Arc Time-Series Database on Ubuntu 24.04 on Azure User Guide
Overview
This guide covers the deployment and operation of Arc Time-Series Database on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Arc is an open source time series and analytical database from Basekick Labs, distributed under the AGPL 3.0 licence. It pairs a high throughput ingestion pipeline with the DuckDB analytical query engine and stores every record as open Apache Parquet files on your own storage, so your data stays in a portable format any Parquet tool can read.
Arc runs as one statically linked binary. Ingestion, storage, background compaction, SQL query and backup all live in a single process: there is no JVM, no Python environment, no separate metadata service and no cluster to operate. This image installs Arc from the official Basekick Labs package, pinned to 26.06.3 and checksum verified, running under systemd as the unprivileged arc system user.
Arc is a headless database. There is no web interface: you operate it over its HTTPS API with curl, the Python SDK, a Grafana data source, or any InfluxDB line protocol client such as Telegraf.
Secure by default — token authentication, no default credentials. Arc's own token authentication is enabled, so every query, write and administrative route requires a bearer token. Only /health and /ready answer without one; they are liveness probes and carry no data. On the very first boot of every virtual machine the image generates a unique 48 character admin API token and a unique TLS certificate, records the token in /root/arc-tsdb-credentials.txt (mode 0600, root only), and only then allows the database to start. An unauthenticated or wrong token request is rejected with HTTP 401, and no two instances share a secret.
Arc terminates TLS itself on port 8000, so bearer tokens never cross the network in clear text. Port 8000 is upstream's default and is deliberately kept, so the Grafana data source, the Telegraf output plugin and the Python SDK all work without reconfiguration.
What is included:
-
Arc 26.06.3 from the official Basekick Labs package, run under systemd as the unprivileged
arcuser (arc.service) -
An HTTPS API on port 8000 with a self signed certificate generated per virtual machine on first boot
-
Time series data stored as Apache Parquet on a dedicated 30 GB data disk mounted at
/var/lib/arc/data -
A unique admin API token generated on first boot and recorded in
/root/arc-tsdb-credentials.txt(0600), with token authentication enforced on every data route -
Automatic background compaction, which merges small Parquet files so scans stay fast and storage stays small
-
Ingestion over the InfluxDB line protocol, MessagePack columnar and MQTT, and analytical SQL with window functions, common table expressions and joins
-
Usage telemetry disabled, so the appliance never reports to a third party
Prerequisites
-
Active Azure subscription, SSH public key, VNet and subnet in your target region
-
A network security group allowing inbound TCP 22 for SSH management and TCP 8000 for the Arc HTTPS API, restricted to the source ranges you trust
-
Recommended size Standard_B2ms (2 vCPU, 8 GiB). Arc's DuckDB memory limit is preconfigured to 3 GB for this size; raise it if you deploy on a larger VM
Step 1: Deploy from the Azure Portal
Create the virtual machine from the cloudimg Arc Time-Series Database on Ubuntu 24.04 image, choosing your resource group, region, size and SSH public key. The 30 GB data disk that carries the Parquet store is defined in the image and is provisioned automatically with the VM, so you do not need to add one.
Once the VM is running, connect over SSH with the admin user you chose at deployment. On the first boot Arc's first boot service generates this instance's admin API token and TLS certificate before the database is allowed to start, which normally completes within a few seconds of the VM reaching the running state.
Step 2: Retrieve your per-VM admin API token
Every instance generates its own token. Read it from the root only credentials file:
sudo cat /root/arc-tsdb-credentials.txt
The file records ARC_ADMIN_TOKEN, the ARC_URL endpoint for this VM and the default database name, along with worked curl examples. It is mode 0600 and owned by root, so only a user who can already become root on the VM can read it. Keep it secret and treat the token as a full administrative credential.
Step 3: Verify the service is running
systemctl status arc --no-pager
dpkg-query -W -f='arc ${Version}\n' arc
ss -ltn | grep 8000
curl -sk https://127.0.0.1:8000/health
arc.service should report active (running), the installed version should be 26.06.3, and Arc should be listening on port 8000 on all interfaces. The /health endpoint is a public liveness probe, so it answers without a token and returns a JSON status document.

Step 4: Run your first query
Arc speaks standard analytical SQL over its HTTPS API. Read the token straight out of the credentials file so you never paste a secret onto the command line:
TOKEN=$(sudo grep '^ARC_ADMIN_TOKEN=' /root/arc-tsdb-credentials.txt | cut -d= -f2-)
curl -sk -X POST https://127.0.0.1:8000/api/v1/query \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"sql":"SELECT 1 AS ok"}'
The response is a JSON document with "success":true, a columns array, a data array of rows and a row_count. Because the query engine is DuckDB, the full analytical SQL surface is available, including common table expressions and window functions:
TOKEN=$(sudo grep '^ARC_ADMIN_TOKEN=' /root/arc-tsdb-credentials.txt | cut -d= -f2-)
curl -sk -X POST https://127.0.0.1:8000/api/v1/query \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"sql":"WITH t AS (SELECT 1 AS v UNION ALL SELECT 2 UNION ALL SELECT 3) SELECT v, sum(v) OVER () AS total FROM t ORDER BY v"}'
Step 5: Write and read back time series data
Arc accepts the InfluxDB line protocol, so anything that already writes to InfluxDB 1.x, including Telegraf, can point at Arc unchanged. Write a few points, force a flush so they are committed to Parquet immediately, then query them back:
TOKEN=$(sudo grep '^ARC_ADMIN_TOKEN=' /root/arc-tsdb-credentials.txt | cut -d= -f2-)
NOW=$(date +%s)000000000
curl -sk -o /dev/null -w 'write: HTTP %{http_code}\n' \
-X POST 'https://127.0.0.1:8000/write?db=cloudimg' \
-H "Authorization: Bearer $TOKEN" \
--data-binary "cpu,host=server01 usage=64.2 $NOW"
curl -sk -o /dev/null -X POST https://127.0.0.1:8000/api/v1/write/line-protocol/flush \
-H "Authorization: Bearer $TOKEN"
sleep 2
curl -sk -X POST https://127.0.0.1:8000/api/v1/query \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"sql":"SELECT count(*) AS points FROM cloudimg.cpu"}'
A successful write returns HTTP 204. The read back query returns "success":true with the number of points stored. In normal operation you do not need to flush by hand: Arc flushes its ingest buffer automatically, and the explicit flush here simply makes the round trip immediate for this walkthrough.
Measurements become tables inside the database you wrote to, so a point written to measurement cpu in database cloudimg is queried as cloudimg.cpu. List what a database currently holds with the measurements endpoint:
TOKEN=$(sudo grep '^ARC_ADMIN_TOKEN=' /root/arc-tsdb-credentials.txt | cut -d= -f2-)
curl -sk -H "Authorization: Bearer $TOKEN" https://127.0.0.1:8000/api/v1/measurements

Step 6: Authentication is enforced
Every data route requires the bearer token. Only the liveness probes are public. Confirm all four cases:
TOKEN=$(sudo grep '^ARC_ADMIN_TOKEN=' /root/arc-tsdb-credentials.txt | cut -d= -f2-)
echo "public /health : HTTP $(curl -sk -o /dev/null -w '%{http_code}' https://127.0.0.1:8000/health)"
echo "no token /query : HTTP $(curl -sk -o /dev/null -w '%{http_code}' -X POST https://127.0.0.1:8000/api/v1/query -H 'Content-Type: application/json' -d '{"sql":"SELECT 1"}')"
echo "bad token /query : HTTP $(curl -sk -o /dev/null -w '%{http_code}' -X POST https://127.0.0.1:8000/api/v1/query -H 'Authorization: Bearer not-a-real-token' -H 'Content-Type: application/json' -d '{"sql":"SELECT 1"}')"
echo "good token /query : HTTP $(curl -sk -o /dev/null -w '%{http_code}' -X POST https://127.0.0.1:8000/api/v1/query -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{"sql":"SELECT 1"}')"
curl -sk -X POST https://127.0.0.1:8000/api/v1/query -H 'Content-Type: application/json' -d '{"sql":"SELECT 1"}'
The public health probe returns 200, the unauthenticated and wrong token queries both return 401 with an "Authentication required" error body and no data, and only the request carrying this VM's token returns 200. The same applies to the write and measurements routes.

Step 7: Security posture and where your data lives
sudo stat -c '%a %U:%G %n' /root/arc-tsdb-credentials.txt /etc/arc/arc.toml /etc/arc/tls/arc.key
df -h /var/lib/arc/data | tail -1
findmnt -no SOURCE,FSTYPE,SIZE /var/lib/arc/data
sudo find /var/lib/arc/data -name '*.parquet' | head -3
getent passwd arc
sudo grep -E '^(tls_enabled|enabled|local_path|db_path)' /etc/arc/arc.toml
The credentials file is 0600 root:root, the Parquet store sits on the dedicated data disk rather than the OS disk, Arc runs as a non login system user, and the configuration shows TLS enabled and telemetry disabled. Your time series data is written as ordinary Parquet files under /var/lib/arc/data, organised by database, measurement and time, which means you can read it directly with DuckDB, pandas, polars or any other Parquet reader.

Connecting from a remote client
From any machine that can reach the VM on port 8000, use the public address recorded as ARC_URL in the credentials file. Because the certificate is self signed per VM, pass -k to curl (or the equivalent "skip verification" option in your client) until you install a certificate your clients already trust:
curl -k -X POST https://<your-vm-ip>:8000/api/v1/query \
-H "Authorization: Bearer <ARC_ADMIN_TOKEN>" \
-H 'Content-Type: application/json' \
-d '{"sql":"SELECT 1 AS ok"}'
Point Telegraf at the appliance by adding an outputs.influxdb section whose urls is https://<your-vm-ip>:8000, with insecure_skip_verify = true while the certificate is self signed, and the token supplied as the HTTP Authorization header. Any existing InfluxDB 1.x writer works the same way.
The Python SDK is published as arc-tsdb-client on PyPI, and Basekick Labs also publishes a Grafana data source plugin, a VS Code extension and Apache Superset dialects. All of them expect the port 8000 endpoint this image keeps.
Using your own TLS certificate
The per VM certificate is generated at /etc/arc/tls/arc.crt with its key at /etc/arc/tls/arc.key, carrying this VM's address in the subject alternative names. For production, replace those two files with a certificate issued for a hostname you control, or point tls_cert_file and tls_key_file in /etc/arc/arc.toml at your own paths, then restart the service with sudo systemctl restart arc. Keep the key readable by the arc group and no wider.
Rotating the admin token
The admin token is seeded from ARC_AUTH_BOOTSTRAP_TOKEN in /etc/arc/arc.env on the first start. To rotate it, edit that file with a new value, delete the credential database with sudo rm /var/lib/arc/arc.db, then restart Arc with sudo systemctl restart arc. Your time series data in /var/lib/arc/data is untouched, because credentials and data are deliberately kept on separate volumes. Record the new value in /root/arc-tsdb-credentials.txt so it stays the single source of truth for the instance.
Storage backends
The image stores data locally on the dedicated data disk, which is the right default for a single VM. Arc can also write directly to Azure Blob Storage, Amazon S3 or MinIO: set backend in the [storage] section of /etc/arc/arc.toml and supply the matching account or bucket settings, then restart the service. Using object storage lets the Parquet store grow far beyond the data disk while keeping the same query interface.
Sizing and tuning
The DuckDB query engine is limited to 3 GB of memory with 2 threads, matching the recommended Standard_B2ms. On a larger VM, raise memory_limit and thread_count in the [database] section of /etc/arc/arc.toml and restart Arc. The ingest buffer is set to 50000 records with a one second maximum age, which suits steady metric collection; raise max_buffer_size in the [ingest] section for very high write rates, remembering that the buffer is held in memory.
What this image includes
This appliance ships the AGPL 3.0 open source build of Arc with no licence key, and the full licence text is kept at /opt/arc/LICENSE. Ingestion, storage, compaction, analytical SQL, token authentication, backup and restore, and Prometheus format metrics are all part of that build. Basekick Labs separately offers a commercial Enterprise tier that adds clustering, role based access control, scheduled retention and continuous queries, tiered storage and audit logging; those features are not part of this image. Because no licence key is configured, the appliance never contacts an external licence server and runs fully air gapped.
Managing the service
systemctl is-enabled arc
systemctl is-active arc
sudo journalctl -u arc --no-pager -n 15
Use sudo systemctl restart arc after any configuration change, and sudo systemctl stop arc before taking a file level backup of /var/lib/arc.
Troubleshooting
Arc is not running after the first boot. The database deliberately refuses to start until the first boot service has generated this VM's token, so check that unit first with sudo systemctl status arc-tsdb-firstboot and read /var/log/cloudimg-firstboot.log. Once it has completed, /root/arc-tsdb-credentials.txt contains a real token and /var/lib/cloudimg/arc-bootstrap-ready exists.
Every request returns 401. Confirm you are sending the token from this VM's credentials file, as a Authorization: Bearer header. Tokens are per instance and are not interchangeable between VMs.
The connection is refused from a remote machine. Check the network security group allows inbound TCP 8000 from your source range, and remember the endpoint is HTTPS, not HTTP.
A certificate warning appears. That is expected while the per VM certificate is self signed. Use -k with curl, or install your own certificate as described above.
A query cannot find a table you just wrote. Points are queried as <database>.<measurement>, and very recent writes may still be in the ingest buffer. Flush explicitly with the line protocol flush endpoint, or wait for the automatic flush.
Support
cloudimg images include 24/7 support. Raise an issue through the cloudimg support channel listed on your Azure Marketplace offer, quoting the image name and the output of dpkg-query -W arc where relevant. Arc has no --version flag: running the binary with no subcommand starts the database, so use the package query above to report the installed version.