Gq
Observability Azure

Gigapipe (qryn) on Ubuntu 24.04 on Azure User Guide

| Product: Gigapipe (qryn) on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and configuration of Gigapipe (qryn) on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Gigapipe, formerly known as qryn, is an open source polyglot observability backend. One lightweight service ingests logs, metrics, traces and profiles through the APIs your tooling already speaks, including Loki push, Prometheus remote write, Tempo and Zipkin, OpenTelemetry (OTLP) over HTTP, InfluxDB line protocol, Datadog and Elastic bulk formats, and stores everything in ClickHouse. You query it with LogQL, PromQL and TraceQL from the built in explorer or from any Grafana compatible client, with no plugins required, and correlate logs, metrics and traces in one place.

The cloudimg image ships the free and open source, AGPL-3.0 licensed Gigapipe 5.4.4 release, installed from the project's official GitHub release (the statically linked binary, pinned by SHA-256), together with the explorer UI from the matching qryn-view release, on box ClickHouse 26.8 LTS from the official ClickHouse repository, and the OpenTelemetry Collector Contrib 0.160.0 from its official release. Gigapipe and ClickHouse bind to the loopback interface only, and nginx terminates TLS and enforces HTTP basic authentication as the single public listener. The collector ships the VM's own systemd journal and host metrics into Gigapipe, so the explorer already shows live data when you first sign in. Everything unique is minted per VM on first boot: the ClickHouse passwords (the default user is never without one), the basic authentication password, a self signed TLS certificate, and the ClickHouse schema itself. Backed by 24/7 cloudimg support.

Gigapipe and qryn are names of their respective owners. This image is produced by cloudimg and is not affiliated with, endorsed by, or sponsored by Gigapipe, HEPVEST BV, Grafana Labs, ClickHouse Inc. or The Linux Foundation. Loki, Tempo and Grafana are trademarks of Grafana Labs; Prometheus and OpenTelemetry of The Linux Foundation; ClickHouse of ClickHouse Inc.; each is named only to identify a compatible API. The image ships the free and open source AGPL-3.0 licensed software, unmodified; the corresponding source is available at https://github.com/metrico/gigapipe and https://github.com/metrico/qryn-view.

The clickhouse-server, nginx, gigapipe and otelcol-contrib services active, the two one shot units completed, and the front door answering 200 with the credential, 401 without it and 301 on plain HTTP

What is included:

  • Gigapipe 5.4.4 — the official release binary, pinned by SHA-256, run unmodified as an unprivileged service account on loopback port 3100
  • The Gigapipe explorer (qryn-view 3.3.2) — the official release bundle, served by nginx on the same origin, for LogQL, PromQL and TraceQL queries
  • ClickHouse 26.8 LTS — the storage engine, bound to loopback only, crash reporting and telemetry disabled, memory capped to share the VM
  • OpenTelemetry Collector Contrib 0.160.0 — ships the VM's own journal (as OTLP logs) and host metrics (as OTLP metrics) into Gigapipe; opens no network port of its own
  • nginx — the single public listener, terminating TLS, enforcing basic authentication on every API and the explorer, with WebSocket support for live tail
  • First boot secret generation — ClickHouse administrator and application passwords, the basic authentication password and the TLS certificate, all unique to each VM
  • A bundled self test — pushes a log line and a metric through the real APIs and queries both back, proving the whole stack end to end

Prerequisites

  • An Azure subscription with permission to create virtual machines
  • An SSH key pair for administrative access
  • A network security group allowing inbound TCP 22 (SSH) and TCP 443 (HTTPS) from your own address ranges
  • Standard_B2ms (2 vCPU, 8 GB RAM) or larger; ClickHouse and Gigapipe share the memory, so choose more RAM and a larger or separate data disk as your ingest volume and retention grow

Step 1: Deploy from the Azure Portal

  1. Open the Azure Marketplace and search for Gigapipe (qryn) on Ubuntu 24.04 LTS by cloudimg.
  2. Select Create, then choose your subscription, resource group and region.
  3. Pick a VM size of Standard_B2ms or larger.
  4. Under Administrator account, select SSH public key and supply your public key.
  5. Under Inbound port rules, allow SSH (22) and HTTPS (443).
  6. Select Review + create, then Create.

Step 2: Deploy from the Azure CLI

az group create --name gigapipe-rg --location eastus
az vm create \
  --resource-group gigapipe-rg \
  --name gigapipe-vm \
  --image cloudimg:qryn-ubuntu-24-04:default:latest \
  --size Standard_B2ms \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

az vm open-port --resource-group gigapipe-rg --name gigapipe-vm --port 443 --priority 1010

Step 3: Connect to your VM

ssh azureuser@<vm-ip>

Step 4: Confirm the services are running

Gigapipe runs as a systemd service on loopback behind nginx, with ClickHouse on box and the OpenTelemetry Collector feeding it. Two one shot units run on boot: qryn-tls mints the per VM TLS certificate before nginx starts, and qryn-firstboot (first boot only) generates the secrets, opens the gate that lets Gigapipe start, and runs the self test.

systemctl is-active clickhouse-server nginx gigapipe otelcol-contrib

Expected output:

active
active
active
active

The one shot units report active (exited) after a successful run, and the first boot unit disables itself afterwards so it can never run twice:

systemctl --no-pager --plain --no-legend list-units 'qryn*' 'gigapipe*'

Expected output:

gigapipe.service       loaded active running Gigapipe (qryn) polyglot observability backend (loopback :3100) (cloudimg)
qryn-firstboot.service loaded active exited  Gigapipe (qryn) first-boot setup (per-VM secrets, schema, self-test) (cloudimg)
qryn-tls.service       loaded active exited  Mint the per-VM self-signed TLS certificate for the Gigapipe (qryn) front door (cloudimg)

Gigapipe and ClickHouse listen only on the loopback interface, and nginx is the single public listener on ports 80 (which redirects to HTTPS) and 443. Rather than grepping for the ports you expect, list every socket that is bound to something other than loopback, so anything unexpected has nowhere to hide:

sudo ss -ltn | awk 'NR>1{print $4}' | grep -vE '^(127\.|\[::1\])' | sort -u

Expected output:

0.0.0.0:22
0.0.0.0:443
0.0.0.0:80
[::]:22
[::]:443
[::]:80

The only sockets bound off loopback are SSH on 22 and nginx on 80 and 443; Gigapipe on 3100 and ClickHouse on 8123, 9000 and 9009 never leave loopback, and the collector listens nowhere

The only entries are 22 (SSH) and 80 and 443 (nginx), on both IPv4 and IPv6. Everything else the image runs, Gigapipe on 127.0.0.1:3100 and ClickHouse on 127.0.0.1:8123, 127.0.0.1:9000 and 127.0.0.1:9009, is bound to loopback and is not reachable from off the box, and the collector opens no listening port at all. Because this lists the full set rather than a filtered subset, it is also the check to re run after you install anything else on the VM.

The front door itself answers over HTTPS, and only with the credential. This reads the per instance password from the root only credentials file, then probes the readiness endpoint with and without it, and plain HTTP:

QP=$(sudo awk -F= '/^QRYN_PASSWORD=/{print $2}' /root/qryn-credentials.txt)
curl -sk -o /dev/null -w 'https /ready with credential -> %{http_code}\n' -u "admin:${QP}" https://127.0.0.1/ready
curl -sk -o /dev/null -w 'https /ready without credential -> %{http_code}\n' https://127.0.0.1/ready
curl -s -o /dev/null -w 'http / -> %{http_code}\n' http://127.0.0.1/

Expected output:

https /ready with credential -> 200
https /ready without credential -> 401
http / -> 301

Step 5: Retrieve your per instance credentials

Every VM generates its own secrets on first boot. They are written to a root only file:

sudo cat /root/qryn-credentials.txt

This file contains:

  • QRYN_USER / QRYN_PASSWORD — the HTTP basic authentication credential for the explorer and every API
  • QRYN_URL — the HTTPS URL of your instance
  • CLICKHOUSE_ADMIN_USER / CLICKHOUSE_ADMIN_PASSWORD — the ClickHouse default user, for administering the database from the VM
  • CLICKHOUSE_APP_USER / CLICKHOUSE_APP_PASSWORD — the gigapipe ClickHouse user the service connects with, limited to the gigapipe database
  • CLICKHOUSE_DB — the database that holds every log, metric, trace and profile

The per VM credentials file, root only, listing the basic authentication user, its password and URL, and both ClickHouse users, every value unique to this VM

The same basic authentication credential is enforced twice, by nginx (/etc/nginx/qryn.htpasswd) and by Gigapipe itself (QRYN_LOGIN and QRYN_PASSWORD in /etc/gigapipe/gigapipe.env), and the collector uses it too (/etc/otelcol-contrib/otelcol-contrib.conf). Keep the file safe; the section on rotating the credential below explains how to change it.

Step 6: Understand the security model

Nothing usable is baked into the image. Everything security sensitive is generated uniquely on the first boot of every VM, before Gigapipe is allowed to start:

  • the ClickHouse default (administrator) password, so the database is never without one
  • the ClickHouse gigapipe application password, for a user that only has rights on the gigapipe database
  • the 24 character basic authentication password for the explorer and every ingest and query API
  • a per instance self signed TLS certificate
  • the ClickHouse schema itself, created by Gigapipe's first start on your VM, so the database holds only your data

Gigapipe and ClickHouse bind to loopback and the collector opens no port, so nginx on 443 is the only network facing surface, and it asks for the credential on every path except the unauthenticated /healthz liveness probe. You can verify the whole model end to end with the bundled self test. It proves the front door works over TLS, that requests without a credential or with a wrong password are refused both at nginx and on loopback, that the explorer is served, that a log line pushed through the Loki API is found again with LogQL, that a metric pushed through the OTLP API is found again with PromQL, that the VM's own journal and host metrics from the collector are queryable, that ClickHouse refuses a password less default login and accepts the per VM credentials, and that the exposed port set is exactly 22, 80 and 443:

sudo /usr/local/bin/qryn-selftest

Expected output:

OK qryn self-test passed: healthz 200, :80 301, unauthenticated 401 (edge + loopback), wrong password 401, /ready 200 + explorer UI served, Loki push -> LogQL round-trip OK (cloudimg-qryn-selftest-...), OTLP metric -> PromQL round-trip OK (...), collector journald + host metrics queryable, ClickHouse default refuses no-password + per-VM creds OK + schema present (29 tables), exposed ports exactly {22,80,443}, gigapipe/ClickHouse loopback-only, collector listens nowhere, no swap

The bundled self test proving the TLS front door, refused unauthenticated and wrong password requests, the explorer, a Loki push to LogQL round trip, an OTLP metric to PromQL round trip, the collector's data and the ClickHouse password checks

ClickHouse really does refuse a login without a password, even from the VM itself, and accepts the per instance one:

clickhouse-client -q 'SELECT 1' >/dev/null 2>&1 && echo 'no password -> accepted' || echo 'no password -> refused'
CP=$(sudo awk -F= '/^CLICKHOUSE_ADMIN_PASSWORD=/{print $2}' /root/qryn-credentials.txt)
clickhouse-client --password "${CP}" -q "SELECT concat('per VM password -> ClickHouse ', version())"

Expected output:

no password -> refused
per VM password -> ClickHouse 26.8.2.7

Step 7: Open the explorer

Open your instance in a browser:

https://<vm-ip>/

Because the TLS certificate is self signed and per VM, your browser will warn once. Accept it to continue, or install a CA issued certificate as described below to remove the warning. The browser then asks for the basic authentication credential: sign in as QRYN_USER with QRYN_PASSWORD from your credentials file. On a successful sign in, nginx sets an HttpOnly session cookie for the explorer, so its queries are authenticated for the rest of the browser session without entering the credential again.

You land on the explorer's search view with the Logs data source selected. The data sources point at the same origin you signed in to, so nothing needs configuring: the collector has already been shipping this VM's journal and host metrics since first boot, and the label browser is populated.

The Gigapipe explorer after signing in, with the Logs data source selected and the query bar, time range picker and label browser ready

Enter a LogQL stream selector and press Show Results. {service_name="journald"} returns the VM's own systemd journal: every line carries the unit, syslog_identifier, level, host_name and service_name labels the collector attached, so {service_name="journald", unit="ssh.service"} shows SSH logins, {service_name="journald", level=~"WARN|ERROR"} shows anything that went wrong, and the query below narrows the journal to the appliance's own service units.

A LogQL query for the appliance's own service units returning live log lines from the VM's systemd journal, with the unit, syslog identifier and level labels the collector attached

The Labels button opens the label browser, which lists every label the collector attached and their values, so you can build a stream selector by clicking rather than typing.

The label browser listing host_name, level, os_type, service_name, syslog_identifier and unit for the VM's journal

Switch the data source to Metrics and enter a PromQL expression. The collector scrapes host metrics every 15 seconds, so system_memory_usage_bytes{state="used"}, avg(system_cpu_utilization{state!="idle"}), system_filesystem_usage_bytes{mountpoint="/", state="used"} and rate(system_network_io_bytes_total[5m]) all chart immediately.

A PromQL query charting the VM's used memory from the host metrics the collector ships every 15 seconds

The Data Sources settings page (/#/datasources) shows the Logs, Metrics and Traces sources all pointing at the address you signed in on, which is your own instance. Nothing needs to be entered there: the explorer authenticates through the session your browser established when it answered the credential prompt. This page is where you would point the explorer at a second Gigapipe instead, or add request headers.

The explorer's data source settings, with the Logs, Metrics and Traces sources all pointing at the address the explorer was opened on (an SSH tunnel to the VM in this capture; your instance's address on yours)

Step 8: Query from the command line

Every API is available from the VM at https://127.0.0.1/ and from your workstation at https://<vm-ip>/ (the QRYN_URL value from your credentials file), always with the basic authentication credential and, until you install a CA issued certificate, curl -k. The Loki label API lists what the collector has produced so far:

QP=$(sudo awk -F= '/^QRYN_PASSWORD=/{print $2}' /root/qryn-credentials.txt)
curl -sk -u "admin:${QP}" https://127.0.0.1/loki/api/v1/labels | jq -c .data
curl -sk -u "admin:${QP}" https://127.0.0.1/loki/api/v1/label/level/values | jq -c .data

Expected output:

["host_name","level","os_type","service_name","syslog_identifier","unit","job","source"]
["INFO","WARN"]

A LogQL range query returns the VM's own SSH log lines. Gigapipe requires both start and end (nanoseconds) on range queries:

QP=$(sudo awk -F= '/^QRYN_PASSWORD=/{print $2}' /root/qryn-credentials.txt)
curl -sk -u "admin:${QP}" -G https://127.0.0.1/loki/api/v1/query_range \
  --data-urlencode 'query={service_name="journald", unit="ssh.service"}' \
  --data-urlencode "start=$(( $(date +%s) - 3600 ))000000000" \
  --data-urlencode "end=$(( $(date +%s) + 60 ))000000000" \
  --data-urlencode 'limit=2' | jq -r '.data.result[].values[][1]'

Expected output:

pam_unix(sshd:session): session opened for user azureuser(uid=1000) by azureuser(uid=0)
Accepted publickey for azureuser from 203.0.113.10 port 49497 ssh2: RSA SHA256:...

A PromQL instant query returns the latest host metric sample. The job label is hostmetrics for everything the collector scrapes:

QP=$(sudo awk -F= '/^QRYN_PASSWORD=/{print $2}' /root/qryn-credentials.txt)
curl -sk -u "admin:${QP}" -G https://127.0.0.1/api/v1/query \
  --data-urlencode 'query=system_memory_usage_bytes{state="used"}' \
  | jq -c '.data.result[0] | {name: .metric.__name__, job: .metric.job, state: .metric.state, bytes: .value[1]}'
curl -sk -u "admin:${QP}" -G https://127.0.0.1/api/v1/label/__name__/values \
  --data-urlencode 'match[]={job="hostmetrics"}' | jq '.data | length'

Expected output:

{"name":"system_memory_usage_bytes","job":"hostmetrics","state":"used","bytes":"1381797888"}
25

Step 9: Push your own data

Anything that speaks Loki, Prometheus remote write, OTLP, InfluxDB line protocol, Datadog or Elastic can send to this instance. The simplest demonstration pushes one log line through the Loki API and reads it back with LogQL:

QP=$(sudo awk -F= '/^QRYN_PASSWORD=/{print $2}' /root/qryn-credentials.txt)
NOW=$(date +%s%N)
curl -sk -o /dev/null -w 'push -> %{http_code}\n' -u "admin:${QP}" -H 'Content-Type: application/json' \
  -d "{\"streams\":[{\"stream\":{\"job\":\"guide-example\",\"app\":\"hello\"},\"values\":[[\"${NOW}\",\"hello from the deployment guide\"]]}]}" \
  https://127.0.0.1/loki/api/v1/push
sleep 2
curl -sk -u "admin:${QP}" -G https://127.0.0.1/loki/api/v1/query_range \
  --data-urlencode 'query={job="guide-example"}' \
  --data-urlencode "start=$(( $(date +%s) - 600 ))000000000" \
  --data-urlencode "end=$(( $(date +%s) + 60 ))000000000" \
  --data-urlencode 'limit=5' | jq -r '.data.result[].values[][1]'

Expected output:

push -> 204
hello from the deployment guide

Metrics work the same way. This pushes a gauge as OTLP JSON to /v1/metrics and queries it back with PromQL; service.name becomes the job label:

QP=$(sudo awk -F= '/^QRYN_PASSWORD=/{print $2}' /root/qryn-credentials.txt)
NOW=$(date +%s%N)
curl -sk -o /dev/null -w 'push -> %{http_code}\n' -u "admin:${QP}" -H 'Content-Type: application/json' \
  -d "{\"resourceMetrics\":[{\"resource\":{\"attributes\":[{\"key\":\"service.name\",\"value\":{\"stringValue\":\"guide-example\"}}]},\"scopeMetrics\":[{\"metrics\":[{\"name\":\"guide_example_gauge\",\"gauge\":{\"dataPoints\":[{\"asDouble\":42,\"timeUnixNano\":\"${NOW}\"}]}}]}]}]}" \
  https://127.0.0.1/v1/metrics
sleep 2
curl -sk -u "admin:${QP}" -G https://127.0.0.1/api/v1/query --data-urlencode 'query=guide_example_gauge' \
  | jq -c '.data.result[0] | {metric: .metric, value: .value[1]}'

Expected output:

push -> 200
{"metric":{"__name__":"guide_example_gauge","job":"guide-example","service_name":"guide-example"},"value":"42"}

The endpoints your agents and applications should use, all under https://<vm-ip> with the basic authentication credential:

Data Send to Compatible with
Logs /loki/api/v1/push Promtail, Grafana Alloy, Fluent Bit, Vector and any Loki client
Logs, metrics, traces, profiles /v1/logs, /v1/metrics, /v1/traces, /v1development/profiles OpenTelemetry Collector and SDKs (OTLP over HTTP, protobuf or JSON)
Metrics /api/v1/prom/remote/write Prometheus remote_write, the OpenTelemetry prometheusremotewrite exporter
Metrics /influx/api/v2/write Telegraf and InfluxDB line protocol clients
Traces /tempo/api/push and /api/v2/spans Tempo and Zipkin clients
Logs and metrics /api/v2/logs, /api/v2/series Datadog agent formats
Logs /_bulk Elastic bulk clients

Point an OpenTelemetry Collector at the instance by adding an otlphttp exporter with basic authentication:

extensions:
  basicauth/gigapipe:
    client_auth:
      username: admin
      password: <QRYN_PASSWORD>
exporters:
  otlphttp/gigapipe:
    endpoint: https://<vm-ip>
    auth:
      authenticator: basicauth/gigapipe
    tls:
      insecure_skip_verify: true   # until you install a CA issued certificate
service:
  extensions: [basicauth/gigapipe]
  pipelines:
    logs:    { receivers: [otlp], exporters: [otlphttp/gigapipe] }
    metrics: { receivers: [otlp], exporters: [otlphttp/gigapipe] }
    traces:  { receivers: [otlp], exporters: [otlphttp/gigapipe] }

For Prometheus, add a remote_write block:

remote_write:
  - url: https://<vm-ip>/api/v1/prom/remote/write
    basic_auth:
      username: admin
      password: <QRYN_PASSWORD>
    tls_config:
      insecure_skip_verify: true   # until you install a CA issued certificate

The OTLP over gRPC receiver is multiplexed on Gigapipe's loopback port and is not exposed by this image; use OTLP over HTTP through nginx, which every OpenTelemetry exporter supports. If you need gRPC from other hosts, add a location with grpc_pass http://127.0.0.1:3100; to the nginx site.

Step 10: Use Grafana, if you want to

Grafana is not included in this image. If you run Grafana elsewhere, add this instance as a Loki data source (URL https://<vm-ip>), as a Prometheus data source (the same URL) and as a Tempo data source (the same URL), each with Basic auth enabled and the QRYN_USER / QRYN_PASSWORD credential, and Skip TLS verify until you install a CA issued certificate. No plugin is needed: Gigapipe implements the native Loki, Prometheus and Tempo query APIs, so Explore, dashboards, alerting and log to trace correlation work as they would against the originals.

Step 11: Configuration, retention and key paths

Gigapipe reads its configuration from /etc/gigapipe/gigapipe.env, generated on first boot. Any variable from the upstream configuration reference can be added to it; these are the ones that matter most, shown with the secrets redacted:

sudo sed -E 's/^(CLICKHOUSE_AUTH|QRYN_PASSWORD)=.*/\1=********/' /etc/gigapipe/gigapipe.env | grep -vE '^#|^$'

Expected output:

CLICKHOUSE_SERVER=127.0.0.1
CLICKHOUSE_PORT=9000
CLICKHOUSE_DB=gigapipe
CLICKHOUSE_AUTH=********
HOST=127.0.0.1
PORT=3100
QRYN_LOGIN=admin
QRYN_PASSWORD=********
SAMPLES_DAYS=7
LOG_LEVEL=info
  • SAMPLES_DAYS — retention in days for logs, metrics and traces (7 as shipped; applied as a ClickHouse TTL). Raise it once you have sized the disk.
  • LOG_LEVELdebug, info, warn or error.
  • BULK_MAX_AGE_MS and BULK_MAX_SIZE_BYTES — how long and how much Gigapipe batches before flushing to ClickHouse.
  • QRYN_RULER_ENABLED=true — enable the recording rules engine and its /api/v1/rules endpoints.
  • LOG_DRILLDOWN=true — enable log pattern detection.

After editing, restart the service:

$ sudo systemctl restart gigapipe

ClickHouse holds everything under /var/lib/clickhouse. Its own settings live in /etc/clickhouse-server/config.d/cloudimg.xml (loopback only, telemetry off, memory capped at 60 percent of RAM) and the two users in /etc/clickhouse-server/users.d/cloudimg-users.xml (SHA-256 password hashes, loopback only). You can see how much space each table uses with the administrator credential:

CP=$(sudo awk -F= '/^CLICKHOUSE_ADMIN_PASSWORD=/{print $2}' /root/qryn-credentials.txt)
clickhouse-client --password "${CP}" -q "SELECT count() AS tables FROM system.tables WHERE database='gigapipe'"
clickhouse-client --password "${CP}" -q "SELECT table, formatReadableSize(sum(bytes_on_disk)) AS size, sum(rows) AS rows FROM system.parts WHERE database='gigapipe' AND active GROUP BY table ORDER BY sum(bytes_on_disk) DESC LIMIT 3 FORMAT PrettyCompactNoEscapes"

Expected output:

29
   ┌─table───────┬─size──────┬─rows─┐
1. │ metrics_15s │ 41.35 KiB │ 1746 │
2. │ samples_v3  │ 40.42 KiB │ 2606 │
3. │ time_series │ 20.75 KiB │  194 │
   └─────────────┴───────────┴──────┘

The collector's pipeline is /etc/otelcol-contrib/config.yaml. To ship more of this VM, add receivers there (a filelog receiver for application log files, for example); to receive telemetry from other hosts, send it straight to Gigapipe's endpoints above rather than through the local collector.

Path Purpose
/etc/gigapipe/gigapipe.env Gigapipe configuration: ClickHouse connection, basic authentication credential, retention
/usr/local/bin/gigapipe The Gigapipe 5.4.4 release binary
/opt/qryn-view/dist The explorer UI bundle served by nginx
/root/qryn-credentials.txt Per VM credentials and URL, root only
/var/lib/clickhouse ClickHouse data (every log, metric, trace and profile)
/etc/clickhouse-server/config.d/cloudimg.xml ClickHouse server settings: loopback only, no telemetry, memory cap
/etc/clickhouse-server/users.d/cloudimg-users.xml The default and gigapipe ClickHouse users
/etc/apt/preferences.d/clickhouse-pin Pins ClickHouse to the verified version; edit it to upgrade deliberately
/etc/otelcol-contrib/config.yaml The collector pipeline (journald and host metrics into Gigapipe)
/etc/otelcol-contrib/otelcol-contrib.conf The collector's environment, including its copy of the credential
/etc/nginx/sites-available/cloudimg-qryn The TLS and basic authentication front door
/etc/nginx/qryn.htpasswd The basic authentication user database
/etc/nginx/conf.d/cloudimg-qryn-session.conf The explorer session token map, root only, re minted by qryn-set-password
/usr/local/sbin/qryn-set-password Rotates the basic authentication credential everywhere at once
/etc/nginx/tls/qryn.crt and .key The per VM TLS certificate and key
/usr/local/bin/qryn-selftest End to end appliance self test
/var/log/cloudimg-firstboot.log First boot log

Confirm the baked versions at any time. The Gigapipe binary is the official release, unchanged since the image was built:

sha256sum /usr/local/bin/gigapipe
dpkg-query -W -f='${Package} ${Version}\n' clickhouse-server otelcol-contrib

Expected output:

39ec6718d17a3153da593f215e91cf69bb3d5ec07d9887ff1e8118b91f154f69  /usr/local/bin/gigapipe
clickhouse-server 26.8.2.7
otelcol-contrib 0.160.0

Step 12: Rotate the credential and replace the certificate

The basic authentication credential is enforced by nginx and by Gigapipe, used by the collector, recorded in the credentials file, and backed by the explorer session token in nginx. One helper updates all of them together and restarts the services (the password must be at least 12 characters and contain no colon, quotes or spaces):

$ sudo qryn-set-password '<new-password>'

Every client, browser or collector then needs the new password; existing explorer sessions are signed out because the session token is re minted with it.

The per VM certificate is self signed. For a real deployment, point a DNS name at the VM, obtain a CA issued certificate for it, install the certificate and key over /etc/nginx/tls/qryn.crt and /etc/nginx/tls/qryn.key, and reload:

$ sudo systemctl reload nginx

The qryn-tls unit only mints a certificate when none exists, so a certificate you install is never overwritten.

Backups

All state lives in ClickHouse under /var/lib/clickhouse and in the configuration files listed above. The simplest consistent backup is an Azure managed disk snapshot of the OS disk (or of the data disk if you move /var/lib/clickhouse onto one). For logical backups, ClickHouse's own BACKUP DATABASE gigapipe TO ... statement works once you configure a backup destination in /etc/clickhouse-server/config.d/, as the ClickHouse documentation describes. Keep a copy of /etc/gigapipe/gigapipe.env, /etc/clickhouse-server/users.d/cloudimg-users.xml and /root/qryn-credentials.txt off the VM.

Security notes

  • Nothing is reachable without a credential. Gigapipe and ClickHouse bind to loopback and the collector opens no port; nginx on 443 is the only network facing surface and it demands basic authentication on every path except /healthz. Gigapipe enforces the same credential a second time on loopback. The explorer's session cookie is issued only after a successful basic authentication page load, is HttpOnly, Secure and SameSite strict, lasts for the browser session, and is as secret as the password: rotate the password to invalidate it.

  • Every secret is per virtual machine. Both ClickHouse passwords, the basic authentication password and the TLS certificate are generated on first boot; the ClickHouse default user is never without a password, and Gigapipe is prevented from starting until first boot has produced the secrets.

  • The image ships empty. The gigapipe database is created for the first time on your VM, so it holds only telemetry produced there. The collector ships this VM's own journal, which will include the source addresses of SSH connection attempts; restrict port 22 to your management network and keep key based authentication.

  • Use TLS end to end. Replace the self signed certificate with a CA issued one before sending production telemetry, and drop insecure_skip_verify from your exporters once you have.

  • The base image ships fully patched with unattended security upgrades enabled. Gigapipe and the collector are installed from pinned release artefacts and ClickHouse is pinned by an apt preference, so none of the three is upgraded automatically; take a newer release deliberately, after reading its release notes, by replacing the binary or package and restarting the service.

Support

cloudimg provides 24/7 support with a guaranteed 24 hour response SLA for this image. Gigapipe and its explorer are open source under the GNU Affero General Public License v3 and are shipped unmodified; ClickHouse and the OpenTelemetry Collector are Apache 2.0. cloudimg is not affiliated with Gigapipe, HEPVEST BV, Grafana Labs, ClickHouse Inc. or The Linux Foundation. For questions about the image, its configuration or its security posture, contact cloudimg support.