# Restore Flags Guide

## Overview

Trilio for Kubernetes (T4K) provides a comprehensive set of restore flags that give you granular control over how applications and data are restored from backups. These flags allow you to customize restore behavior for different scenarios, including disaster recovery, cross-cluster migrations, and application updates.

This guide provides detailed information about each restore flag, including its purpose, behavior, and examples.

***

## Restore Flags <a href="#skipifalreadyexists" id="skipifalreadyexists"></a>

### skipIfAlreadyExists <a href="#skipifalreadyexists" id="skipifalreadyexists"></a>

**Purpose**: Skips restoring resources that already exist in the restore namespace.

**Behavior**:

* Checks if a resource with the same name already exists
* If exists, skips restoration and logs a warning
* Only applicable to metadata resources (Deployments, Services, ConfigMaps, Secrets, Pods, Custom Resources, etc.), not data resources (PV/PVC)
* Useful when you want to preserve existing resources

**Scenario**: Restoring to a namespace where some resources already exist.

**Existing Resources in Namespace:**

* Deployment: `web-app` (exists with 2 replicas, custom configuration)
* Service: `web-app-service` (does not exist)
* ConfigMap: `app-config` (exists with different values)

**Backup Contains:**

* Deployment: `web-app` (3 replicas, original configuration)
* Service: `web-app-service`
* ConfigMap: `app-config` (original values)

**After Restore with `skipIfAlreadyExists: true`:**

**Restored Resources:**

* ⚠️ Deployment: `web-app` - **SKIPPED** (already exists, preserved with 2 replicas)
* ✅ Service: `web-app-service` - **RESTORED** (didn't exist)
* ⚠️ ConfigMap: `app-config` - **SKIPPED** (already exists, preserved with current values)

**Result**: Existing resources are preserved, and only missing resources are restored. The existing deployment continues running with its current configuration (2 replicas), and the service is newly created.

***

### patchIfAlreadyExists

**Purpose**: Enables Trilio to patch existing metadata resources during restore using a 3-way merge, ensuring the cluster's current modifications are preserved while applying changes from the backup.

**Behavior**:

* If a resource already exists → it is patched, not recreated
* Works only for metadata resources (Deployments, Services, ConfigMaps, Secrets, Pods, CRs)
* Does not apply to data resources (PVCs/PVs/VolumeSnapshots)
* Prevents pre-restore validation failures when resources already exist

**Merge Strategy**:

* Tracks 3 states: Original (backup) ⬌ Modified (desired) ⬌ Current (cluster)
* Preserves local edits that don't conflict with backup
* Patch types: Strategic merge patch → Built-in Kubernetes resources JSON merge patch → CRDs/unregistered resources

**Scenario**: Restoring to a namespace where a deployment exists but was accidentally modified.

**Existing Deployment in Cluster:**

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 2  # Changed from original 3
  template:
    spec:
      containers:
      - name: web
        image: nginx:1.20  # Changed from nginx:1.18
        resources:
          requests:
            memory: "256Mi"  # Added after backup
```

**Backup Contains (Original):**

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3  # Original value
  template:
    spec:
      containers:
      - name: web
        image: nginx:1.18  # Original image
        # No resources section
```

**After Restore with `patchIfAlreadyExists: true`:**

**Patched Deployment:**

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3  # Restored from backup (original value)
  template:
    spec:
      containers:
      - name: web
        image: nginx:1.18  # Restored from backup (original image)
        resources:
          requests:
            memory: "256Mi"  # Preserved from current (user modification)
```

**Result**: The deployment is patched to restore the original replica count (3) and image version (nginx:1.18) from backup, while preserving the user-added memory request. The resource is not recreated, maintaining its history and relationships.

**Note**: `skipIfAlreadyExists` and `patchIfAlreadyExists` are mutually exclusive. You cannot use both flags simultaneously.

***

### retainHelmReleaseName

**Purpose**: Retains the original Helm release name during restore instead of generating a new unique name.

**Behavior**:

* By default, a new unique release name is generated to avoid conflicts
* When enabled, uses the original release name from backup
* Applies to both standalone Helm charts and Helm-based operators
* Special case: Automatically retained for local subchart dependencies

**Scenario**: Restoring a Helm-managed application.

**Backup Contains:**

* Helm Release: `my-web-app` (original name)
* Resources managed by Helm:
  * Deployment: `my-web-app-web` (name includes release name)
  * Service: `my-web-app-service`
  * PVC: `my-web-app-data` (name includes release name)

**After Restore with `retainHelmReleaseName: false` (default):**

**Restored Resources:**

* ✅ Helm Release: `my-web-app-a1b2c3` - **NEW NAME GENERATED**
* ✅ Deployment: `my-web-app-a1b2c3-web` - **RESTORED** (new name)
* ✅ Service: `my-web-app-a1b2c3-service` - **RESTORED** (new name)
* ✅ PVC: `my-web-app-a1b2c3-data` - **RESTORED** (new name)

**Result**: Application is restored with a new release name to avoid conflicts. Resource names are updated to reflect the new release name.

**After Restore with `retainHelmReleaseName: true`:**

**Restored Resources:**

* ✅ Helm Release: `my-web-app` - **ORIGINAL NAME RETAINED**
* ✅ Deployment: `my-web-app-web` - **RESTORED** (original name)
* ✅ Service: `my-web-app-service` - **RESTORED** (original name)
* ✅ PVC: `my-web-app-data` - **RESTORED** (original name)

**Result**: Application is restored with the exact same release name and resource names as the backup, maintaining complete compatibility with existing BackupPlans or dependencies that reference the original release name.

***

### restoreStorageClass

**Purpose**: Controls whether StorageClass resources are restored during the restore operation.

**Behavior**:

* When `true`: Restores StorageClass resources from backup if not present on cluster
* When `false` or `nil`: Skips StorageClass restoration
* Default behavior: `false` (StorageClasses are not restored by default)
* StorageClasses are cluster-scoped resources that define storage provisioners
* If backup storageclass is default, then that storageclass is not restored as default

**Scenario**: Restoring to a new cluster that doesn't have the custom StorageClass.

**Backup Contains:**

* StorageClass: `fast-ssd` (provisioner: kubernetes.io/aws-ebs, parameters: type=gp3)
* PVC: `app-data` (storageClassName: fast-ssd, 100Gi)

**Cluster State Before Restore:**

* ❌ StorageClass: `fast-ssd` - **NOT PRESENT**
* ❌ PVC: `app-data` - **NOT PRESENT**

**After Restore with `restoreStorageClass: true`:**

**Restored Resources:**

* ✅ StorageClass: `fast-ssd` - **RESTORED** (cluster-scoped)
* ✅ PVC: `app-data` - **RESTORED** (can now bind because StorageClass exists)

**After Restore with `restoreStorageClass: false` (default):**

**Restored Resources:**

* ❌ StorageClass: `fast-ssd` - **SKIPPED**
* ⚠️ PVC: `app-data` - **RESTORED** but may be in Pending state (StorageClass missing)

**Result**: With the flag enabled, both the StorageClass and PVC are restored, ensuring the PVC can bind properly. Without the flag, the PVC may fail to bind if the StorageClass doesn't exist in the cluster.

***

### restoreVMMACAddress

**Purpose**: Preserves Virtual Machine MAC addresses during restore instead of removing them.

**Behavior**:

* By default, MAC addresses are removed to prevent conflicts
* When enabled, original MAC addresses from backup are preserved
* Applies to VirtualMachine and VirtualMachinePool resources
* Handles multiple network interfaces

**Scenario**: Restoring a Virtual Machine with a licensed application that depends on MAC address.

**Backup Contains VirtualMachine:**

```yaml
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  name: licensed-app-vm
spec:
  template:
    spec:
      domain:
        devices:
          interfaces:
          - name: default
            macAddress: "02:00:00:00:00:01"  # Original MAC
          - name: secondary
            macAddress: "02:00:00:00:00:02"  # Original MAC
```

**After Restore with `restoreVMMACAddress: false` (default):**

**Restored VirtualMachine:**

```yaml
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  name: licensed-app-vm
spec:
  template:
    spec:
      domain:
        devices:
          interfaces:
          - name: default
            # macAddress removed - KubeVirt will auto-assign
          - name: secondary
            # macAddress removed - KubeVirt will auto-assign
```

**Result**: VM is restored but MAC addresses are removed. KubeVirt auto-assigns new MAC addresses. If the application license is tied to the original MAC address, the license may not work.

**After Restore with `restoreVMMACAddress: true`:**

**Restored VirtualMachine:**

```yaml
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  name: licensed-app-vm
spec:
  template:
    spec:
      domain:
        devices:
          interfaces:
          - name: default
            macAddress: "02:00:00:00:00:01"  # PRESERVED from backup
          - name: secondary
            macAddress: "02:00:00:00:00:02"  # PRESERVED from backup
```

**Result**: VM is restored with the exact same MAC addresses as the backup. The licensed application recognizes the MAC address and continues to work correctly.

**Note**: Cannot be used together with custom MAC address transformations.

***

### imageRestore

**Purpose**: Enables restoration of container images from backup to a target registry.

**Behavior**:

* Extracts images from qcow2 format stored in backup
* Pushes images to the specified target registry if the image with same sha doesn't exist on the original and target registry
* Updates resource specifications to use restored images
* Handles image existence checks and tag generation

**Scenario**: Restoring an application where the original registry is not accessible (air-gapped environment).

**Backup Contains:**

* Deployment with image: `quay.io/original-repo/web-app:v1.2.3` (original registry not accessible)
* Service: `web-app-service`
* ConfigMap: `app-config`

**Target Registry Configuration:**

* Registry: `docker.io`
* Repository: `my-company/restored-images`

**After Restore with `imageRestore: true`:**

**Step 1: Image Restore Phase**

* ✅ Image extracted from qcow2 backup
* ✅ Image pushed to: `docker.io/my-company/restored-images/web-app:v1.2.3`
* ✅ Image SHA verified and updated

**Step 2: Metadata Restore Phase**

* ✅ Deployment - **RESTORED** with updated image reference:
  * **Before**: `quay.io/original-repo/web-app:v1.2.3`
  * **After**: `docker.io/my-company/restored-images/web-app:v1.2.3`
* ✅ Service: `web-app-service` - **RESTORED**
* ✅ ConfigMap: `app-config` - **RESTORED**

**Result**: The application is restored with images available in the new registry. Pods can pull images from `docker.io/my-company/restored-images/web-app:v1.2.3` instead of the inaccessible original registry.

**Required Configuration**: When `imageRestore: true`, you must provide `imageRegistry` configuration with registry, repository, and authentication secret.

**overrideImageIfExist**

**Purpose**: Controls whether to override existing images in the target registry when the same image name and tag already exists.

**Behavior**:

* When `true`: Overwrites existing images with the same name and tag
* When `false`: Generates incremental tags (e.g., `tag-1`, `tag-2`) to avoid conflicts
* Works together with `imageRestore` flag
* Updates image SHA after restore

**Scenario**: Restoring an image that already exists in the target registry with a different SHA.

**Backup Contains:**

* Image: `web-app:v1.2.3` with SHA `abc123...` (from backup)

**Target Registry State:**

* Image exists: `docker.io/my-repo/web-app:v1.2.3` with SHA `xyz789...` (different content)

**After Restore with `overrideImageIfExist: false` (default):**

**Image Restore:**

* ✅ System detects existing image with different SHA
* ✅ Generates new tag: `web-app:v1.2.3-1`
* ✅ Pushes image to: `docker.io/my-repo/web-app:v1.2.3-1` with SHA `abc123...`

**Metadata Restore:**

* ✅ Deployment updated to use: `docker.io/my-repo/web-app:v1.2.3-1`

**Result**: The existing image is preserved, and a new version is created with an incremental tag. Both versions coexist in the registry.

**After Restore with `overrideImageIfExist: true`:**

**Image Restore:**

* ✅ System detects existing image
* ✅ Overwrites existing image: `docker.io/my-repo/web-app:v1.2.3`
* ✅ New SHA: `abc123...` (replaces `xyz789...`)

**Metadata Restore:**

* ✅ Deployment updated to use: `docker.io/my-repo/web-app:v1.2.3`

**Result**: The existing image is overwritten with the backup version. The old image content is replaced, and the deployment uses the restored image version.

**skipTLSVerify**

**Purpose**: Skips TLS certificate verification when pushing/pulling images to/from registries.

**Behavior**:

* Bypasses certificate chain and hostname verification
* Allows connections to registries with self-signed or untrusted certificates
* Applies to both authentication and image operations
* Configured in the ImageRegistry specification

**Scenario**: Restoring images to an internal registry with self-signed certificates.

**Backup Contains:**

* Deployment with image: `quay.io/public/web-app:v1.2.3`

**Target Registry:**

* Registry: `https://internal-registry.company.local` (self-signed certificate)
* Repository: `restored-images`

**After Restore with `skipTLSVerify: false` (default):**

**Image Restore Attempt:**

* ❌ Registry authentication fails: `x509: certificate signed by unknown authority`
* ❌ Image push fails: TLS verification error
* ❌ Restore fails with certificate errors

**Result**: Restore cannot complete because the registry's self-signed certificate is not trusted.

**After Restore with `skipTLSVerify: true`:**

**Image Restore:**

* ✅ Registry authentication succeeds (TLS verification skipped)
* ✅ Image pushed to: `https://internal-registry.company.local/restored-images/web-app:v1.2.3`
* ✅ Image SHA verified

**Metadata Restore:**

* ✅ Deployment - **RESTORED** with updated image:
  * **Before**: `quay.io/public/web-app:v1.2.3`
  * **After**: `https://internal-registry.company.local/restored-images/web-app:v1.2.3`

**Result**: Images are successfully restored to the internal registry despite self-signed certificates. The application can pull images from the internal registry.

**Security Note**: This flag should only be used in development/test environments. For production, use proper CA certificates instead of skipping verification.

***

## Action Flags

### cleanupOnFailure

**Purpose**: Automatically cleans up partially restored resources when a restore operation fails, ensuring the cluster returns to its pre-restore state.

**Behavior**:

* Triggers automatically when restore status becomes `Failed`
* Deletes newly created resources that were restored
* Reverts existing resources that were patched during restore (using last-applied-configuration annotation)
* Deletes PersistentVolumeClaims (PVCs) and PersistentVolumes (PVs) created during restore
* Deletes datamover jobs that were created for data restore
* Cleans up OLM resources for operator applications
* Can be enabled even after restore has failed (one-way flag: can be set to true, but cannot be changed back to false)
* Does not trigger for validation phase failures (RestoreValidation, RestoreTargetValidation)

**Scenario**: A restore operation fails partway through, leaving some resources created and some existing resources modified.

**Initial Cluster State:**

* Deployment: `web-app` (exists with 2 replicas, image: nginx:1.20)
* Service: `web-app-service` (does not exist)
* ConfigMap: `app-config` (exists with current values)

**Backup Contains:**

* Deployment: `web-app` (3 replicas, image: nginx:1.18)
* Service: `web-app-service`
* ConfigMap: `app-config` (backup values)
* PVC: `web-data` (100Gi)

**Restore Process (with `patchIfAlreadyExists: true`):**

1. ✅ Deployment `web-app` - **PATCHED** (replicas: 2→3, image: nginx:1.20→nginx:1.18)
2. ✅ Service `web-app-service` - **CREATED**
3. ✅ ConfigMap `app-config` - **PATCHED** (values updated)
4. ❌ PVC `web-data` - **FAILED** (storage quota exceeded)

**Restore Status**: `Failed`

**After Restore Failure with `cleanupOnFailure: false` (default):**

**Cluster State:**

* ⚠️ Deployment: `web-app` - **MODIFIED** (still has 3 replicas, nginx:1.18 from restore)
* ⚠️ Service: `web-app-service` - **CREATED** (orphaned resource)
* ⚠️ ConfigMap: `app-config` - **MODIFIED** (still has backup values)
* ❌ PVC: `web-data` - **NOT CREATED** (failed)

**Result**: Cluster is in an inconsistent state. Partially restored resources remain, and existing resources were modified but the restore didn't complete.

**After Restore Failure with `cleanupOnFailure: true`:**

**Cleanup Process:**

1. ✅ Service `web-app-service` - **DELETED** (newly created resource)
2. ✅ Deployment `web-app` - **REVERTED** (back to 2 replicas, nginx:1.20 using last-applied-config)
3. ✅ ConfigMap `app-config` - **REVERTED** (back to original values using last-applied-config)
4. ✅ Datamover jobs - **DELETED** (if any were created)

**Final Cluster State:**

* ✅ Deployment: `web-app` - **REVERTED** (back to original: 2 replicas, nginx:1.20)
* ✅ Service: `web-app-service` - **DELETED** (never existed originally)
* ✅ ConfigMap: `app-config` - **REVERTED** (back to original values)
* ✅ No PVC created (restore failed before creation)

**Result**: Cluster is returned to its exact pre-restore state. All changes made during the failed restore are undone, and newly created resources are removed.

**Important Notes**:

* The flag can be enabled at any time, even after a restore has already failed
* Once set to `true`, it cannot be changed back to `false` (prevents accidental disabling)
* Cleanup uses the `last-applied-configuration` annotation to revert patched resources to their original state
* If a resource was created during restore (not patched), it is deleted
* Data volumes (PVCs/PVs) are deleted with background propagation policy to ensure complete cleanup

***

### protectRestoredApp

**Purpose**: Automatically creates protecting resources (Target, BackupPlan, Hooks, Policies, Secrets) after a successful restore.

**Behavior**:

* Runs after successful application restore
* Recreates Target, BackupPlan, Hooks, Policies, and Target Secrets
* Handles resource conflicts intelligently (creates with new names if conflicts exist)
* Updates dependencies automatically (e.g., updates Target reference in BackupPlan)

**Scenario**: Restoring an application and automatically setting up backup protection.

**Backup Contains:**

* Application: Deployment `web-app`, Service `web-app-svc`, PVC `web-data`
* Original BackupPlan: `web-app-backup-plan` (references Target `s3-target`)
* Original Target: `s3-target` (S3 bucket configuration)
* Original Policy: `retention-policy` (30-day retention)

**After Application Restore (Before Protection Phase):**

* ✅ Deployment: `web-app` - **RESTORED**
* ✅ Service: `web-app-svc` - **RESTORED**
* ✅ PVC: `web-data` - **RESTORED**
* ❌ BackupPlan: `web-app-backup-plan` - **NOT PRESENT**
* ❌ Target: `s3-target` - **NOT PRESENT**
* ❌ Policy: `retention-policy` - **NOT PRESENT**

**After Restore with `protectRestoredApp: true` (Protection Phase Complete):**

**Restored Application:**

* ✅ Deployment: `web-app` - **RESTORED**
* ✅ Service: `web-app-svc` - **RESTORED**
* ✅ PVC: `web-data` - **RESTORED**

**Created Protecting Resources:**

* ✅ Target: `s3-target` - **CREATED** (from backup)
* ✅ Policy: `retention-policy` - **CREATED** (from backup)
* ✅ BackupPlan: `web-app-backup-plan` - **CREATED** (references restored Target and Policy)

**Result**: The application is fully restored and immediately protected. You can create a new Backup using the restored BackupPlan without manually creating Target, Policy, or BackupPlan resources.

***

### restoreNamespaceLabelsAnnotations

**Purpose**: Restores namespace labels and annotations from backup to the restored namespace during restore operations. This ensures that external systems and integrations that depend on namespace metadata continue to function correctly after restore.

**Behavior**:

* Captures namespace labels and annotations from backup
* Applies them to the restored namespace using one of three strategies: Override, Append, or Replace
* Optional feature, disabled by default
* Supports both single namespace and cluster restore operations
* For cluster restores, can be configured globally or per-namespace

For detailed information about restoration strategies, configuration examples, and use cases, see [Restore Namespace Labels and Annotations](https://github.com/trilioData/docs-public-k8s/blob/5.2.x/t4k-concepts/restore-namespace-labels-annotations/restore-namespace-labels-annotations.md).

***

## Advanced Flags

### patchCRD

**Purpose**: Patches existing CustomResourceDefinitions (CRDs) instead of skipping them.

**Behavior**:

* Detects existing CRDs with the same name
* Performs a 3-way merge patch to update CRD specifications
* Validates CRD compatibility (scope and version) before patching
* Ensures CRD changes are applied safely

**Scenario**: A CRD exists in the cluster but needs to be updated with schema changes from backup.

**Existing CRD in Cluster:**

```yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: databases.example.com
spec:
  versions:
  - name: v1
    schema:
      openAPIV3Schema:
        properties:
          spec:
            properties:
              name:
                type: string
              # Missing: replicas field
```

**Backup Contains (Updated CRD):**

```yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: databases.example.com
spec:
  versions:
  - name: v1
    schema:
      openAPIV3Schema:
        properties:
          spec:
            properties:
              name:
                type: string
              replicas:  # New field added
                type: integer
```

**After Restore with `patchCRD: true`:**

**Patched CRD:**

```yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: databases.example.com
spec:
  versions:
  - name: v1
    schema:
      openAPIV3Schema:
        properties:
          spec:
            properties:
              name:
                type: string
              replicas:  # Added from backup
                type: integer
```

**Result**: The CRD is patched to include the new `replicas` field from the backup, allowing Custom Resources to use this new field. Existing Custom Resources continue to work, and new ones can use the updated schema.

**Note**: `patchCRD` and `skipOperatorResources` are mutually exclusive. You cannot use both flags simultaneously.

***

### onlyMetadata

**Purpose**: Restores only the metadata components of an application, skipping data volumes.

**Behavior**:

* Restores Kubernetes resources like Deployments, Services, ConfigMaps, Secrets, Pods, Custom Resources, etc.
* Skips PersistentVolumeClaims (PVCs), PersistentVolumes (PVs), and VolumeSnapshots
* Useful when you want to restore application configuration without restoring data
* StorageClass: Can be restored if restoreStorageClass: true is set

**Backup Contains:**

* Deployment: `web-app` (3 replicas)
* Service: `web-app-service`
* ConfigMap: `app-config`
* Secret: `app-secret`
* PVC: `web-app-data` (10Gi)

**After Restore with `onlyMetadata: true`:**

**Restored Resources:**

* ✅ Deployment: `web-app` (3 replicas) - **RESTORED**
* ✅ Service: `web-app-service` - **RESTORED**
* ✅ ConfigMap: `app-config` - **RESTORED**
* ✅ Secret: `app-secret` - **RESTORED**
* ❌ PVC: `web-app-data` - **SKIPPED** (data resource)

**Result**: Application structure is restored, but pods will be in Pending state because the PVC is missing. You can manually create the PVC or restore it separately.

***

### onlyData

**Purpose**: Restores only the data volume components, skipping metadata resources.

**Behavior**:

* Restores PersistentVolumeClaims (PVCs), PersistentVolumes (PVs), and VolumeSnapshots
* Skips all metadata resources like Deployments, Services, ConfigMaps, etc.
* Useful when you want to restore data volumes without recreating the application structure
* StorageClass: Can be restored if restoreStorageClass: true is set
* Operator resources: When onlyData: true, operator resources and custom resources are skipped

**Backup Contains:**

* Deployment: `database-app` (2 replicas)
* Service: `database-service`
* ConfigMap: `db-config`
* PVC: `database-data` (50Gi) with data
* PVC: `database-logs` (20Gi) with data

**After Restore with `onlyData: true`:**

**Restored Resources:**

* ❌ Deployment: `database-app` - **SKIPPED** (metadata resource)
* ❌ Service: `database-service` - **SKIPPED** (metadata resource)
* ❌ ConfigMap: `db-config` - **SKIPPED** (metadata resource)
* ✅ PVC: `database-data` (50Gi) - **RESTORED** with data
* ✅ PVC: `database-logs` (20Gi) - **RESTORED** with data

**Result**: Data volumes are restored and available, but the application is not running. You need to deploy the application separately to use the restored data.

***

### omitMetadata

**Purpose**: Removes labels and annotations from resources during restore.

**Behavior**:

* Strips all labels and annotations from restored resources
* Preserves the resource name
* The flag is skipped for resources that are part of a Helm release, because Helm needs the metadata to manage those resources

**Backup Contains Deployment with:**

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  labels:
    app: web-app
    env: production
    backup-id: backup-123
  annotations:
    deployment.kubernetes.io/revision: "2"
    backup-timestamp: "2024-01-15T10:30:00Z"
spec:
  replicas: 3
  # ... rest of spec
```

**After Restore with `omitMetadata: true`:**

**Restored Deployment:**

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  # No labels
  # No annotations
spec:
  replicas: 3
  # ... rest of spec (unchanged)
```

**Result**: The deployment is restored with a clean metadata section, without any labels or annotations from the backup. The resource name and spec remain intact.

***

### skipOperatorResources

**Purpose**: The skipOperatorResources flag skips restoring operator infrastructure (Deployments, Services, CRDs, etc.) and only restores Custom Resources (CRs) and their managed resources. Use this when the operator is already installed and you only need to restore the application.

**Behavior**:

* Skips operator Deployments, Services, ConfigMaps, and other infrastructure components
* Restores only Custom Resources (CRs) that the operator manages
* Assumes the operator is already installed in the target cluster
* Also skips operator-related data volumes if present

**Scenario**: MySQL Operator is already installed in the cluster, but the MySQL database instance needs to be restored.

**Backup Contains:**

* Operator Deployment: `mysql-operator` (infrastructure)
* Operator Service: `mysql-operator-service` (infrastructure)
* CRD: `mysqlclusters.mysql.oracle.com` (infrastructure)
* Custom Resource: `MySQLCluster` named `production-db` (application)
* PVC: `mysql-data` (50Gi) (data)

**Cluster State Before Restore:**

* ✅ Operator Deployment: `mysql-operator` - **ALREADY EXISTS**
* ✅ Operator Service: `mysql-operator-service` - **ALREADY EXISTS**
* ✅ CRD: `mysqlclusters.mysql.oracle.com` - **ALREADY EXISTS**
* ❌ Custom Resource: `production-db` - **NOT PRESENT**
* ❌ PVC: `mysql-data` - **NOT PRESENT**

**After Restore with `skipOperatorResources: true`:**

**Restored Resources:**

* ⏭️ Operator Deployment: `mysql-operator` - **SKIPPED** (operator infrastructure)
* ⏭️ Operator Service: `mysql-operator-service` - **SKIPPED** (operator infrastructure)
* ⏭️ CRD: `mysqlclusters.mysql.oracle.com` - **SKIPPED** (operator infrastructure)
* ✅ Custom Resource: `MySQLCluster` named `production-db` - **RESTORED** (application)
* ✅ PVC: `mysql-data` (50Gi) - **RESTORED** (data)

**Result**: Only the MySQL database Custom Resource and its data are restored. The existing operator detects the restored CR and creates the necessary StatefulSet, Services, and Pods to run the database instance.

***

### disableIgnoreResources

**Purpose**: Disables the default ignore list, allowing normally ignored resources to be restored.

**Behavior**:

* By default, certain resources are ignored (Namespace, APIService, OpenShift Console resources)
* When enabled, these resources are restored
* Applies to both validation and restore phases
* Useful for advanced restore scenarios

**Scenario**: Restoring a backup that includes Namespace resources.

**Backup Contains:**

* Namespace: `production` (normally ignored)
* Deployment: `web-app` in namespace `production`
* Service: `web-app-svc` in namespace `production`
* ConfigMap: `app-config` in namespace `production`

**After Restore with `disableIgnoreResources: false` (default):**

**Restored Resources:**

* ❌ Namespace: `production` - **IGNORED** (default behavior)
* ⚠️ Deployment: `web-app` - **RESTORED** (but namespace must exist)
* ⚠️ Service: `web-app-svc` - **RESTORED** (but namespace must exist)
* ⚠️ ConfigMap: `app-config` - **RESTORED** (but namespace must exist)

**Result**: Resources are restored, but you must manually create the namespace first, or restore fails if the namespace doesn't exist.

**After Restore with `disableIgnoreResources: true`:**

**Restored Resources:**

* ✅ Namespace: `production` - **RESTORED** (normally ignored, now restored)
* ✅ Deployment: `web-app` - **RESTORED** in namespace `production`
* ✅ Service: `web-app-svc` - **RESTORED** in namespace `production`
* ✅ ConfigMap: `app-config` - **RESTORED** in namespace `production`

**Result**: The namespace is restored along with all resources, creating a complete restore including the namespace itself.

***

### useOCPNamespaceUIDRange

**Purpose**: This ensures that the restored data on OpenShift uses the UID range defined by the target namespace’s SCC (Security Context Constraint) instead of the original UIDs stored in the backup.

**Behavior**:

* OpenShift-specific flag (only works on OpenShift clusters)
* Creates helper PVCs in install namespace during primitive restore
* Uses namespace SCC UID range annotation for file ownership
* Ensures data ownership complies with OpenShift security policies

**Scenario**: Restoring data to an OpenShift namespace with a different UID range than the source.

**Backup Contains:**

* PVC: `app-data` (100Gi) with files owned by UID 1000 (from source namespace)

**Source Namespace (Backup):**

* UID Range: `1000/10000` (annotation: `openshift.io/sa.scc.uid-range: 1000/10000`)
* Files in PVC owned by: UID 1000

**Target Namespace (Restore):**

* UID Range: `2000/10000` (annotation: `openshift.io/sa.scc.uid-range: 2000/10000`)
* SCC requires files to be owned by UID range 2000-11999

**After Restore with `useOCPNamespaceUIDRange: false` (default):**

**Restored PVC:**

* ✅ PVC: `app-data` - **RESTORED**
* ⚠️ File ownership: UID 1000 (original from backup)
* ❌ **Problem**: Pods in target namespace cannot access files (UID mismatch with SCC)

**After Restore with `useOCPNamespaceUIDRange: true`:**

**Restored PVC:**

* ✅ Helper PVC created in install namespace
* ✅ DataOwnerUpdate job updates file ownership to UID 2000 (target namespace range)
* ✅ PVC: `app-data` - **RESTORED** with files owned by UID 2000
* ✅ **Result**: Pods can access files (UID matches SCC requirements)

**Result**: Data is restored with file ownership that complies with the target namespace's SCC UID range, ensuring pods can access the data without permission errors.

***

### resourcesReadyWaitSeconds

**Purpose**: Configures the wait time (in seconds) for restored application resources to become ready before proceeding.

**Behavior**:

* Default: 600 seconds (10 minutes)
* Minimum: 0 seconds
* Maximum: 1200 seconds (20 minutes)
* Waits for Deployments, StatefulSets, Pods, etc. to become ready
* Checks replica availability every 5 seconds

**Scenario**: Restoring an operator application where the operator needs time to start before Custom Resources can be created.

**Backup Contains:**

* Operator Deployment: `mysql-operator` (needs to start and register webhooks)
* Custom Resource: `MySQLCluster` named `production-db`

**Restore Process with `resourcesReadyWaitSeconds: 300` (5 minutes):**

**Step 1: Operator Resources Restored**

* ✅ Deployment: `mysql-operator` - **RESTORED** (but pods not ready yet)

**Step 2: Wait Phase (5 minutes)**

* ⏳ System waits for `mysql-operator` deployment pods to become ready
* ⏳ Checks every 5 seconds: Are all replicas available?
* ⏳ After 2 minutes: Pods are ready, webhooks are registered

**Step 3: Custom Resource Restored**

* ✅ Custom Resource: `MySQLCluster` named `production-db` - **RESTORED**
* ✅ Webhook validation succeeds (operator is ready)
* ✅ Operator creates StatefulSet and manages the database

**Result**: The system waits for the operator to be ready before attempting to restore Custom Resources, preventing webhook validation failures.

**If `resourcesReadyWaitSeconds: 0` or too short:**

* ⚠️ Custom Resource restore may fail with webhook errors
* ⚠️ Operator webhooks not ready to validate CR creation

***

### Cleanup Configuration (In-Place Restore)

#### cleanupConfig

**Purpose**: Enables In-Place Restore by performing a complete cleanup of existing application resources before restoring them from backup. This ensures the application is restored to an exact copy of the backup state.

**Behavior**:

* When enabled, all existing application resources (metadata and data) are deleted before restoration begins
* Trilio automatically determines the cleanup strategy (namespace-level or application-level) based on the backup type
* Resources are first deleted gracefully; if deletion gets stuck, Trilio automatically falls back to force deletion by removing finalizers and owner references
* Critical shared resources (CRDs, StorageClasses, OLM infrastructure) are automatically excluded from cleanup
* All cleanup actions are recorded in the Restore CR status for full visibility

**Configuration Fields**:

| Field                            | Type    | Required | Description                                                                                                                  |
| -------------------------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `enabled`                        | boolean | Yes      | Set to `true` to enable cleanup before restore                                                                               |
| `gracefulDeletionTimeoutSeconds` | integer | No       | Time in seconds to wait for graceful deletion before falling back to force deletion. Range: 60–1800. Default: **60 seconds** |

**Scenario**: Restoring a namespace to its exact backup state after the application has drifted or become corrupted.

**Current Cluster State:**

* Deployment: `web-app` (5 replicas — scaled up since backup)
* Service: `web-app-service` (modified ports)
* ConfigMap: `app-config` (updated with new values)
* Secret: `db-credentials` (rotated since backup)
* PVC: `web-data` (contains current data)
* Deployment: `monitoring-sidecar` (newly added — not in backup)

**Backup Contains (Original State):**

* Deployment: `web-app` (3 replicas)
* Service: `web-app-service` (original ports)
* ConfigMap: `app-config` (original values)
* Secret: `db-credentials` (original credentials)
* PVC: `web-data` (backup data)

**After Restore with `cleanupConfig.enabled: true`:**

**Cleanup Phase** (only resources present in backup are cleaned up):

* ✅ Deployment: `web-app` — **DELETED** (will be recreated from backup)
* ✅ Service: `web-app-service` — **DELETED** (will be recreated)
* ✅ ConfigMap: `app-config` — **DELETED and RESTORED** (atomic infrastructure cleanup)
* ✅ Secret: `db-credentials` — **DELETED and RESTORED** (atomic infrastructure cleanup)
* ✅ PVC: `web-data` — **DELETED** (will be recreated with backup data)
* ⏭️ Deployment: `monitoring-sidecar` — **NOT TOUCHED** (not in backup)

**Restore Phase:**

* ✅ Deployment: `web-app` (3 replicas) — **RESTORED** from backup
* ✅ Service: `web-app-service` (original ports) — **RESTORED** from backup
* ✅ PVC: `web-data` — **RESTORED** with backup data

**Final Cluster State:**

* ✅ All backed-up resources match the exact backup state
* ✅ `monitoring-sidecar` deployment remains untouched (was not in backup)
* ✅ All resource configurations match backup values

**Result**: All resources from the backup are reset to their exact backed-up state. Resources that were added after the backup (like `monitoring-sidecar`) remain untouched — only resources present in the backup are cleaned up and restored.

**YAML Example:**

```yaml
apiVersion: triliovault.trilio.io/v1
kind: Restore
metadata:
  name: inplace-restore-example
  namespace: my-app
spec:
  source:
    type: Backup
    backup:
      name: my-app-backup-20250115
      namespace: my-app
  cleanupConfig:
    enabled: true
    gracefulDeletionTimeoutSeconds: 120
```

{% hint style="warning" %}
**Important**: In-Place Restore causes application downtime during the cleanup phase. All existing application resources are deleted before being recreated from backup. Plan accordingly.
{% endhint %}

{% hint style="info" %}
**Mutual Exclusivity**: The `cleanupConfig` option cannot be used together with resource exclusion selectors (`excludeResources`) in the same restore. When cleanup is enabled, the entire backup scope is restored — selective resource exclusion is not supported.
{% endhint %}

{% hint style="info" %}
**Automatic Helm Behavior**: When cleanup is enabled, the `retainHelmReleaseName` flag is automatically set to `true` by the restore admission webhook. You do not need to set this manually.
{% endhint %}

For complete details on how In-Place Restore works, including supported application types, resource handling, and important constraints, see [In-Place Restore](/kubernetes/user-guide/getting-started-with-management-console/index/restoring-backups/in-place-restore.md).

## Best Practices

1. **Use `skipIfAlreadyExists` or `patchIfAlreadyExists`** when restoring to namespaces with existing resources
2. **Enable `protectRestoredApp`** for production restores to ensure immediate backup capability
3. **Enable `cleanupOnFailure`** for production restores to automatically clean up on failure
4. **Set appropriate `resourcesReadyWaitSeconds`** for applications with webhooks or slow startup times
5. **Use `onlyMetadata` or `onlyData`** for granular control over what gets restored
6. **Enable `restoreStorageClass`** when migrating between clusters with different storage configurations
7. **Use `skipOperatorResources`** when operators are already installed in the target cluster
8. **Avoid `skipTLSVerify` in production** - use proper CA certificates instead
9. **Use `restoreNamespaceLabelsAnnotations`** when restoring applications that depend on namespace metadata (service mesh, policies, etc.)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.trilio.io/kubernetes/user-guide/getting-started-with-management-console/index/restoring-backups/restore-flags-guide.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
