Skip to content

Configuration Reference

Every flag and CRD field across all four binaries, with defaults.

Overview

This page is the single reference for configuring pg-regression-radar, regardless of which binary or CRD you're using — see Architecture Overview for which one applies to your setup. Every table documents the default, including when the default is "(required)" or an empty string, since an absent default is itself information.

Operator flags (cmd/operator)

Flag Default Description
--dsn (required) Postgres connection string. Use a least-privilege role (e.g. pg_monitor) — see Installation: least-privilege role.
--cluster-name default Label added to all metrics
--namespace default Kubernetes namespace label
--scrape-interval 60s How often to read pg_stat_statements
--webhook-listen :8080 Deploy-event webhook listen address
--webhook-secret `` Shared secret for webhook authentication. When set, every POST /webhook must include this value in the X-Webhook-Token header (constant-time comparison), or the request is rejected with 401. Recommended for internet-facing deployments — see Deploy Sources & Webhooks: Webhook authentication
--metrics-listen :9090 Prometheus metrics listen address
--dashboard-addr `` Listen address for the read-only dashboard API (GET /api/v1/{regressions,deploys,queries,watches} — see API Reference). Empty (default) disables it. /api/v1/deploys and /api/v1/queries work regardless of --state-backend. /api/v1/regressions and /api/v1/watches always answer 501 Not Implemented on this binary, since cmd/operator has no Kubernetes client; run cmd/manager (--dashboard-addr there too) for those two routes
--slack-url `` Slack incoming-webhook URL. Alias of --alert-url with --alert-format=slack (the default) — kept for backward compatibility
--alert-format slack Notification payload layout: slack, teams, pagerduty, or custom — see Alerting
--alert-url `` Webhook URL for --alert-format=slack/teams/custom; ignored for pagerduty. Falls back to --slack-url when unset
--alerting-allowed-destinations `` Optional strict allowlist: comma-separated exact hostnames, IPs, or CIDRs. When set, --alert-url / --slack-url must match one of them
--alerting-destination-policy permissive Destination validation strategy: permissive (SSRF blocklist only, default), allowlist (host must be in --alerting-allowed-destinations), or relay-only (ignore --alert-url, always send to --alerting-destination-policy-relay-url) — see Alerting: destination policies
--alerting-destination-policy-relay-url `` Fixed relay endpoint used when --alerting-destination-policy=relay-only. Must be a valid http/https URL. Required (startup fails) when the policy is relay-only
--pagerduty-routing-key `` PagerDuty Events API v2 routing key; required when --alert-format=pagerduty
--alert-template `` Go text/template source, inline; required (or use --alert-template-file) when --alert-format=custom — see Alerting: custom format
--alert-template-file `` Path to a Go text/template file — alternative to --alert-template when passing the source inline isn't convenient
--source-type generic argocd, argo-rollouts, flux, generic
--window-minutes 30 Analysis window (minutes before/after deploy)
--min-executions 10 Min query executions per window
--latency-threshold 0.20 Min relative latency increase to flag (e.g. 0.20 = 20%)
--max-query-text-len 200 Max query text length (characters) stored per sample before truncation for alerting and fingerprint fallback — see Collector Internals
--retention-minutes 180 How long the collector keeps in-memory query samples before pruning them — see Collector Internals
--changepoint-tolerance 0 (auto) Max distance between the E-divisive change point and the deploy timestamp still attributed to that deploy (0 = auto: 20% of window, floor 2m)
--state-backend memory State persistence backend: memory or postgres — see Persistence
--state-dsn `` Postgres DSN for the state backend when --state-backend=postgres (defaults to --dsn)
--state-retention 168h (7 days) How long samples/events are kept in the postgres state backend
--state-prune-interval 15m How often the retention sweep runs against the postgres state backend
--capture-plans false Capture periodic EXPLAIN (GENERIC_PLAN) plan snapshots for tracked queries and attach a plan-diff summary to detected regressions. Requires PostgreSQL 16+ (logged once and otherwise a no-op on older servers); adds one extra planner invocation per tracked query per scrape cycle — see Detection Algorithm
--periodic-detection false Also run regression detection on a rolling schedule, independent of any tracked deploy — see Periodic Detection
--periodic-window-minutes 60 Split-window size for periodic detection; only meaningful alongside --periodic-detection
--periodic-interval-minutes 15 How often a full periodic-detection pass runs; only meaningful alongside --periodic-detection
--version false Print version, commit, and build date, then exit — see Versioning & dry-run
--dry-run false Validate --source-type, the alerting configuration (--alert-format and its format-specific required fields), Postgres connectivity, (if --state-backend=postgres) the state DSN, and (if --periodic-detection) that --periodic-window-minutes/--periodic-interval-minutes are positive, then exit without starting any server — see Versioning & dry-run

Standalone collector flags (cmd/collector)

The collector binary exposes the scraper on its own, without the correlation/alerting/webhook pieces:

Flag Default Description
--dsn (required) Postgres connection string. Use a least-privilege role (e.g. pg_monitor) — see Installation: least-privilege role.
--scrape-interval 60s How often to read pg_stat_statements
--cluster-name default Label added to all metrics
--namespace default Kubernetes namespace label
--listen :9090 Prometheus metrics listen address
--max-query-text-len 200 Max query text length (characters) stored per sample before truncation for alerting and fingerprint fallback — see Collector Internals
--retention-minutes 180 How long to keep in-memory query samples before pruning them
--version false Print version, commit, and build date, then exit
--dry-run false Validate Postgres connectivity (dial + pg_stat_statements installed), then exit without scraping

Ingester flags (cmd/ingester)

The ingester binary runs the deploy-event webhook receiver on its own, without the collector/correlation/alerting pieces — useful when the Correlation Engine polls it separately or when running the operator/manager mode with an external ingester:

Flag Default Description
--listen :8080 HTTP listen address for the webhook endpoint
--source-type generic argocd, argo-rollouts, flux, generic
--source-name default Unique name for this DeploySource
--postgres-watch-ref `` PostgresWatch to associate events with
--app-name `` (all apps) Filter events to a specific application name
--cluster-name `` Cluster identity stamped on DeployEvents when the webhook payload doesn't carry one (e.g. Argo Rollouts, Flux without eventMetadata)
--webhook-secret `` Shared secret for webhook authentication. When set, every POST /webhook must include this value in the X-Webhook-Token header (constant-time comparison), or the request is rejected with 401. Recommended for internet-facing deployments — see Deploy Sources & Webhooks: Webhook authentication
--version false Print version, commit, and build date, then exit
--dry-run false Validate --source-type and that --listen is a resolvable address, then exit without starting the webhook server

Exposed routes once running: POST /webhook (receives deploy events), GET /events (lists everything ingested so far, as JSON), GET /healthz. When --webhook-secret is set, GET /events requires the same X-Webhook-Token header as POST /webhook — see Deploy Sources & Webhooks: Webhook authentication. Leaving --webhook-secret unset leaves both routes unauthenticated, so don't expose this binary beyond a trusted network in that case.

Manager flags (cmd/manager)

Flag Default Description
--leader-elect true Enable leader election (multi-replica HA)
--leader-election-namespace (auto) Namespace the coordination.k8s.io Lease is created in
--metrics-bind-address 0 (disabled) controller-runtime's own reconcile/workqueue metrics
--pg-metrics-bind-address :9090 Aggregated pg_stat_statements-derived metrics (leader only)
--webhook-bind-address :8080 Deploy-event webhooks, one route per DeploySource CR (leader only)
--dashboard-addr `` Listen address for the read-only dashboard API (GET /api/v1/{regressions,watches} — see API Reference). Empty (default) disables it. Unlike --webhook-bind-address/--pg-metrics-bind-address, served on every replica (not leader-only), since it only reads the Kubernetes API server directly. /api/v1/deploys and /api/v1/queries always answer 501 Not Implemented on this binary; run cmd/operator for those two routes
--alerting-allowed-destinations `` Optional strict allowlist: comma-separated exact hostnames, IPs, or CIDRs. When set, every PostgresWatch alert destination must match one of them — see Alerting
--alerting-destination-policy permissive Manager-wide destination validation strategy: permissive (default), allowlist, or relay-only — see Alerting: destination policies
--alerting-destination-policy-relay-url `` Fixed relay endpoint for --alerting-destination-policy=relay-only. Required when the policy is relay-only
--health-probe-bind-address :8081 /healthz and /readyz
--cluster-health-check-interval 5m How often PostgresRadarClusterReconciler re-probes each registered remote cluster's kubeconfig (a lightweight discovery.ServerVersion() call), independent of whether any PostgresWatch currently references it — see Multi-Cluster: Cluster health checks
--version false Print version, commit, and build date, then exit
--dry-run false Validate that a Kubernetes API server config can be resolved (in-cluster or via kubeconfig), then exit without starting the manager

Versioning & dry-run

All four binaries share two flags:

  • --version prints a one-line <binary> <version> (commit <sha>, built <date>) string and exits immediately, before any other flag is validated. A build made without the Dockerfile's --build-arg VERSION/COMMIT/DATE (e.g. a local go build) reports dev/none/unknown rather than a misleading guess.
  • --dry-run validates configuration and (where applicable) live connectivity — Postgres reachability and the pg_stat_statements extension for operator/collector, source-type/address validity for ingester, Kubernetes API config resolution for manager — then exits with status 0 on success or 1 (with a logged error) on failure, without starting any server. Useful in CI or an init container to fail fast on a bad DSN or typo'd --source-type before the real process starts.

Tuning notes for less-frequently changed collector/operator flags

--max-query-text-len

  • What it does: Caps the stored query text length per sample before the collector truncates it and computes the fallback fingerprint.
  • Default / valid values: 200; any positive integer number of characters.
  • When to change it: Raise it if your workload has long query texts where the distinguishing part often appears after the first 200 characters and you need better alert/debug context.
  • Caveats: Setting it very low reduces the amount of text available to the fingerprint fallback described in Collector Internals. Values below roughly 20 characters materially increase the chance that different queries collapse onto the same truncated prefix and get merged together incorrectly.

--capture-plans

  • What it does: Enables periodic plan capture so detected regressions can include a short plan-diff summary alongside the latency change.
  • Default / valid values: false; set to true to enable it.
  • When to change it: Turn it on when plan context will help an operator quickly tell whether a regression lines up with an index choice, join strategy, or other planner change.
  • Caveats: It is a no-op below PostgreSQL 16 (logged once at startup/scrape time) and it adds one extra planner invocation per tracked query per scrape cycle, so leave it off if you do not need the extra diagnostic signal.

--changepoint-tolerance

  • What it does: Limits how far the detected change point may land from a deploy timestamp and still be attributed to that deploy.
  • Default / valid values: 0 (auto), which resolves to 20% of --window-minutes with a minimum of 2 minutes; any non-negative Go duration string is accepted.
  • When to change it: Increase it for slow rolling deploys or event sources whose timestamps lag the actual traffic shift; decrease it when closely spaced deploys are being merged too aggressively.
  • Caveats: Larger tolerances make it easier to attribute a genuine regression to the wrong deploy when multiple changes happen near each other.

--state-prune-interval

  • What it does: Controls how often the postgres state backend sweeps old samples and deploy events past --state-retention.
  • Default / valid values: 15m; Go duration strings, with non-positive values falling back to the default 15-minute sweep interval in the prune loop.
  • When to change it: Shorten it if you need expired state cleared sooner to keep the backing tables smaller; lengthen it if you would rather do fewer background deletes and can tolerate stale rows lingering a little longer.
  • Caveats: More frequent pruning means more frequent DELETE ... WHERE recorded_at < now()-retention work against the state database; less frequent pruning leaves more expired rows behind between sweeps.

PostgresWatch spec fields

Field Default Description
clusterName (required) Label added to metrics and to PerformanceRegression CRs
dsn / dsnSecretRef (one required) Postgres DSN, inline or via a Secret key (preferred). Use a least-privilege role (e.g. pg_monitor) — see Installation: least-privilege role. The referenced Secret must carry the pg-regression-radar.io/allow-postgreswatch-access: "true" label or the manager refuses to read it — see Multi-Cluster: Secret consent label. In multi-tenant clusters, also enforce who can set that label via admission policy (example: docs/policies/kyverno-consent-label-policy.yaml).
remoteClusterRef (none) Preferred remote-cluster path: name of a pre-registered PostgresRadarCluster (cluster-scoped). The manager reads that registry entry's kubeconfig Secret and resolves dsnSecretRef through the referenced remote cluster — see Multi-Cluster (Fleet) Mode.
remoteClusterSecretRef (none) Deprecated direct Secret path: Secret (in the hub cluster) holding a kubeconfig for a remote cluster to resolve dsnSecretRef against instead of the hub. Still accepted in controlled security-profile mode for backward compatibility, rejected in hardened mode. Same consent-label requirement as dsnSecretRef above, and the kubeconfig itself must use a static credential (no exec/auth-provider/proxy-url) — see Multi-Cluster: Kubeconfig restrictions.
remoteNamespace (same name as the watch's own namespace) Namespace to look up dsnSecretRef in on the remote cluster; meaningful alongside either remoteClusterRef or remoteClusterSecretRef — see Multi-Cluster (Fleet) Mode: What actually happens on reconcile
cnpgClusterRef (none) Name of a CloudNativePG Cluster object (postgresql.cnpg.io/v1) that must exist and be healthy before the watch starts — read through the same client (hub or remote) and namespace as dsnSecretRef. See Multi-Cluster: Remote CloudNativePG Cluster validation. When used alongside remoteClusterRef/remoteClusterSecretRef, the kubeconfig's own embedded RBAC must also grant get on this object.
scrapeIntervalSeconds 60 How often to read pg_stat_statements
windowMinutes 30 Analysis window (minutes before/after deploy)
minExecutions 10 Min query executions per window
latencyChangeThreshold "0.20" Min relative latency increase to flag (e.g. "0.20" = 20%)
pValueThreshold "0.05" Welch's t-test significance cutoff
criticalQueryIDs [] Queries that bypass minExecutions
slackWebhookUrl "" Slack incoming-webhook URL for this watch. Deprecated: use alerting instead (equivalent to alerting.format: slack); ignored entirely whenever alerting is set
alerting.format "" (→ slack) Notification payload layout: slack, teams, pagerduty, or custom — see Alerting
alerting.url "" Webhook URL for slack/teams/custom; ignored for pagerduty
alerting.destinationPolicy "" (→ permissive) Destination policy override for this watch: permissive, allowlist, or relay-only. Used only when manager-level --alerting-destination-policy is left at default/empty — see Alerting: destination policies
alerting.pagerDutyRoutingKey "" PagerDuty Events API v2 routing key; required when alerting.format is pagerduty
alerting.customTemplate "" Go text/template source; required when alerting.format is custom — see Alerting: custom format
capturePlans false Enable plan-diff correlation — see Detection Algorithm: Plan-diff correlation. Populates the resulting PerformanceRegression's status.planDiffSummary
autoAbort.enabled false Automatically abort the Argo Rollouts canary behind a high-confidence detected regression instead of only alerting — see Auto-Abort
autoAbort.confidenceThreshold "0.99" Minimum confidence required before auto-aborting; only meaningful alongside autoAbort.enabled — see Auto-Abort: Confidence threshold
periodicDetection.enabled false Also run regression detection on a rolling schedule, independent of any tracked deploy — see Periodic Detection
periodicDetection.windowMinutes 60 Split-window size for periodic detection; only meaningful alongside periodicDetection.enabled
periodicDetection.intervalMinutes 15 How often a full periodic-detection pass runs; only meaningful alongside periodicDetection.enabled
sampleSource.type "collector" Sample source backing the Correlation Engine: collector (default — internal/collector.Collector scrapes pg_stat_statements directly) or prometheus (internal/telemetry/promsource — see OpenTelemetry / Prometheus Sample Source). dsn/dsnSecretRef are not required when this is prometheus; capturePlans: true is rejected when combined with it
sampleSource.prometheus.url (required with type: prometheus) Prometheus HTTP API base, e.g. http://prometheus:9090
sampleSource.prometheus.metricName (required with type: prometheus) Prometheus metric to read samples from — see OpenTelemetry / Prometheus Sample Source: The real gap for why there's no safe default
sampleSource.prometheus.queryIDLabel "queryid" Label carrying the queryid
sampleSource.prometheus.queryTextLabel "query" Label carrying the query text; set to "-" to disable
sampleSource.prometheus.stepSeconds 15 query_range resolution

DeploySource spec fields

Field Default Description
postgresWatchRef (required) Name of the PostgresWatch (same namespace) to correlate against
sourceType generic argocd, argo-rollouts, flux, generic, kubernetes — see Deploy Sources & Webhooks: Native Kubernetes watch for kubernetes
appName "" (all apps) Narrow correlation to a single application. When sourceType is kubernetes, this is the Deployment/StatefulSet's name and is required
workloadKind "" Deployment or StatefulSet. Only meaningful (and required) when sourceType is kubernetes
webhookSecret "" Shared secret for this source's webhook, equivalent to --webhook-secret above. Not meaningful when sourceType is kubernetes (that mode has no webhook route to protect). Store the value in a Secret and reference it with valueFrom.secretKeyRef rather than inlining it — see Deploy Sources & Webhooks: Webhook authentication

Source-type capabilities by mode

sourceType / --source-type means different things depending on which binary or field is reading it, and the two don't accept the same set of values:

Where it's set Accepted values Accepts kubernetes?
operator --source-type / ingester --source-type (CLI flag, validated by --dry-run) argocd, argo-rollouts, flux, generic No — kubernetes isn't in ValidSourceTypes, so --dry-run rejects it outright, and without --dry-run it would silently fall through to generic-style webhook parsing instead of doing anything kubernetes-specific
DeploySource.spec.sourceType (CRD field, manager mode only) argocd, argo-rollouts, flux, generic, kubernetes Yes — this is the only place kubernetes is meaningful. It skips webhook route registration entirely and instead watches the Deployment/StatefulSet named in appName directly — see Deploy Sources & Webhooks: Native Kubernetes watch

In short: if you're running the standalone operator/ingester binaries, kubernetes is not a valid --source-type no matter what — that capability only exists behind the manager/CRD path.

See also