Skip to main content

Tutorial

Get started on Azure Database for PostgreSQL

Follow this guided path to take an Azure Database for PostgreSQL Flexible Server from nothing to a first Elevarq Signals snapshot. It assumes you know Azure but not PostgreSQL internals — the only database step is pasting a short block of SQL — and it ends with a working snapshot on your host. This guide uses the simple password method; passwordless Microsoft Entra auth is referenced at the end.

Topology

Signals connects to your Flexible Server like any database client, so it has to sit on a network path that reaches the server:

  • Recommended: run Signals on a VM, VMSS, or AKS in the same VNet as the server when it uses private access (VNet integration).
  • Public access works too — add a server firewall rulethat allows the Signals host's public IP to reach the server on TCP 5432.
  • Point Signals at the server endpoint (e.g. myserver.postgres.database.azure.com) on port 5432. That endpoint hostname is not the database name — the database name is the logical DB inside it (often postgresor your application's database).

Prerequisites

  • A Flexible Server running PostgreSQL 14+: its endpoint, port (5432), database name, and the admin (server administrator) username.
  • The admin username and its password.
  • Network reach from where Signals will run to that endpoint — confirm with psql first (step 1).
  • docker (this guide) or the host binaries.
  • The Azure root CA, for verify-full TLS (downloaded in step 1).

1. Confirm you can reach the database

Download the root CA that Microsoft documents for Flexible Server — currently the DigiCert Global Root G2 and Microsoft RSA Root CA 2017certificates. Use the download links in Microsoft's Networking and TLS documentation and save the certificate as signals-data/azure-ca.pem. Then connect as the admin user over verify-full TLS. If this fails, fix networking or TLS before going further:

mkdir -p signals-data
# Download the Flexible Server root CA documented by Microsoft
# (DigiCert Global Root G2 / Microsoft RSA Root CA 2017) and save it as:
#   signals-data/azure-ca.pem

psql "host=<server-name>.postgres.database.azure.com port=5432 dbname=<dbname> \
  user=<admin-user> sslmode=verify-full \
  sslrootcert=signals-data/azure-ca.pem"

2. Create the read-only Signals role

Run this once per server, connected as the admin user to the database you want to observe. The Signals role is a plain LOGIN role granted pg_monitor — read-only, least-privilege:

-- as the admin user, connected to <dbname>:
CREATE ROLE signals LOGIN PASSWORD '<choose-a-strong-password>'
  NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS;
GRANT pg_monitor TO signals;
GRANT CONNECT ON DATABASE <dbname> TO signals;

Verify the role by connecting as signals in a fresh session, using the same host and TLS settings:

psql "host=<server-name>.postgres.database.azure.com port=5432 dbname=<dbname> \
  user=signals sslmode=verify-full \
  sslrootcert=signals-data/azure-ca.pem"

Then, in that session:

SELECT count(*) FROM pg_stat_database;   -- succeeds: read access
CREATE TABLE _signals_check (id int);    -- fails: permission denied

3. (Optional) Enable query statistics

pg_stat_statements adds query-level statistics. It is optional — Signals collects everything else without it. On Flexible Server you do not edit postgresql.conf; instead use Server parameters in the Azure portal or CLI:

  • Add pg_stat_statements to the shared_preload_libraries server parameter, and ensure it is on the azure.extensions allow-list.
  • Restart the server if Azure marks the change as requiring a restart.
  • Then, connected to the database, run CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

See Azure's PostgreSQL extensions documentation for the allow-list details.

4. Write the config and an API token

# API token shared by the daemon and signalsctl:
openssl rand -hex 32 > signals-data/api-token

cat > signals.yaml <<'YAML'
signals:
  poll_interval: 6h
  retention_days: 30
api:
  listen_addr: 0.0.0.0:8081   # bind inside the container; -p limits exposure to loopback
database:
  path: /data/signals.db
targets:
  - name: primary
    host: <server-name>.postgres.database.azure.com
    port: 5432
    dbname: <dbname>
    user: signals
    sslmode: verify-full
    sslrootcert_file: /data/azure-ca.pem
    password_env: SIGNALS_PRIMARY_PASSWORD
YAML
The env-var password (password_env) keeps the first run simple. For production, store the secret in Azure Key Vault (auth_method: secret_store), or go passwordless with Microsoft Entra (azure_entra). See Authentication methods for all options.

5. Run the container

Bind-mount ./signals-data to /data so the SQLite store, the CA certificate, the token, and any exports all live in a directory you can see on the host:

docker run -d --name signals \
  -v "$PWD/signals.yaml:/etc/signals/signals.yaml:ro" \
  -v "$PWD/signals-data:/data" \
  -e SIGNALS_API_TOKEN="$(cat signals-data/api-token)" \
  -e SIGNALS_PRIMARY_PASSWORD="<the-password-from-step-2>" \
  -p 127.0.0.1:8081:8081 \
  ghcr.io/elevarq/signals:<version>
Which version? Replace <version> with a release tag from the Signals releases page and pin it (e.g. v1.2.0) for anything beyond a trial. ghcr.io/elevarq/signals:latest tracks the newest release and is fine for a quick evaluation.
The control API is bound to 0.0.0.0:8081 inside the container so the published port works, while -p 127.0.0.1:8081:8081restricts exposure to your host's loopback — reachable from this host and via docker exec, never from the network.

6. Verify, collect, export

Drive the running daemon with signalsctl inside the container — it inherits SIGNALS_API_TOKEN, so it authenticates to the local API automatically:

docker exec signals signalsctl status
docker exec signals signalsctl connect test primary
docker exec signals signalsctl collect now
docker exec signals signalsctl export --output /data/snapshot.zip

Because /data is the bind mount, the export lands at ./signals-data/snapshot.zip on the host (or pull it with docker cp signals:/data/snapshot.zip .). You now have a working snapshot.

Running docker exec signals signalsctl status shows the collector state per target; connect test primary confirms connectivity before you rely on the schedule.

Where next

To run Signals without a stored password, use the passwordless Microsoft Entra method described in Authentication methods, and see Run as a service to keep the collector running on a schedule.