Kubectl Commands
Common kubectl commands for managing site recovery resources, checking status, and inspecting events
This page is a reference for the kubectl commands you use day-to-day to manage Trilio Site Recovery resources. Because every DR operation in Site Recovery is declared through Kubernetes custom resources in the siterecovery.trilio.io/v1alpha1 API group, kubectl (and oc on OpenShift) is the primary operational interface. Use this page to look up short names, label selectors, field meanings, and common command patterns for each resource kind.
All Site Recovery custom resources live in the siterecovery.trilio.io/v1alpha1 API group. The general form for any kubectl command against them is:
kubectl <verb> <resource>[/<name>] -n <namespace> [flags]
Resources on the TSR-Manager cluster live in the zone namespace dr-<zone-name>.
Resources on each workload cluster live in the namespace of the protected VM (for DRBDResource) or in trilio-site-recovery-system (for controllers and the node-agent DaemonSet).
Important: Always confirm which cluster your
kubectlcontext targets before running commands. TSR-Manager resources (ProtectionZone,ProtectionGroup,ProtectionRequest,FailoverRequest,TestFailover,Hook,ReplicationGroupStatus,RPOEvent,DRBDReplicationPolicy) exist only on the TSR-Manager cluster.DRBDResourceexists only on workload clusters. Runningkubectl get dresagainst the TSR-Manager cluster returnsserver doesn't have a resource type.
The table below covers the kubectl flags most frequently used with Site Recovery resources. These are standard kubectl flags; no Site Recovery-specific flags exist on kubectl itself.
| Flag | Short | Default | Description |
|---|---|---|---|
--namespace | -n | (active context) | Target namespace. For TSR-Manager resources use dr-<zone-name>; for workload resources use the VM's namespace. |
--all-namespaces | -A | — | List resources across all namespaces on the targeted cluster. |
--context | — | (kubeconfig default) | Kubeconfig context to use. Use this to switch between the TSR-Manager cluster and each workload cluster. |
--kubeconfig | — | ~/.kube/config | Path to a kubeconfig file. |
--output | -o | (table) | Output format: yaml, json, wide, jsonpath=..., custom-columns=.... |
--watch | -w | — | Watch for changes and stream updates to the terminal. |
--selector | -l | — | Filter by label selector (see Label selectors below). |
--field-selector | — | — | Filter by field. Custom resources support only metadata.name and metadata.namespace; spec.* and status.* field selectors are not supported on any Site Recovery CRD. Use label selectors instead. |
--patch | — | — | Inline patch payload. |
--type | — | strategic | Patch type: strategic, merge, or json. |
--dry-run | — | none | client or server; validates a manifest without applying it. |
--force | — | — | Force-delete a resource, bypassing finalizers. Use with caution. |
--since | — | — | With kubectl logs, limit log output to a relative duration (e.g. 1h, 30m). |
--tail | — | — | With kubectl logs, limit output to the last N lines. |
--timestamps | — | — | With kubectl logs, prefix each line with its RFC3339 timestamp. |
The sections below are organised by resource kind. Each kind table shows the kubectl short name, which cluster the resource lives on, and the most useful commands.
Resource quick-reference
| Kind | Short name(s) | Cluster | Namespace |
|---|---|---|---|
ProtectionZone | pz | TSR-Manager | dr-<zone-name> |
ProtectionGroup | pg | TSR-Manager | dr-<zone-name> |
ProtectionRequest | pr, protect | TSR-Manager | dr-<zone-name> |
FailoverRequest | fr | TSR-Manager | dr-<zone-name> |
TestFailover | tfo | TSR-Manager | dr-<zone-name> |
DRBDReplicationPolicy | drp, drbdpolicy | TSR-Manager | dr-<zone-name> |
ReplicationGroupStatus | rgs | TSR-Manager | dr-<zone-name> |
RPOEvent | rpoe | TSR-Manager | dr-<zone-name> |
Hook | (none) | TSR-Manager | dr-<zone-name> |
DRBDResource | dres | Each workload cluster | VM's namespace |
Hookhas no short name;kubectl get hooksis the only form.
ProtectionZone (pz)
A ProtectionZone registers a pair of workload clusters with the TSR-Manager. The ClustersValidated condition must be True before ProtectionGroup or ProtectionRequest resources can be created or modified in the zone.
Printer columns: Display Name, Backend, Clusters, Status (phase), Age
kubectl get pz -n dr-<zone-name>
kubectl describe pz <zone-name> -n dr-<zone-name>
kubectl get pz <zone-name> -n dr-<zone-name> -o yaml
kubectl get pz <zone-name> -n dr-<zone-name> -o jsonpath='{.status.phase}'
kubectl get pz <zone-name> -n dr-<zone-name> -o jsonpath='{.status.conditions}'
ProtectionGroup (pg)
A ProtectionGroup groups one or more KubeVirt VMs for coordinated failover. It holds SLA targets (RPO/RTO), boot ordering, MAC address policy, and hook bindings.
Printer columns: VMs, State, Active (activeClusterID), Age
kubectl get pg -n dr-<zone-name>
kubectl describe pg <group-name> -n dr-<zone-name>
kubectl get pg <group-name> -n dr-<zone-name> -o jsonpath='{.status.state}'
kubectl get pg <group-name> -n dr-<zone-name> -o jsonpath='{.status.activeClusterID}'
To watch state transitions during a failover:
kubectl get pg <group-name> -n dr-<zone-name> -w
ProtectionRequest (pr, protect)
A ProtectionRequest declares protection for a single KubeVirt VM. The spec.sourceCluster field is the failover lever — patching it to the DR cluster triggers a role flip.
Printer columns: VM, Namespace, Phase, Sync (syncProgress %), Age
Phase values: Pending → Validating → CreatingDRBD → Syncing → ReadyToActivate → Activating → Protected → Unprotecting → Failed
kubectl get pr -n dr-<zone-name>
kubectl get protect -n dr-<zone-name>
kubectl describe pr <request-name> -n dr-<zone-name>
kubectl get pr <request-name> -n dr-<zone-name> -o jsonpath='{.status.phase}'
kubectl get pr <request-name> -n dr-<zone-name> -o jsonpath='{.status.syncProgress}'
To watch sync progress:
kubectl get pr -n dr-<zone-name> -w
FailoverRequest (fr)
A FailoverRequest triggers a planned or unplanned failover of all VMs in a ProtectionGroup. Per-VM detail is in status.vmStatuses; there is no status.failedVMs field.
Printer columns: Protection Group, Target, State, Phase, Age
Phase values: Initializing → Validating → RunningPreHooks → StoppingSource → WaitingForLocks → RemovingTaints → StartingTarget → Monitoring → RunningPostHooks → Completed → Failed
kubectl get fr -n dr-<zone-name>
kubectl describe fr <request-name> -n dr-<zone-name>
kubectl get fr <request-name> -n dr-<zone-name> -o jsonpath='{.status.state}'
kubectl get fr <request-name> -n dr-<zone-name> -o jsonpath='{.status.phase}'
kubectl get fr <request-name> -n dr-<zone-name> -o jsonpath='{.status.vmStatuses}'
To watch a failover in progress:
kubectl get fr <request-name> -n dr-<zone-name> -w
TestFailover (tfo)
A TestFailover triggers a non-disruptive DR drill on the standby cluster. The test namespace is reported in status.testNamespace and is generated by the controller; you never construct or specify it.
Printer columns: Protection Group, Phase, Result, Passed, Scheduling, Age
Phase values: Queued → PreparingEnvironment → CreatingSnapshots → CreatingVolumes → CreatingVMs → StartingVMs → Verifying → Succeeded → Failed → CleaningUp → Cleaned
kubectl get tfo -n dr-<zone-name>
kubectl describe tfo <tfo-name> -n dr-<zone-name>
kubectl get tfo <tfo-name> -n dr-<zone-name> -o jsonpath='{.status.phase}'
kubectl get tfo <tfo-name> -n dr-<zone-name> -o jsonpath='{.status.result}'
kubectl get tfo <tfo-name> -n dr-<zone-name> -o jsonpath='{.status.testNamespace}'
kubectl get tfo <tfo-name> -n dr-<zone-name> -o jsonpath='{.status.verificationResults}'
DRBDReplicationPolicy (drp, drbdpolicy)
A DRBDReplicationPolicy is created automatically by the ProtectionZone controller when a zone becomes available. The auto-created policy is named <zone-name>-default with isDefault: true. You do not create or modify replication policies.
Printer columns: Clusters, Mode, Protocol, Default, Phase, Age
kubectl get drp -n dr-<zone-name>
kubectl describe drp <zone-name>-default -n dr-<zone-name>
kubectl get drp <zone-name>-default -n dr-<zone-name> -o yaml
ReplicationGroupStatus (rgs)
A ReplicationGroupStatus is auto-created by the replication monitor for each ProtectionGroup. It is named <protection-group-name>-replication. Use the siterecovery.trilio.io/protection-group label to filter these objects.
Printer columns: Protection-Group, RPO-Status, Out-Of-Sync, Synced, Connection, Last-Sync, Age
kubectl get rgs -n dr-<zone-name>
kubectl get rgs -n dr-<zone-name> -l siterecovery.trilio.io/protection-group=<group-name>
kubectl describe rgs <group-name>-replication -n dr-<zone-name>
kubectl get rgs <group-name>-replication -n dr-<zone-name> -o jsonpath='{.status.rpoStatus}'
kubectl get rgs <group-name>-replication -n dr-<zone-name> -o jsonpath='{.status.aggregated}'
kubectl get rgs <group-name>-replication -n dr-<zone-name> -o jsonpath='{.status.currentRPOSeconds}'
RPOEvent (rpoe)
An RPOEvent is an immutable audit record created on each replication status transition. Use the siterecovery.trilio.io/protection-group label to filter events for a specific group.
Printer columns: Protection-Group, Event-Type, RPO-Seconds, Out-Of-Sync, Reason, Timestamp, Age
kubectl get rpoe -n dr-<zone-name>
kubectl get rpoe -n dr-<zone-name> -l siterecovery.trilio.io/protection-group=<group-name>
kubectl get rpoe -n dr-<zone-name> --sort-by=.spec.timestamp
kubectl describe rpoe <event-name> -n dr-<zone-name>
Hook
A Hook defines a Kubernetes Job to run at failover or test lifecycle points. It has no short name. The admission webhook prevents deletion or spec edits while a hook is actively used by an operation.
Printer columns: Image, Timeout, Retries, Age
kubectl get hooks -n dr-<zone-name>
kubectl describe hook <hook-name> -n dr-<zone-name>
kubectl get hook <hook-name> -n dr-<zone-name> -o yaml
DRBDResource (dres)
A DRBDResource lives on each workload cluster in the protected VM's namespace. It is created and managed by the TSR-Manager-side ProtectionRequest controller. Each resource is named drbd-<namespace>-<vm>-<hash8>. Do not run kubectl get dres against the TSR-Manager cluster — the CRD is not registered there.
Printer columns: Side, Role, Node, State (connectionState), Phase, Age
# Run these commands against a workload cluster context
kubectl get dres -n <vm-namespace> --context <workload-cluster-context>
kubectl describe dres <drbd-resource-name> -n <vm-namespace> --context <workload-cluster-context>
kubectl get dres <drbd-resource-name> -n <vm-namespace> --context <workload-cluster-context> -o jsonpath='{.status.phase}'
kubectl get dres <drbd-resource-name> -n <vm-namespace> --context <workload-cluster-context> -o jsonpath='{.status.connectionState}'
kubectl get dres <drbd-resource-name> -n <vm-namespace> --context <workload-cluster-context> -o jsonpath='{.status.role}'
kubectl get dres <drbd-resource-name> -n <vm-namespace> --context <workload-cluster-context> -o jsonpath='{.status.syncProgress}'
kubectl reads its configuration from the KUBECONFIG environment variable and standard flag defaults. Site Recovery adds no custom environment variables to kubectl.
| Variable | Default | Description |
|---|---|---|
KUBECONFIG | ~/.kube/config | Colon-separated list of kubeconfig files. When managing multiple clusters (TSR-Manager plus two workload clusters), you can merge them here or use --kubeconfig and --context per command. |
Note:
kubectldoes not read aKUBE_CONTEXTenvironment variable. To target a specific cluster, always pass--context <name>explicitly or set the active context withkubectl config use-context <name>.
kubectl exits with standard Unix exit codes.
| Code | Meaning |
|---|---|
0 | Command succeeded. |
1 | General error (invalid flags, connection failure, API error, patch rejected by admission webhook). |
When an admission webhook rejects a resource (for example, attempting to create a ProtectionGroup before ClustersValidated is True, or setting spec.storageBackendMode: Linstor), the command exits 1 and prints the webhook rejection message to stderr.
The examples below are organised by common workflow. Replace <zone-name>, <group-name>, <vm-name>, <vm-namespace>, <request-name>, and <cluster-context> with your actual values.
Check zone status
Run this on the TSR-Manager cluster context.
kubectl get pz -n dr-<zone-name>
kubectl get pz <zone-name> -n dr-<zone-name> \
-o jsonpath='{range .status.conditions[*]}{.type}{"\t"}{.status}{"\t"}{.reason}{"\n"}{end}'
The ClustersValidated condition must be True before you can create ProtectionGroup or ProtectionRequest resources.
List all protected VMs on a workload cluster
Run this on a workload cluster context. The siterecovery.trilio.io/protected-vm=true label is stamped only on VirtualMachine objects on workload clusters.
kubectl get vm -A -l siterecovery.trilio.io/protected-vm=true \
--context <workload-cluster-context>
Watch protection progress for a VM
After creating a ProtectionRequest, watch its phase advance from Syncing to Protected.
kubectl get pr <request-name> -n dr-<zone-name> -w
To extract sync percentage:
kubectl get pr <request-name> -n dr-<zone-name> \
-o jsonpath='{.status.syncProgress}'
Inspect replication health for a ProtectionGroup
The ReplicationGroupStatus is named <group-name>-replication.
kubectl get rgs <group-name>-replication -n dr-<zone-name>
To see aggregated out-of-sync bytes, connection state, and synced volume count:
kubectl get rgs <group-name>-replication -n dr-<zone-name> \
-o jsonpath='{.status.aggregated}'
To check RPO status:
kubectl get rgs <group-name>-replication -n dr-<zone-name> \
-o jsonpath='{.status.rpoStatus}'
Filter RPO events for a ProtectionGroup
Field selectors on spec.* are not supported on custom resources. Use the label selector instead.
kubectl get rpoe -n dr-<zone-name> \
-l siterecovery.trilio.io/protection-group=<group-name> \
--sort-by=.spec.timestamp
Inspect DRBD resource state on a workload cluster
DRBDResource names follow the pattern drbd-<namespace>-<vm>-<hash8>.
# List all DRBDResources in a VM namespace on the primary workload cluster
kubectl get dres -n <vm-namespace> --context <workload-cluster-context>
To check connection state and DRBD role:
kubectl get dres <drbd-resource-name> -n <vm-namespace> \
--context <workload-cluster-context> \
-o jsonpath='{.status.connectionState}{" "}{.status.role}'
To check sync progress per volume:
kubectl get dres <drbd-resource-name> -n <vm-namespace> \
--context <workload-cluster-context> \
-o jsonpath='{.status.volumes[*].syncProgress}'
Create a FailoverRequest (planned failover)
kubectl apply -f - <<EOF
apiVersion: siterecovery.trilio.io/v1alpha1
kind: FailoverRequest
metadata:
name: failover-<group-name>
namespace: dr-<zone-name>
spec:
protectionGroupRef:
name: <group-name>
targetCluster: cluster2
failoverType: planned
skipHooks: false
EOF
Watch the failover phase:
kubectl get fr failover-<group-name> -n dr-<zone-name> -w
Inspect per-VM status:
kubectl get fr failover-<group-name> -n dr-<zone-name> \
-o jsonpath='{.status.vmStatuses}'
Trigger a test failover (DR drill)
kubectl apply -f - <<EOF
apiVersion: siterecovery.trilio.io/v1alpha1
kind: TestFailover
metadata:
name: drill-<group-name>
namespace: dr-<zone-name>
spec:
protectionGroupRef:
name: <group-name>
cleanupPolicy: Automatic
skipHooks: false
EOF
Watch phase progression:
kubectl get tfo drill-<group-name> -n dr-<zone-name> -w
After the drill completes, retrieve the test namespace (controller-generated; never user-specified):
kubectl get tfo drill-<group-name> -n dr-<zone-name> \
-o jsonpath='{.status.testNamespace}'
Check verification results:
kubectl get tfo drill-<group-name> -n dr-<zone-name> \
-o jsonpath='{.status.verificationResults}'
Retrieve controller logs on the TSR-Manager cluster
The TSR-Manager control plane runs in the zone namespace dr-<zone-name>. All Site Recovery pods carry app.kubernetes.io/part-of: site-recovery.
# List TSR-Manager control-plane pods
kubectl get pods -n dr-<zone-name> \
-l app.kubernetes.io/component=manager-control-plane
# Stream logs from the manager-control-plane pod
kubectl logs -n dr-<zone-name> \
-l app.kubernetes.io/component=manager-control-plane \
--since=1h --timestamps
For the ProtectionZone controller:
kubectl logs -n dr-<zone-name> \
-l app.kubernetes.io/component=protectionzone-controller \
--since=1h --timestamps
Retrieve workload control-plane logs on a workload cluster
kubectl logs -n trilio-site-recovery-system \
-l app.kubernetes.io/component=workload-control-plane \
--context <workload-cluster-context> \
--since=1h --timestamps
Retrieve DRBD node-agent logs from a specific node
The node-agent DaemonSet pod selector uses app.kubernetes.io/component=drbd-node-agent. Because kubectl logs has no --field-selector flag, selecting by node requires two steps.
# Step 1: find the agent pod name for a specific node
AGENT_POD=$(kubectl get pods -n trilio-site-recovery-system \
--context <workload-cluster-context> \
--field-selector spec.nodeName=<node-name> \
-l app.kubernetes.io/component=drbd-node-agent \
-o jsonpath='{.items[0].metadata.name}')
# Step 2: stream logs from that pod
kubectl logs -n trilio-site-recovery-system \
--context <workload-cluster-context> \
"$AGENT_POD" --since=1h --timestamps
Adjust runtime log level without restarting pods
Site Recovery reads the site-recovery-logging ConfigMap at runtime. The global key is level; per-component overrides use level.control-plane, level.drbd-node-agent, or level.protectionzone. Accepted values: error, info, debug, trace.
# Raise TSR-Manager control-plane to debug on the TSR-Manager cluster
kubectl patch configmap site-recovery-logging -n dr-<zone-name> \
--type merge \
-p '{"data":{"level.control-plane":"debug"}}'
# Raise node-agent to debug on a workload cluster
kubectl patch configmap site-recovery-logging \
-n trilio-site-recovery-system \
--context <workload-cluster-context> \
--type merge \
-p '{"data":{"level.drbd-node-agent":"debug"}}'
Inspect ProtectionZone conditions
kubectl get pz <zone-name> -n dr-<zone-name> \
-o jsonpath='{range .status.conditions[*]}{.type}{"\t"}{.status}{"\t"}{.reason}{"\t"}{.message}{"\n"}{end}'
Key condition types and reasons to look for:
| Condition | Reason | Meaning |
|---|---|---|
ClustersValidated | InsufficientClusterPermissions | Zone credential authenticated but RBAC checks failed; rotate with tsrctl zone-credential rotate --refresh-rbac. |
ClustersValidated | ClusterAlreadyClaimed | Workload cluster is already leased to another zone. |
ClustersValidated | ClusterIdentityChanged | Credential now reaches a different cluster (UID mismatch); the zone must be re-created. |
ClustersValidated | KubeVirtNotInstalled | KubeVirt CRDs were not found on the workload cluster. |
ClustersValidated | SCCPrerequisiteMissing | Required SCC not present on the OpenShift workload cluster. |
WorkloadStacksReady | SCCAdmissionDenied | The node-agent DaemonSet was rejected by OpenShift SCC admission after install. |
Check DRBD replication metadata PVCs on a workload cluster
Metadata PVCs are named drbd-md-<resourceName>-v<volumeNumber>.
kubectl get pvc -n <vm-namespace> \
--context <workload-cluster-context> \
-l app.kubernetes.io/part-of=site-recovery
Label selectors reference
Use these labels with -l on any kubectl get command.
| Label | Value | What it selects |
|---|---|---|
app.kubernetes.io/part-of | site-recovery | All Site Recovery objects on any cluster |
app.kubernetes.io/component | manager-control-plane | TSR-Manager control-plane pods |
app.kubernetes.io/component | workload-control-plane | Workload control-plane pods |
app.kubernetes.io/component | protectionzone-controller | ProtectionZone controller pods |
app.kubernetes.io/component | drbd-node-agent | DRBD node-agent DaemonSet pods |
siterecovery.trilio.io/protection-group | <group-name> | Auto-created ReplicationGroupStatus and RPOEvent objects for a group |
siterecovery.trilio.io/protected-vm | true | VirtualMachine objects on workload clusters that are currently protected |
Field selector limitation: Custom resources support only
metadata.nameandmetadata.namespaceas field selectors. Filtering onspec.*orstatus.*fields returns an error. Always use label selectors for filtering Site Recovery custom resources.