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:
--versionprints 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 localgo build) reportsdev/none/unknownrather than a misleading guess.--dry-runvalidates configuration and (where applicable) live connectivity — Postgres reachability and thepg_stat_statementsextension foroperator/collector, source-type/address validity foringester, Kubernetes API config resolution formanager— then exits with status0on success or1(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-typebefore 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 totrueto 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-minuteswith 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()-retentionwork 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¶
- Getting Started — these flags/fields used in full runnable commands.
- Detection Algorithm — what
--window-minutes,--min-executions,--latency-threshold, and--changepoint-toleranceactually control. - Persistence — the
--state-*flags in depth. - Deploy Sources & Webhooks —
--source-typeandsourceTypeper webhook source. - Auto-Abort (Argo Rollouts) —
autoAbort.enabled/autoAbort.confidenceThresholdin depth, including the RBAC gate. - Periodic (Deploy-Independent) Detection —
--periodic-detection/periodicDetection.*in depth, including the false-positive caveat and re-arm suppression model. - Multi-Cluster (Fleet) Mode —
remoteClusterRef,cnpgClusterRef, and cluster health checks in depth, including the hub-spoke RBAC split. - API Versioning & Compatibility — what
v1alpha1guarantees (and doesn't) for the CRDs documented above.