Metrics
Prometheus metrics exposed by the controller, including recovery success rates and duration
Trilio Site Recovery exposes Prometheus-compatible metrics from both the quorum control-plane manager and the workload-side DRBD node agent. These metrics let you track replication health, failover outcomes, and controller activity without leaving your existing monitoring stack. This page explains what metrics are available, how the metrics server is configured, and how to query and interpret the most operationally important signals — including recovery success rates and failover duration.
Before working with Site Recovery metrics you need:
- A Site Recovery deployment with at least one
ProtectionZoneinAvailablephase (quorum and both workload clusters operational). - Access to the quorum cluster with
ocorkubectland appropriate RBAC permissions. - Prometheus or a compatible scraper reachable from the quorum and workload cluster networks.
- OpenShift 4.17 or later on all three clusters (quorum, primary, DR) — the validated floor per
docs/tech-preview-supported-configurations.md. - Familiarity with
kubectl getandoccommands;ocis required for OpenShift-specific paths such asoc adm must-gather.
Metrics endpoints are enabled by default — no separate installation step is required. The quorum control-plane Deployment and the DRBD node agent DaemonSet both start a metrics server automatically when metrics.enabled is true (the default).
Verify the quorum control-plane metrics server
- Confirm the quorum control-plane pod is running in the zone namespace (
dr-<name>):
oc get pods -n dr-<name> \
-l app.kubernetes.io/component=quorum-control-plane,app.kubernetes.io/part-of=site-recovery
Expected output (single container named manager, 1/1 Running):
NAME READY STATUS RESTARTS AGE
<release>-site-recovery-quorum-control-plane-... 1/1 Running 0 10m
- Confirm the metrics port is reachable. The default port is
8443(HTTPS, authenticated):
oc -n dr-<name> port-forward deploy/<release>-site-recovery-quorum-control-plane 8443:8443
Then in a second terminal:
curl -k https://localhost:8443/metrics
You should see Prometheus exposition-format output beginning with # HELP lines.
Verify the DRBD node agent metrics server
The DRBD node agent (drbd-node-agent) runs on the host network. Its metrics server defaults to disabled (metricsAddr: "0"). When enabled via the chart (agent.metricsPort), each agent pod listens on agent.metricsPort (default 18091) on the host IP.
# Identify an agent pod on a specific worker node
oc get pods -n trilio-site-recovery-system \
-l app.kubernetes.io/component=drbd-node-agent,app.kubernetes.io/part-of=site-recovery \
-o wide
# Port-forward to that pod (substitute the pod name)
oc -n trilio-site-recovery-system port-forward <agent-pod> 18091:18091
# Scrape in a second terminal
curl http://localhost:18091/metrics
Note: The agent metrics endpoint has no authentication or TLS by default. Enable only in environments where host-network access is controlled.
Metrics behaviour is controlled by values in the embedded Helm charts, which the ProtectionZone controller applies automatically — you do not run helm install directly. To change these settings, update the relevant fields in the ProtectionZone CR or through the site-recovery-logging ConfigMap in the zone namespace.
Quorum control-plane metrics
| Key | Default | Effect |
|---|---|---|
metrics.enabled | true | Enables or disables the metrics server entirely. |
metrics.port | 8443 | The port the metrics HTTPS server binds to inside the manager container. |
metrics.secure | true | Serves metrics over HTTPS with token authentication and authorization when true. Set to false only in isolated lab environments. |
When metrics.secure is true (the default), the metrics server applies WithAuthenticationAndAuthorization — scraper clients must present a valid bearer token and be authorized by the cluster RBAC. This is the correct setting for production: scraping unauthenticated metrics from a controller that holds kubeconfig Secrets would be a security regression.
DRBD node agent metrics
| Key | Default | Effect |
|---|---|---|
agent.metricsPort | 18091 | The port the agent metrics server binds to on the host network. Only active when the agent starts with a non-"0" bind address. |
The agent's metrics server is disabled by default ("0"). The agent runs on the host network where common ports such as 8080 and 8081 may already be bound by other node components. When you enable it, choose a port not already used on your worker nodes.
Runtime log-level and observability
Log verbosity for all controllers is managed through the site-recovery-logging ConfigMap in the zone namespace (dr-<name>). This ConfigMap is watched at runtime — no pod restart is required.
oc -n dr-<name> get configmap site-recovery-logging -o yaml
The level key controls the global level. Per-component keys override it:
| Key | Valid values | Component |
|---|---|---|
level | error, info, debug, trace | All controllers |
level.control-plane | error, info, debug, trace | Quorum control-plane |
level.drbd-node-agent | error, info, debug, trace | DRBD node agent |
level.protectionzone | error, info, debug, trace | ProtectionZone controller |
Note: There is no
warnlevel. The controller uses logr's two-class model (info/error). Settingtraceis equivalent to integer level2at startup.
To raise verbosity on the quorum control-plane at runtime:
oc -n dr-<name> patch configmap site-recovery-logging \
--type merge \
-p '{"data":{"level.control-plane":"debug"}}'
Site Recovery metrics fall into three categories: controller-runtime framework metrics (work queue depth, reconcile errors, webhook latency), replication health metrics (sync state, out-of-sync bytes, RPO), and failover operation metrics (duration, success/failure counts). You observe replication and failover health primarily through the Custom Resources (ReplicationGroupStatus, RPOEvent, ProtectionGroup, FailoverRequest) and the TSR web console; Prometheus metrics complement this with time-series data for alerting and dashboards.
Scraping the quorum control-plane
Configure your Prometheus scrape job to use token authentication against the quorum API server's RBAC. Example ServiceMonitor (OpenShift / Prometheus Operator):
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: site-recovery-quorum
namespace: dr-<name>
spec:
selector:
matchLabels:
app.kubernetes.io/part-of: site-recovery
app.kubernetes.io/component: quorum-control-plane
endpoints:
- port: metrics
scheme: https
tlsConfig:
insecureSkipVerify: true
bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
Querying replication health via the TSR backend API
The site-recovery-backend Flask API exposes zone-level and per-group replication status endpoints. On OpenShift the API is reachable through the oauth-proxy Route created by the ProtectionZone controller:
# Retrieve the console Route hostname
oc -n dr-<name> get route -o jsonpath='{.items[0].spec.host}'
# Query the replication status for a zone
curl -sk -H "Authorization: Bearer $(oc whoami -t)" \
https://<route-host>/api/protection-zones/<zone_id>/replication-status
# Query per-group replication status
curl -sk -H "Authorization: Bearer $(oc whoami -t)" \
https://<route-host>/api/protection-zones/<zone_id>/protection-groups/<pg_name>/replication-status
# Query recent RPO events
curl -sk -H "Authorization: Bearer $(oc whoami -t)" \
https://<route-host>/api/protection-zones/<zone_id>/rpo-events
# Query RPO compliance summary
curl -sk -H "Authorization: Bearer $(oc whoami -t)" \
https://<route-host>/api/protection-zones/<zone_id>/rpo-compliance
Monitoring failover operations via kubectl
Failover outcome and duration are best read from the FailoverRequest CR status rather than a metric time series, because each failover is a discrete event:
# List all failover requests in a zone namespace
oc -n dr-<name> get failoverrequests
# Describe a specific request to see per-VM status
oc -n dr-<name> describe failoverrequest <name>
# Watch failover progress live
oc -n dr-<name> get failoverrequest <name> -w
The status.state field transitions through Pending → InProgress → Completed | Failed. Per-VM detail is in status.vmStatuses (not a failedVMs field — that field does not exist).
Monitoring replication health via kubectl
# View aggregated replication health for all protection groups in a zone
oc -n dr-<name> get replicationgroupstatuses
# Detailed view of one group (includes per-volume sync progress)
oc -n dr-<name> describe replicationgroupstatus <pg-name>-replication
# View recent RPO violation events
oc -n dr-<name> get rpoevents
# Watch DRBDResource sync state on the primary cluster
oc -n <vm-namespace> get drbdresources -w
Filter RPO events by protection group using the label stamped at creation:
oc -n dr-<name> get rpoevents \
-l siterecovery.trilio.io/protection-group=<pg-name>
Field selectors on Custom Resources: CRDs in this product do not declare
selectableFields, so--field-selectoronspec.*orstatus.*fields is not supported and will return an error. Use thesiterecovery.trilio.io/protection-grouplabel selector instead.
Example 1 — Check overall zone health
Use the health API to confirm all components are operational before running a failover:
ROUTE=$(oc -n dr-<name> get route -o jsonpath='{.items[0].spec.host}')
TOKEN=$(oc whoami -t)
curl -sk -H "Authorization: Bearer $TOKEN" \
https://$ROUTE/api/protection-zones/<zone_id>/health
Expected response (all checks healthy):
{
"status": "healthy",
"zone": "<zone_id>",
"checks": [
{"name": "quorum-reachable", "status": "healthy"},
{"name": "cluster1-reachable", "status": "healthy"},
{"name": "cluster2-reachable", "status": "healthy"}
]
}
Example 2 — Query per-group RPO status
curl -sk -H "Authorization: Bearer $TOKEN" \
https://$ROUTE/api/protection-zones/<zone_id>/protection-groups/<pg_name>/replication-status
Expected fields in the response include rpoStatus (Met | Degraded | Violated | Unknown), currentRPOSeconds, lastFullySyncedAt, and per-volume outOfSyncBytes. A rpoStatus of Met with currentRPOSeconds: 0 confirms Protocol C synchronous replication is working correctly (RPO=0).
Example 3 — Observe ReplicationGroupStatus for a protection group
The ReplicationGroupStatus CR (short name rgs) is auto-created by the replication monitor and named <pg-name>-replication:
oc -n dr-<name> get rgs <pg-name>-replication -o yaml
Key fields in the output:
status:
rpoStatus: Met # Met | Degraded | Violated | Unknown
currentRPOSeconds: 0 # 0 for synchronous Protocol C
lastFullySyncedAt: "2026-05-01T10:23:00Z"
aggregated:
connectionState: Connected
outOfSyncBytes: 0
syncedVolumeCount: 4
totalVolumeCount: 4
avgWriteLatencyMilliseconds: 3
Example 4 — Review RPO events for compliance reporting
RPO events are immutable audit records. List all events for a protection group:
oc -n dr-<name> get rpoevents \
-l siterecovery.trilio.io/protection-group=<pg-name> \
--sort-by=.spec.timestamp
Example output columns:
PROTECTION-GROUP EVENT-TYPE RPO-SECONDS OUT-OF-SYNC REASON TIMESTAMP
pg-prod SyncCompleted 0 0 2026-05-01T09:00:00Z
pg-prod ConnectionLost 45 2097152 NetworkTimeout 2026-05-01T10:00:00Z
pg-prod ConnectionRestored 0 0 2026-05-01T10:01:30Z
Example 5 — Measure failover duration from FailoverRequest status
oc -n dr-<name> get failoverrequest <name> \
-o jsonpath='Start: {.status.startTime}%nEnd: {.status.completionTime}%nState: {.status.state}%n'
Expected output for a completed planned failover:
Start: 2026-05-01T11:00:00Z
End: 2026-05-01T11:03:42Z
State: Completed
For per-VM detail:
oc -n dr-<name> get failoverrequest <name> \
-o jsonpath='{range .status.vmStatuses[*]}{.name}: {.phase}{"\n"}{end}'
Example 6 — Run a health check and review results
# Trigger an on-demand health check
curl -sk -X POST -H "Authorization: Bearer $TOKEN" \
https://$ROUTE/api/protection-zones/<zone_id>/health/run
# Retrieve the detailed check results
curl -sk -H "Authorization: Bearer $TOKEN" \
https://$ROUTE/api/protection-zones/<zone_id>/health/checks
Example 7 — Dashboard statistics for a zone
curl -sk -H "Authorization: Bearer $TOKEN" \
https://$ROUTE/api/protection-zones/<zone_id>/dashboard/stats
The response includes counts of protected VMs, active failovers, replication health summary, and operation statistics — useful as a top-level health signal for an external dashboard.
Issue: ReplicationGroupStatus shows Degraded or Critical unexpectedly
Symptom: The health field on a ReplicationGroupStatus object transitions to Degraded or Critical without a user-initiated failover.
Likely cause: The observed replication lag has exceeded spec.rpoObjectiveSeconds, or the DRBD connection between primary and DR nodes has dropped. An RPOEvent will be present in the same namespace.
Fix:
- List recent
RPOEventrecords for the affected group:oc get rpoevent -n dr-<name> \ -o jsonpath='{range .items[*]}{.spec.eventType}\t{.spec.violationReason}\t{.spec.rpoAtEvent}s\n{end}' - Check DRBD node agent logs on the affected worker nodes:
oc logs -n <workload-namespace> \ -l app.kubernetes.io/part-of=site-recovery,app.kubernetes.io/component=workload-control-plane \ --tail=200 - Verify TCP port 7000–8000 is open between primary and DR worker nodes.
- If using Protocol C (synchronous), confirm round-trip latency is below 50ms; elevated latency causes write stalls that manifest as lag.
Issue: No metrics appear in Prometheus after configuring the ServiceMonitor
Symptom: The Prometheus target for site-recovery-quorum shows DOWN or never appears.
Likely cause: The Service selector labels do not match the pods, or the metrics port in the ServiceMonitor does not match the port exposed by the Service.
Fix:
- Confirm the quorum control plane pods are running and
1/1:oc get pods -n dr-<name> \ -l app.kubernetes.io/part-of=site-recovery,app.kubernetes.io/component=quorum-control-plane - Verify the
Serviceendpoint resolves to a pod IP:oc get endpoints site-recovery-quorum-metrics -n dr-<name> - Check that the port name in the
ServiceMonitor(spec.endpoints[].port) exactly matches the port name defined in theService(spec.ports[].name). - If TLS is enforced on the metrics endpoint, ensure
tlsConfigis set correctly in theServiceMonitor.
Issue: RPOEvent records accumulate but no alerts fire
Symptom: oc get rpoevent -n dr-<name> shows many critical events, but no Prometheus alerts trigger.
Likely cause: PrometheusRule or alerting rules have not been created for RPOEvent-based signals, or the Prometheus instance cannot reach the quorum cluster metrics endpoint.
Fix:
- Confirm the Prometheus target is
UP(see previous issue). - Verify that any alerting rules reference the correct metric names and label matchers as exposed by the controller. If the rules were written against invented metric names, they will never match.
- Use
tsr-gatherto collect a full diagnostic bundle and share it with support:oc adm must-gather \ --image=<tsr-gather-image> \ --dest-dir=./tsr-bundle
Issue: FailoverRequest remains in InProgress indefinitely
Symptom: A FailoverRequest does not transition to Completed or Failed after several minutes.
Likely cause: The failover-controller on the quorum cluster cannot reach the primary or DR API server, DRBD volume promotion is stalled, or a VM failed to start on the target cluster.
Fix:
- Check quorum control plane logs for failover reconciler output:
oc logs -f deployment/<quorum-release-name> -n dr-<name> -c manager - Inspect per-VM status for the stuck request:
oc get failoverrequest <name> -n dr-<name> \ -o jsonpath='{.status.vmStatuses}' | python3 -m json.tool - Confirm TCP port 6443 is open from the quorum cluster to both workload cluster API servers.
- Check
DRBDResourcestatus on the DR cluster to verify volume promotion succeeded:oc --kubeconfig <dr-kubeconfig> get drbdresource -n <namespace>
Issue: TestFailover is stuck in CleaningUp and never reaches Cleaned
Symptom: A TestFailover object remains in the CleaningUp phase for an extended period.
Likely cause: The test-failover-controller is unable to delete resources in the isolated test namespace, often due to finalizers on test PVCs or VMs that cannot be released.
Fix:
- Identify the test namespace from the CR status (do not construct it manually):
oc get testfailover <name> -n dr-<name> \ -o jsonpath='{.status.testNamespace}' - Check for stuck resources in that namespace:
oc get all -n <test-namespace> - Review test-failover-controller logs for the specific error:
oc logs deployment/<quorum-release-name> -n dr-<name> -c manager | grep -i "testfailover" - Do not patch finalizers directly. Contact Trilio support and provide the
tsr-gatherbundle for guidance on safe remediation.