Configure GCP GKE Backup Restore Plans

The gcp:gkebackup/restorePlan:RestorePlan resource, part of the Pulumi GCP provider, defines how GKE Backup for GKE restores cluster state from backups: which namespaces or applications to restore, how to handle conflicts, and what transformations to apply. This guide focuses on four capabilities: namespace and application-level restore scoping, conflict resolution modes, transformation rules for namespace renaming, and dependency-based restore ordering.

Restore plans reference existing GKE clusters and BackupPlans. Clusters must have the GKE Backup Agent addon enabled. The examples are intentionally small. Combine them with your own backup strategies and cluster configurations.

Restore all namespaces with conflict handling

Teams recovering from cluster failures often need to restore everything at once, bringing back all namespaces and their resources in a single operation.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const primary = new gcp.container.Cluster("primary", {
    name: "restore-all-ns-cluster",
    location: "us-central1",
    initialNodeCount: 1,
    workloadIdentityConfig: {
        workloadPool: "my-project-name.svc.id.goog",
    },
    addonsConfig: {
        gkeBackupAgentConfig: {
            enabled: true,
        },
    },
    deletionProtection: true,
    network: "default",
    subnetwork: "default",
});
const basic = new gcp.gkebackup.BackupPlan("basic", {
    name: "restore-all-ns",
    cluster: primary.id,
    location: "us-central1",
    backupConfig: {
        includeVolumeData: true,
        includeSecrets: true,
        allNamespaces: true,
    },
});
const allNs = new gcp.gkebackup.RestorePlan("all_ns", {
    name: "restore-all-ns",
    location: "us-central1",
    backupPlan: basic.id,
    cluster: primary.id,
    restoreConfig: {
        allNamespaces: true,
        namespacedResourceRestoreMode: "FAIL_ON_CONFLICT",
        volumeDataRestorePolicy: "RESTORE_VOLUME_DATA_FROM_BACKUP",
        clusterResourceRestoreScope: {
            allGroupKinds: true,
        },
        clusterResourceConflictPolicy: "USE_EXISTING_VERSION",
    },
});
import pulumi
import pulumi_gcp as gcp

primary = gcp.container.Cluster("primary",
    name="restore-all-ns-cluster",
    location="us-central1",
    initial_node_count=1,
    workload_identity_config={
        "workload_pool": "my-project-name.svc.id.goog",
    },
    addons_config={
        "gke_backup_agent_config": {
            "enabled": True,
        },
    },
    deletion_protection=True,
    network="default",
    subnetwork="default")
basic = gcp.gkebackup.BackupPlan("basic",
    name="restore-all-ns",
    cluster=primary.id,
    location="us-central1",
    backup_config={
        "include_volume_data": True,
        "include_secrets": True,
        "all_namespaces": True,
    })
all_ns = gcp.gkebackup.RestorePlan("all_ns",
    name="restore-all-ns",
    location="us-central1",
    backup_plan=basic.id,
    cluster=primary.id,
    restore_config={
        "all_namespaces": True,
        "namespaced_resource_restore_mode": "FAIL_ON_CONFLICT",
        "volume_data_restore_policy": "RESTORE_VOLUME_DATA_FROM_BACKUP",
        "cluster_resource_restore_scope": {
            "all_group_kinds": True,
        },
        "cluster_resource_conflict_policy": "USE_EXISTING_VERSION",
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/container"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/gkebackup"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		primary, err := container.NewCluster(ctx, "primary", &container.ClusterArgs{
			Name:             pulumi.String("restore-all-ns-cluster"),
			Location:         pulumi.String("us-central1"),
			InitialNodeCount: pulumi.Int(1),
			WorkloadIdentityConfig: &container.ClusterWorkloadIdentityConfigArgs{
				WorkloadPool: pulumi.String("my-project-name.svc.id.goog"),
			},
			AddonsConfig: &container.ClusterAddonsConfigArgs{
				GkeBackupAgentConfig: &container.ClusterAddonsConfigGkeBackupAgentConfigArgs{
					Enabled: pulumi.Bool(true),
				},
			},
			DeletionProtection: pulumi.Bool(true),
			Network:            pulumi.String("default"),
			Subnetwork:         pulumi.String("default"),
		})
		if err != nil {
			return err
		}
		basic, err := gkebackup.NewBackupPlan(ctx, "basic", &gkebackup.BackupPlanArgs{
			Name:     pulumi.String("restore-all-ns"),
			Cluster:  primary.ID(),
			Location: pulumi.String("us-central1"),
			BackupConfig: &gkebackup.BackupPlanBackupConfigArgs{
				IncludeVolumeData: pulumi.Bool(true),
				IncludeSecrets:    pulumi.Bool(true),
				AllNamespaces:     pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		_, err = gkebackup.NewRestorePlan(ctx, "all_ns", &gkebackup.RestorePlanArgs{
			Name:       pulumi.String("restore-all-ns"),
			Location:   pulumi.String("us-central1"),
			BackupPlan: basic.ID(),
			Cluster:    primary.ID(),
			RestoreConfig: &gkebackup.RestorePlanRestoreConfigArgs{
				AllNamespaces:                 pulumi.Bool(true),
				NamespacedResourceRestoreMode: pulumi.String("FAIL_ON_CONFLICT"),
				VolumeDataRestorePolicy:       pulumi.String("RESTORE_VOLUME_DATA_FROM_BACKUP"),
				ClusterResourceRestoreScope: &gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs{
					AllGroupKinds: pulumi.Bool(true),
				},
				ClusterResourceConflictPolicy: pulumi.String("USE_EXISTING_VERSION"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var primary = new Gcp.Container.Cluster("primary", new()
    {
        Name = "restore-all-ns-cluster",
        Location = "us-central1",
        InitialNodeCount = 1,
        WorkloadIdentityConfig = new Gcp.Container.Inputs.ClusterWorkloadIdentityConfigArgs
        {
            WorkloadPool = "my-project-name.svc.id.goog",
        },
        AddonsConfig = new Gcp.Container.Inputs.ClusterAddonsConfigArgs
        {
            GkeBackupAgentConfig = new Gcp.Container.Inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs
            {
                Enabled = true,
            },
        },
        DeletionProtection = true,
        Network = "default",
        Subnetwork = "default",
    });

    var basic = new Gcp.GkeBackup.BackupPlan("basic", new()
    {
        Name = "restore-all-ns",
        Cluster = primary.Id,
        Location = "us-central1",
        BackupConfig = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigArgs
        {
            IncludeVolumeData = true,
            IncludeSecrets = true,
            AllNamespaces = true,
        },
    });

    var allNs = new Gcp.GkeBackup.RestorePlan("all_ns", new()
    {
        Name = "restore-all-ns",
        Location = "us-central1",
        BackupPlan = basic.Id,
        Cluster = primary.Id,
        RestoreConfig = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigArgs
        {
            AllNamespaces = true,
            NamespacedResourceRestoreMode = "FAIL_ON_CONFLICT",
            VolumeDataRestorePolicy = "RESTORE_VOLUME_DATA_FROM_BACKUP",
            ClusterResourceRestoreScope = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs
            {
                AllGroupKinds = true,
            },
            ClusterResourceConflictPolicy = "USE_EXISTING_VERSION",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.container.Cluster;
import com.pulumi.gcp.container.ClusterArgs;
import com.pulumi.gcp.container.inputs.ClusterWorkloadIdentityConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs;
import com.pulumi.gcp.gkebackup.BackupPlan;
import com.pulumi.gcp.gkebackup.BackupPlanArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupConfigArgs;
import com.pulumi.gcp.gkebackup.RestorePlan;
import com.pulumi.gcp.gkebackup.RestorePlanArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var primary = new Cluster("primary", ClusterArgs.builder()
            .name("restore-all-ns-cluster")
            .location("us-central1")
            .initialNodeCount(1)
            .workloadIdentityConfig(ClusterWorkloadIdentityConfigArgs.builder()
                .workloadPool("my-project-name.svc.id.goog")
                .build())
            .addonsConfig(ClusterAddonsConfigArgs.builder()
                .gkeBackupAgentConfig(ClusterAddonsConfigGkeBackupAgentConfigArgs.builder()
                    .enabled(true)
                    .build())
                .build())
            .deletionProtection(true)
            .network("default")
            .subnetwork("default")
            .build());

        var basic = new BackupPlan("basic", BackupPlanArgs.builder()
            .name("restore-all-ns")
            .cluster(primary.id())
            .location("us-central1")
            .backupConfig(BackupPlanBackupConfigArgs.builder()
                .includeVolumeData(true)
                .includeSecrets(true)
                .allNamespaces(true)
                .build())
            .build());

        var allNs = new RestorePlan("allNs", RestorePlanArgs.builder()
            .name("restore-all-ns")
            .location("us-central1")
            .backupPlan(basic.id())
            .cluster(primary.id())
            .restoreConfig(RestorePlanRestoreConfigArgs.builder()
                .allNamespaces(true)
                .namespacedResourceRestoreMode("FAIL_ON_CONFLICT")
                .volumeDataRestorePolicy("RESTORE_VOLUME_DATA_FROM_BACKUP")
                .clusterResourceRestoreScope(RestorePlanRestoreConfigClusterResourceRestoreScopeArgs.builder()
                    .allGroupKinds(true)
                    .build())
                .clusterResourceConflictPolicy("USE_EXISTING_VERSION")
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:container:Cluster
    properties:
      name: restore-all-ns-cluster
      location: us-central1
      initialNodeCount: 1
      workloadIdentityConfig:
        workloadPool: my-project-name.svc.id.goog
      addonsConfig:
        gkeBackupAgentConfig:
          enabled: true
      deletionProtection: true
      network: default
      subnetwork: default
  basic:
    type: gcp:gkebackup:BackupPlan
    properties:
      name: restore-all-ns
      cluster: ${primary.id}
      location: us-central1
      backupConfig:
        includeVolumeData: true
        includeSecrets: true
        allNamespaces: true
  allNs:
    type: gcp:gkebackup:RestorePlan
    name: all_ns
    properties:
      name: restore-all-ns
      location: us-central1
      backupPlan: ${basic.id}
      cluster: ${primary.id}
      restoreConfig:
        allNamespaces: true
        namespacedResourceRestoreMode: FAIL_ON_CONFLICT
        volumeDataRestorePolicy: RESTORE_VOLUME_DATA_FROM_BACKUP
        clusterResourceRestoreScope:
          allGroupKinds: true
        clusterResourceConflictPolicy: USE_EXISTING_VERSION

When allNamespaces is true, the restore operation processes every namespace in the backup. The namespacedResourceRestoreMode controls what happens when restored resources conflict with existing ones; FAIL_ON_CONFLICT stops the restore if any resource already exists. The volumeDataRestorePolicy determines whether to restore persistent volume data from the backup. The clusterResourceRestoreScope with allGroupKinds set to true restores all cluster-scoped resources like CustomResourceDefinitions and StorageClasses, while clusterResourceConflictPolicy uses existing versions when conflicts occur.

Roll back specific namespaces with deletion

When a deployment goes wrong in production, teams need to roll back specific namespaces to a known-good state without affecting the rest of the cluster.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const primary = new gcp.container.Cluster("primary", {
    name: "rollback-ns-cluster",
    location: "us-central1",
    initialNodeCount: 1,
    workloadIdentityConfig: {
        workloadPool: "my-project-name.svc.id.goog",
    },
    addonsConfig: {
        gkeBackupAgentConfig: {
            enabled: true,
        },
    },
    deletionProtection: true,
    network: "default",
    subnetwork: "default",
});
const basic = new gcp.gkebackup.BackupPlan("basic", {
    name: "rollback-ns",
    cluster: primary.id,
    location: "us-central1",
    backupConfig: {
        includeVolumeData: true,
        includeSecrets: true,
        allNamespaces: true,
    },
});
const rollbackNs = new gcp.gkebackup.RestorePlan("rollback_ns", {
    name: "rollback-ns-rp",
    location: "us-central1",
    backupPlan: basic.id,
    cluster: primary.id,
    restoreConfig: {
        selectedNamespaces: {
            namespaces: ["my-ns"],
        },
        namespacedResourceRestoreMode: "DELETE_AND_RESTORE",
        volumeDataRestorePolicy: "RESTORE_VOLUME_DATA_FROM_BACKUP",
        clusterResourceRestoreScope: {
            selectedGroupKinds: [
                {
                    resourceGroup: "apiextension.k8s.io",
                    resourceKind: "CustomResourceDefinition",
                },
                {
                    resourceGroup: "storage.k8s.io",
                    resourceKind: "StorageClass",
                },
            ],
        },
        clusterResourceConflictPolicy: "USE_EXISTING_VERSION",
    },
});
import pulumi
import pulumi_gcp as gcp

primary = gcp.container.Cluster("primary",
    name="rollback-ns-cluster",
    location="us-central1",
    initial_node_count=1,
    workload_identity_config={
        "workload_pool": "my-project-name.svc.id.goog",
    },
    addons_config={
        "gke_backup_agent_config": {
            "enabled": True,
        },
    },
    deletion_protection=True,
    network="default",
    subnetwork="default")
basic = gcp.gkebackup.BackupPlan("basic",
    name="rollback-ns",
    cluster=primary.id,
    location="us-central1",
    backup_config={
        "include_volume_data": True,
        "include_secrets": True,
        "all_namespaces": True,
    })
rollback_ns = gcp.gkebackup.RestorePlan("rollback_ns",
    name="rollback-ns-rp",
    location="us-central1",
    backup_plan=basic.id,
    cluster=primary.id,
    restore_config={
        "selected_namespaces": {
            "namespaces": ["my-ns"],
        },
        "namespaced_resource_restore_mode": "DELETE_AND_RESTORE",
        "volume_data_restore_policy": "RESTORE_VOLUME_DATA_FROM_BACKUP",
        "cluster_resource_restore_scope": {
            "selected_group_kinds": [
                {
                    "resource_group": "apiextension.k8s.io",
                    "resource_kind": "CustomResourceDefinition",
                },
                {
                    "resource_group": "storage.k8s.io",
                    "resource_kind": "StorageClass",
                },
            ],
        },
        "cluster_resource_conflict_policy": "USE_EXISTING_VERSION",
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/container"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/gkebackup"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		primary, err := container.NewCluster(ctx, "primary", &container.ClusterArgs{
			Name:             pulumi.String("rollback-ns-cluster"),
			Location:         pulumi.String("us-central1"),
			InitialNodeCount: pulumi.Int(1),
			WorkloadIdentityConfig: &container.ClusterWorkloadIdentityConfigArgs{
				WorkloadPool: pulumi.String("my-project-name.svc.id.goog"),
			},
			AddonsConfig: &container.ClusterAddonsConfigArgs{
				GkeBackupAgentConfig: &container.ClusterAddonsConfigGkeBackupAgentConfigArgs{
					Enabled: pulumi.Bool(true),
				},
			},
			DeletionProtection: pulumi.Bool(true),
			Network:            pulumi.String("default"),
			Subnetwork:         pulumi.String("default"),
		})
		if err != nil {
			return err
		}
		basic, err := gkebackup.NewBackupPlan(ctx, "basic", &gkebackup.BackupPlanArgs{
			Name:     pulumi.String("rollback-ns"),
			Cluster:  primary.ID(),
			Location: pulumi.String("us-central1"),
			BackupConfig: &gkebackup.BackupPlanBackupConfigArgs{
				IncludeVolumeData: pulumi.Bool(true),
				IncludeSecrets:    pulumi.Bool(true),
				AllNamespaces:     pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		_, err = gkebackup.NewRestorePlan(ctx, "rollback_ns", &gkebackup.RestorePlanArgs{
			Name:       pulumi.String("rollback-ns-rp"),
			Location:   pulumi.String("us-central1"),
			BackupPlan: basic.ID(),
			Cluster:    primary.ID(),
			RestoreConfig: &gkebackup.RestorePlanRestoreConfigArgs{
				SelectedNamespaces: &gkebackup.RestorePlanRestoreConfigSelectedNamespacesArgs{
					Namespaces: pulumi.StringArray{
						pulumi.String("my-ns"),
					},
				},
				NamespacedResourceRestoreMode: pulumi.String("DELETE_AND_RESTORE"),
				VolumeDataRestorePolicy:       pulumi.String("RESTORE_VOLUME_DATA_FROM_BACKUP"),
				ClusterResourceRestoreScope: &gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs{
					SelectedGroupKinds: gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeSelectedGroupKindArray{
						&gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeSelectedGroupKindArgs{
							ResourceGroup: pulumi.String("apiextension.k8s.io"),
							ResourceKind:  pulumi.String("CustomResourceDefinition"),
						},
						&gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeSelectedGroupKindArgs{
							ResourceGroup: pulumi.String("storage.k8s.io"),
							ResourceKind:  pulumi.String("StorageClass"),
						},
					},
				},
				ClusterResourceConflictPolicy: pulumi.String("USE_EXISTING_VERSION"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var primary = new Gcp.Container.Cluster("primary", new()
    {
        Name = "rollback-ns-cluster",
        Location = "us-central1",
        InitialNodeCount = 1,
        WorkloadIdentityConfig = new Gcp.Container.Inputs.ClusterWorkloadIdentityConfigArgs
        {
            WorkloadPool = "my-project-name.svc.id.goog",
        },
        AddonsConfig = new Gcp.Container.Inputs.ClusterAddonsConfigArgs
        {
            GkeBackupAgentConfig = new Gcp.Container.Inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs
            {
                Enabled = true,
            },
        },
        DeletionProtection = true,
        Network = "default",
        Subnetwork = "default",
    });

    var basic = new Gcp.GkeBackup.BackupPlan("basic", new()
    {
        Name = "rollback-ns",
        Cluster = primary.Id,
        Location = "us-central1",
        BackupConfig = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigArgs
        {
            IncludeVolumeData = true,
            IncludeSecrets = true,
            AllNamespaces = true,
        },
    });

    var rollbackNs = new Gcp.GkeBackup.RestorePlan("rollback_ns", new()
    {
        Name = "rollback-ns-rp",
        Location = "us-central1",
        BackupPlan = basic.Id,
        Cluster = primary.Id,
        RestoreConfig = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigArgs
        {
            SelectedNamespaces = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigSelectedNamespacesArgs
            {
                Namespaces = new[]
                {
                    "my-ns",
                },
            },
            NamespacedResourceRestoreMode = "DELETE_AND_RESTORE",
            VolumeDataRestorePolicy = "RESTORE_VOLUME_DATA_FROM_BACKUP",
            ClusterResourceRestoreScope = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs
            {
                SelectedGroupKinds = new[]
                {
                    new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeSelectedGroupKindArgs
                    {
                        ResourceGroup = "apiextension.k8s.io",
                        ResourceKind = "CustomResourceDefinition",
                    },
                    new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeSelectedGroupKindArgs
                    {
                        ResourceGroup = "storage.k8s.io",
                        ResourceKind = "StorageClass",
                    },
                },
            },
            ClusterResourceConflictPolicy = "USE_EXISTING_VERSION",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.container.Cluster;
import com.pulumi.gcp.container.ClusterArgs;
import com.pulumi.gcp.container.inputs.ClusterWorkloadIdentityConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs;
import com.pulumi.gcp.gkebackup.BackupPlan;
import com.pulumi.gcp.gkebackup.BackupPlanArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupConfigArgs;
import com.pulumi.gcp.gkebackup.RestorePlan;
import com.pulumi.gcp.gkebackup.RestorePlanArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigSelectedNamespacesArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var primary = new Cluster("primary", ClusterArgs.builder()
            .name("rollback-ns-cluster")
            .location("us-central1")
            .initialNodeCount(1)
            .workloadIdentityConfig(ClusterWorkloadIdentityConfigArgs.builder()
                .workloadPool("my-project-name.svc.id.goog")
                .build())
            .addonsConfig(ClusterAddonsConfigArgs.builder()
                .gkeBackupAgentConfig(ClusterAddonsConfigGkeBackupAgentConfigArgs.builder()
                    .enabled(true)
                    .build())
                .build())
            .deletionProtection(true)
            .network("default")
            .subnetwork("default")
            .build());

        var basic = new BackupPlan("basic", BackupPlanArgs.builder()
            .name("rollback-ns")
            .cluster(primary.id())
            .location("us-central1")
            .backupConfig(BackupPlanBackupConfigArgs.builder()
                .includeVolumeData(true)
                .includeSecrets(true)
                .allNamespaces(true)
                .build())
            .build());

        var rollbackNs = new RestorePlan("rollbackNs", RestorePlanArgs.builder()
            .name("rollback-ns-rp")
            .location("us-central1")
            .backupPlan(basic.id())
            .cluster(primary.id())
            .restoreConfig(RestorePlanRestoreConfigArgs.builder()
                .selectedNamespaces(RestorePlanRestoreConfigSelectedNamespacesArgs.builder()
                    .namespaces("my-ns")
                    .build())
                .namespacedResourceRestoreMode("DELETE_AND_RESTORE")
                .volumeDataRestorePolicy("RESTORE_VOLUME_DATA_FROM_BACKUP")
                .clusterResourceRestoreScope(RestorePlanRestoreConfigClusterResourceRestoreScopeArgs.builder()
                    .selectedGroupKinds(                    
                        RestorePlanRestoreConfigClusterResourceRestoreScopeSelectedGroupKindArgs.builder()
                            .resourceGroup("apiextension.k8s.io")
                            .resourceKind("CustomResourceDefinition")
                            .build(),
                        RestorePlanRestoreConfigClusterResourceRestoreScopeSelectedGroupKindArgs.builder()
                            .resourceGroup("storage.k8s.io")
                            .resourceKind("StorageClass")
                            .build())
                    .build())
                .clusterResourceConflictPolicy("USE_EXISTING_VERSION")
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:container:Cluster
    properties:
      name: rollback-ns-cluster
      location: us-central1
      initialNodeCount: 1
      workloadIdentityConfig:
        workloadPool: my-project-name.svc.id.goog
      addonsConfig:
        gkeBackupAgentConfig:
          enabled: true
      deletionProtection: true
      network: default
      subnetwork: default
  basic:
    type: gcp:gkebackup:BackupPlan
    properties:
      name: rollback-ns
      cluster: ${primary.id}
      location: us-central1
      backupConfig:
        includeVolumeData: true
        includeSecrets: true
        allNamespaces: true
  rollbackNs:
    type: gcp:gkebackup:RestorePlan
    name: rollback_ns
    properties:
      name: rollback-ns-rp
      location: us-central1
      backupPlan: ${basic.id}
      cluster: ${primary.id}
      restoreConfig:
        selectedNamespaces:
          namespaces:
            - my-ns
        namespacedResourceRestoreMode: DELETE_AND_RESTORE
        volumeDataRestorePolicy: RESTORE_VOLUME_DATA_FROM_BACKUP
        clusterResourceRestoreScope:
          selectedGroupKinds:
            - resourceGroup: apiextension.k8s.io
              resourceKind: CustomResourceDefinition
            - resourceGroup: storage.k8s.io
              resourceKind: StorageClass
        clusterResourceConflictPolicy: USE_EXISTING_VERSION

The selectedNamespaces property targets specific namespaces by name. Setting namespacedResourceRestoreMode to DELETE_AND_RESTORE removes existing resources in those namespaces before restoring from backup, ensuring a clean rollback. The selectedGroupKinds array limits which cluster-scoped resources are restored, here targeting only CustomResourceDefinitions and StorageClasses.

Restore individual applications by name

Application-level recovery targets specific workloads without touching other resources in the namespace or cluster.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const primary = new gcp.container.Cluster("primary", {
    name: "rollback-app-cluster",
    location: "us-central1",
    initialNodeCount: 1,
    workloadIdentityConfig: {
        workloadPool: "my-project-name.svc.id.goog",
    },
    addonsConfig: {
        gkeBackupAgentConfig: {
            enabled: true,
        },
    },
    deletionProtection: true,
    network: "default",
    subnetwork: "default",
});
const basic = new gcp.gkebackup.BackupPlan("basic", {
    name: "rollback-app",
    cluster: primary.id,
    location: "us-central1",
    backupConfig: {
        includeVolumeData: true,
        includeSecrets: true,
        allNamespaces: true,
    },
});
const rollbackApp = new gcp.gkebackup.RestorePlan("rollback_app", {
    name: "rollback-app-rp",
    location: "us-central1",
    backupPlan: basic.id,
    cluster: primary.id,
    restoreConfig: {
        selectedApplications: {
            namespacedNames: [{
                name: "my-app",
                namespace: "my-ns",
            }],
        },
        namespacedResourceRestoreMode: "DELETE_AND_RESTORE",
        volumeDataRestorePolicy: "REUSE_VOLUME_HANDLE_FROM_BACKUP",
        clusterResourceRestoreScope: {
            noGroupKinds: true,
        },
    },
});
import pulumi
import pulumi_gcp as gcp

primary = gcp.container.Cluster("primary",
    name="rollback-app-cluster",
    location="us-central1",
    initial_node_count=1,
    workload_identity_config={
        "workload_pool": "my-project-name.svc.id.goog",
    },
    addons_config={
        "gke_backup_agent_config": {
            "enabled": True,
        },
    },
    deletion_protection=True,
    network="default",
    subnetwork="default")
basic = gcp.gkebackup.BackupPlan("basic",
    name="rollback-app",
    cluster=primary.id,
    location="us-central1",
    backup_config={
        "include_volume_data": True,
        "include_secrets": True,
        "all_namespaces": True,
    })
rollback_app = gcp.gkebackup.RestorePlan("rollback_app",
    name="rollback-app-rp",
    location="us-central1",
    backup_plan=basic.id,
    cluster=primary.id,
    restore_config={
        "selected_applications": {
            "namespaced_names": [{
                "name": "my-app",
                "namespace": "my-ns",
            }],
        },
        "namespaced_resource_restore_mode": "DELETE_AND_RESTORE",
        "volume_data_restore_policy": "REUSE_VOLUME_HANDLE_FROM_BACKUP",
        "cluster_resource_restore_scope": {
            "no_group_kinds": True,
        },
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/container"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/gkebackup"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		primary, err := container.NewCluster(ctx, "primary", &container.ClusterArgs{
			Name:             pulumi.String("rollback-app-cluster"),
			Location:         pulumi.String("us-central1"),
			InitialNodeCount: pulumi.Int(1),
			WorkloadIdentityConfig: &container.ClusterWorkloadIdentityConfigArgs{
				WorkloadPool: pulumi.String("my-project-name.svc.id.goog"),
			},
			AddonsConfig: &container.ClusterAddonsConfigArgs{
				GkeBackupAgentConfig: &container.ClusterAddonsConfigGkeBackupAgentConfigArgs{
					Enabled: pulumi.Bool(true),
				},
			},
			DeletionProtection: pulumi.Bool(true),
			Network:            pulumi.String("default"),
			Subnetwork:         pulumi.String("default"),
		})
		if err != nil {
			return err
		}
		basic, err := gkebackup.NewBackupPlan(ctx, "basic", &gkebackup.BackupPlanArgs{
			Name:     pulumi.String("rollback-app"),
			Cluster:  primary.ID(),
			Location: pulumi.String("us-central1"),
			BackupConfig: &gkebackup.BackupPlanBackupConfigArgs{
				IncludeVolumeData: pulumi.Bool(true),
				IncludeSecrets:    pulumi.Bool(true),
				AllNamespaces:     pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		_, err = gkebackup.NewRestorePlan(ctx, "rollback_app", &gkebackup.RestorePlanArgs{
			Name:       pulumi.String("rollback-app-rp"),
			Location:   pulumi.String("us-central1"),
			BackupPlan: basic.ID(),
			Cluster:    primary.ID(),
			RestoreConfig: &gkebackup.RestorePlanRestoreConfigArgs{
				SelectedApplications: &gkebackup.RestorePlanRestoreConfigSelectedApplicationsArgs{
					NamespacedNames: gkebackup.RestorePlanRestoreConfigSelectedApplicationsNamespacedNameArray{
						&gkebackup.RestorePlanRestoreConfigSelectedApplicationsNamespacedNameArgs{
							Name:      pulumi.String("my-app"),
							Namespace: pulumi.String("my-ns"),
						},
					},
				},
				NamespacedResourceRestoreMode: pulumi.String("DELETE_AND_RESTORE"),
				VolumeDataRestorePolicy:       pulumi.String("REUSE_VOLUME_HANDLE_FROM_BACKUP"),
				ClusterResourceRestoreScope: &gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs{
					NoGroupKinds: pulumi.Bool(true),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var primary = new Gcp.Container.Cluster("primary", new()
    {
        Name = "rollback-app-cluster",
        Location = "us-central1",
        InitialNodeCount = 1,
        WorkloadIdentityConfig = new Gcp.Container.Inputs.ClusterWorkloadIdentityConfigArgs
        {
            WorkloadPool = "my-project-name.svc.id.goog",
        },
        AddonsConfig = new Gcp.Container.Inputs.ClusterAddonsConfigArgs
        {
            GkeBackupAgentConfig = new Gcp.Container.Inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs
            {
                Enabled = true,
            },
        },
        DeletionProtection = true,
        Network = "default",
        Subnetwork = "default",
    });

    var basic = new Gcp.GkeBackup.BackupPlan("basic", new()
    {
        Name = "rollback-app",
        Cluster = primary.Id,
        Location = "us-central1",
        BackupConfig = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigArgs
        {
            IncludeVolumeData = true,
            IncludeSecrets = true,
            AllNamespaces = true,
        },
    });

    var rollbackApp = new Gcp.GkeBackup.RestorePlan("rollback_app", new()
    {
        Name = "rollback-app-rp",
        Location = "us-central1",
        BackupPlan = basic.Id,
        Cluster = primary.Id,
        RestoreConfig = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigArgs
        {
            SelectedApplications = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigSelectedApplicationsArgs
            {
                NamespacedNames = new[]
                {
                    new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigSelectedApplicationsNamespacedNameArgs
                    {
                        Name = "my-app",
                        Namespace = "my-ns",
                    },
                },
            },
            NamespacedResourceRestoreMode = "DELETE_AND_RESTORE",
            VolumeDataRestorePolicy = "REUSE_VOLUME_HANDLE_FROM_BACKUP",
            ClusterResourceRestoreScope = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs
            {
                NoGroupKinds = true,
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.container.Cluster;
import com.pulumi.gcp.container.ClusterArgs;
import com.pulumi.gcp.container.inputs.ClusterWorkloadIdentityConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs;
import com.pulumi.gcp.gkebackup.BackupPlan;
import com.pulumi.gcp.gkebackup.BackupPlanArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupConfigArgs;
import com.pulumi.gcp.gkebackup.RestorePlan;
import com.pulumi.gcp.gkebackup.RestorePlanArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigSelectedApplicationsArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var primary = new Cluster("primary", ClusterArgs.builder()
            .name("rollback-app-cluster")
            .location("us-central1")
            .initialNodeCount(1)
            .workloadIdentityConfig(ClusterWorkloadIdentityConfigArgs.builder()
                .workloadPool("my-project-name.svc.id.goog")
                .build())
            .addonsConfig(ClusterAddonsConfigArgs.builder()
                .gkeBackupAgentConfig(ClusterAddonsConfigGkeBackupAgentConfigArgs.builder()
                    .enabled(true)
                    .build())
                .build())
            .deletionProtection(true)
            .network("default")
            .subnetwork("default")
            .build());

        var basic = new BackupPlan("basic", BackupPlanArgs.builder()
            .name("rollback-app")
            .cluster(primary.id())
            .location("us-central1")
            .backupConfig(BackupPlanBackupConfigArgs.builder()
                .includeVolumeData(true)
                .includeSecrets(true)
                .allNamespaces(true)
                .build())
            .build());

        var rollbackApp = new RestorePlan("rollbackApp", RestorePlanArgs.builder()
            .name("rollback-app-rp")
            .location("us-central1")
            .backupPlan(basic.id())
            .cluster(primary.id())
            .restoreConfig(RestorePlanRestoreConfigArgs.builder()
                .selectedApplications(RestorePlanRestoreConfigSelectedApplicationsArgs.builder()
                    .namespacedNames(RestorePlanRestoreConfigSelectedApplicationsNamespacedNameArgs.builder()
                        .name("my-app")
                        .namespace("my-ns")
                        .build())
                    .build())
                .namespacedResourceRestoreMode("DELETE_AND_RESTORE")
                .volumeDataRestorePolicy("REUSE_VOLUME_HANDLE_FROM_BACKUP")
                .clusterResourceRestoreScope(RestorePlanRestoreConfigClusterResourceRestoreScopeArgs.builder()
                    .noGroupKinds(true)
                    .build())
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:container:Cluster
    properties:
      name: rollback-app-cluster
      location: us-central1
      initialNodeCount: 1
      workloadIdentityConfig:
        workloadPool: my-project-name.svc.id.goog
      addonsConfig:
        gkeBackupAgentConfig:
          enabled: true
      deletionProtection: true
      network: default
      subnetwork: default
  basic:
    type: gcp:gkebackup:BackupPlan
    properties:
      name: rollback-app
      cluster: ${primary.id}
      location: us-central1
      backupConfig:
        includeVolumeData: true
        includeSecrets: true
        allNamespaces: true
  rollbackApp:
    type: gcp:gkebackup:RestorePlan
    name: rollback_app
    properties:
      name: rollback-app-rp
      location: us-central1
      backupPlan: ${basic.id}
      cluster: ${primary.id}
      restoreConfig:
        selectedApplications:
          namespacedNames:
            - name: my-app
              namespace: my-ns
        namespacedResourceRestoreMode: DELETE_AND_RESTORE
        volumeDataRestorePolicy: REUSE_VOLUME_HANDLE_FROM_BACKUP
        clusterResourceRestoreScope:
          noGroupKinds: true

The selectedApplications property identifies applications by name and namespace. This narrows the restore scope from entire namespaces to specific workloads. Setting volumeDataRestorePolicy to REUSE_VOLUME_HANDLE_FROM_BACKUP reattaches existing persistent volumes rather than creating new ones. The noGroupKinds flag prevents restoring any cluster-scoped resources, focusing entirely on the application itself.

Transform namespace names during restore

Disaster recovery scenarios sometimes require restoring to a different namespace to avoid conflicts or test recovery procedures without disrupting production.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const primary = new gcp.container.Cluster("primary", {
    name: "rename-ns-cluster",
    location: "us-central1",
    initialNodeCount: 1,
    workloadIdentityConfig: {
        workloadPool: "my-project-name.svc.id.goog",
    },
    addonsConfig: {
        gkeBackupAgentConfig: {
            enabled: true,
        },
    },
    deletionProtection: true,
    network: "default",
    subnetwork: "default",
});
const basic = new gcp.gkebackup.BackupPlan("basic", {
    name: "rename-ns",
    cluster: primary.id,
    location: "us-central1",
    backupConfig: {
        includeVolumeData: true,
        includeSecrets: true,
        allNamespaces: true,
    },
});
const renameNs = new gcp.gkebackup.RestorePlan("rename_ns", {
    name: "rename-ns-rp",
    location: "us-central1",
    backupPlan: basic.id,
    cluster: primary.id,
    restoreConfig: {
        selectedNamespaces: {
            namespaces: ["ns1"],
        },
        namespacedResourceRestoreMode: "FAIL_ON_CONFLICT",
        volumeDataRestorePolicy: "REUSE_VOLUME_HANDLE_FROM_BACKUP",
        clusterResourceRestoreScope: {
            noGroupKinds: true,
        },
        transformationRules: [
            {
                description: "rename namespace from ns1 to ns2",
                resourceFilter: {
                    groupKinds: [{
                        resourceKind: "Namespace",
                    }],
                    jsonPath: ".metadata[?(@.name == 'ns1')]",
                },
                fieldActions: [{
                    op: "REPLACE",
                    path: "/metadata/name",
                    value: "ns2",
                }],
            },
            {
                description: "move all resources from ns1 to ns2",
                resourceFilter: {
                    namespaces: ["ns1"],
                },
                fieldActions: [{
                    op: "REPLACE",
                    path: "/metadata/namespace",
                    value: "ns2",
                }],
            },
        ],
    },
});
import pulumi
import pulumi_gcp as gcp

primary = gcp.container.Cluster("primary",
    name="rename-ns-cluster",
    location="us-central1",
    initial_node_count=1,
    workload_identity_config={
        "workload_pool": "my-project-name.svc.id.goog",
    },
    addons_config={
        "gke_backup_agent_config": {
            "enabled": True,
        },
    },
    deletion_protection=True,
    network="default",
    subnetwork="default")
basic = gcp.gkebackup.BackupPlan("basic",
    name="rename-ns",
    cluster=primary.id,
    location="us-central1",
    backup_config={
        "include_volume_data": True,
        "include_secrets": True,
        "all_namespaces": True,
    })
rename_ns = gcp.gkebackup.RestorePlan("rename_ns",
    name="rename-ns-rp",
    location="us-central1",
    backup_plan=basic.id,
    cluster=primary.id,
    restore_config={
        "selected_namespaces": {
            "namespaces": ["ns1"],
        },
        "namespaced_resource_restore_mode": "FAIL_ON_CONFLICT",
        "volume_data_restore_policy": "REUSE_VOLUME_HANDLE_FROM_BACKUP",
        "cluster_resource_restore_scope": {
            "no_group_kinds": True,
        },
        "transformation_rules": [
            {
                "description": "rename namespace from ns1 to ns2",
                "resource_filter": {
                    "group_kinds": [{
                        "resource_kind": "Namespace",
                    }],
                    "json_path": ".metadata[?(@.name == 'ns1')]",
                },
                "field_actions": [{
                    "op": "REPLACE",
                    "path": "/metadata/name",
                    "value": "ns2",
                }],
            },
            {
                "description": "move all resources from ns1 to ns2",
                "resource_filter": {
                    "namespaces": ["ns1"],
                },
                "field_actions": [{
                    "op": "REPLACE",
                    "path": "/metadata/namespace",
                    "value": "ns2",
                }],
            },
        ],
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/container"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/gkebackup"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		primary, err := container.NewCluster(ctx, "primary", &container.ClusterArgs{
			Name:             pulumi.String("rename-ns-cluster"),
			Location:         pulumi.String("us-central1"),
			InitialNodeCount: pulumi.Int(1),
			WorkloadIdentityConfig: &container.ClusterWorkloadIdentityConfigArgs{
				WorkloadPool: pulumi.String("my-project-name.svc.id.goog"),
			},
			AddonsConfig: &container.ClusterAddonsConfigArgs{
				GkeBackupAgentConfig: &container.ClusterAddonsConfigGkeBackupAgentConfigArgs{
					Enabled: pulumi.Bool(true),
				},
			},
			DeletionProtection: pulumi.Bool(true),
			Network:            pulumi.String("default"),
			Subnetwork:         pulumi.String("default"),
		})
		if err != nil {
			return err
		}
		basic, err := gkebackup.NewBackupPlan(ctx, "basic", &gkebackup.BackupPlanArgs{
			Name:     pulumi.String("rename-ns"),
			Cluster:  primary.ID(),
			Location: pulumi.String("us-central1"),
			BackupConfig: &gkebackup.BackupPlanBackupConfigArgs{
				IncludeVolumeData: pulumi.Bool(true),
				IncludeSecrets:    pulumi.Bool(true),
				AllNamespaces:     pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		_, err = gkebackup.NewRestorePlan(ctx, "rename_ns", &gkebackup.RestorePlanArgs{
			Name:       pulumi.String("rename-ns-rp"),
			Location:   pulumi.String("us-central1"),
			BackupPlan: basic.ID(),
			Cluster:    primary.ID(),
			RestoreConfig: &gkebackup.RestorePlanRestoreConfigArgs{
				SelectedNamespaces: &gkebackup.RestorePlanRestoreConfigSelectedNamespacesArgs{
					Namespaces: pulumi.StringArray{
						pulumi.String("ns1"),
					},
				},
				NamespacedResourceRestoreMode: pulumi.String("FAIL_ON_CONFLICT"),
				VolumeDataRestorePolicy:       pulumi.String("REUSE_VOLUME_HANDLE_FROM_BACKUP"),
				ClusterResourceRestoreScope: &gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs{
					NoGroupKinds: pulumi.Bool(true),
				},
				TransformationRules: gkebackup.RestorePlanRestoreConfigTransformationRuleArray{
					&gkebackup.RestorePlanRestoreConfigTransformationRuleArgs{
						Description: pulumi.String("rename namespace from ns1 to ns2"),
						ResourceFilter: &gkebackup.RestorePlanRestoreConfigTransformationRuleResourceFilterArgs{
							GroupKinds: gkebackup.RestorePlanRestoreConfigTransformationRuleResourceFilterGroupKindArray{
								&gkebackup.RestorePlanRestoreConfigTransformationRuleResourceFilterGroupKindArgs{
									ResourceKind: pulumi.String("Namespace"),
								},
							},
							JsonPath: pulumi.String(".metadata[?(@.name == 'ns1')]"),
						},
						FieldActions: gkebackup.RestorePlanRestoreConfigTransformationRuleFieldActionArray{
							&gkebackup.RestorePlanRestoreConfigTransformationRuleFieldActionArgs{
								Op:    pulumi.String("REPLACE"),
								Path:  pulumi.String("/metadata/name"),
								Value: pulumi.String("ns2"),
							},
						},
					},
					&gkebackup.RestorePlanRestoreConfigTransformationRuleArgs{
						Description: pulumi.String("move all resources from ns1 to ns2"),
						ResourceFilter: &gkebackup.RestorePlanRestoreConfigTransformationRuleResourceFilterArgs{
							Namespaces: pulumi.StringArray{
								pulumi.String("ns1"),
							},
						},
						FieldActions: gkebackup.RestorePlanRestoreConfigTransformationRuleFieldActionArray{
							&gkebackup.RestorePlanRestoreConfigTransformationRuleFieldActionArgs{
								Op:    pulumi.String("REPLACE"),
								Path:  pulumi.String("/metadata/namespace"),
								Value: pulumi.String("ns2"),
							},
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var primary = new Gcp.Container.Cluster("primary", new()
    {
        Name = "rename-ns-cluster",
        Location = "us-central1",
        InitialNodeCount = 1,
        WorkloadIdentityConfig = new Gcp.Container.Inputs.ClusterWorkloadIdentityConfigArgs
        {
            WorkloadPool = "my-project-name.svc.id.goog",
        },
        AddonsConfig = new Gcp.Container.Inputs.ClusterAddonsConfigArgs
        {
            GkeBackupAgentConfig = new Gcp.Container.Inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs
            {
                Enabled = true,
            },
        },
        DeletionProtection = true,
        Network = "default",
        Subnetwork = "default",
    });

    var basic = new Gcp.GkeBackup.BackupPlan("basic", new()
    {
        Name = "rename-ns",
        Cluster = primary.Id,
        Location = "us-central1",
        BackupConfig = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigArgs
        {
            IncludeVolumeData = true,
            IncludeSecrets = true,
            AllNamespaces = true,
        },
    });

    var renameNs = new Gcp.GkeBackup.RestorePlan("rename_ns", new()
    {
        Name = "rename-ns-rp",
        Location = "us-central1",
        BackupPlan = basic.Id,
        Cluster = primary.Id,
        RestoreConfig = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigArgs
        {
            SelectedNamespaces = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigSelectedNamespacesArgs
            {
                Namespaces = new[]
                {
                    "ns1",
                },
            },
            NamespacedResourceRestoreMode = "FAIL_ON_CONFLICT",
            VolumeDataRestorePolicy = "REUSE_VOLUME_HANDLE_FROM_BACKUP",
            ClusterResourceRestoreScope = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs
            {
                NoGroupKinds = true,
            },
            TransformationRules = new[]
            {
                new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigTransformationRuleArgs
                {
                    Description = "rename namespace from ns1 to ns2",
                    ResourceFilter = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigTransformationRuleResourceFilterArgs
                    {
                        GroupKinds = new[]
                        {
                            new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigTransformationRuleResourceFilterGroupKindArgs
                            {
                                ResourceKind = "Namespace",
                            },
                        },
                        JsonPath = ".metadata[?(@.name == 'ns1')]",
                    },
                    FieldActions = new[]
                    {
                        new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigTransformationRuleFieldActionArgs
                        {
                            Op = "REPLACE",
                            Path = "/metadata/name",
                            Value = "ns2",
                        },
                    },
                },
                new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigTransformationRuleArgs
                {
                    Description = "move all resources from ns1 to ns2",
                    ResourceFilter = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigTransformationRuleResourceFilterArgs
                    {
                        Namespaces = new[]
                        {
                            "ns1",
                        },
                    },
                    FieldActions = new[]
                    {
                        new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigTransformationRuleFieldActionArgs
                        {
                            Op = "REPLACE",
                            Path = "/metadata/namespace",
                            Value = "ns2",
                        },
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.container.Cluster;
import com.pulumi.gcp.container.ClusterArgs;
import com.pulumi.gcp.container.inputs.ClusterWorkloadIdentityConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs;
import com.pulumi.gcp.gkebackup.BackupPlan;
import com.pulumi.gcp.gkebackup.BackupPlanArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupConfigArgs;
import com.pulumi.gcp.gkebackup.RestorePlan;
import com.pulumi.gcp.gkebackup.RestorePlanArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigSelectedNamespacesArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var primary = new Cluster("primary", ClusterArgs.builder()
            .name("rename-ns-cluster")
            .location("us-central1")
            .initialNodeCount(1)
            .workloadIdentityConfig(ClusterWorkloadIdentityConfigArgs.builder()
                .workloadPool("my-project-name.svc.id.goog")
                .build())
            .addonsConfig(ClusterAddonsConfigArgs.builder()
                .gkeBackupAgentConfig(ClusterAddonsConfigGkeBackupAgentConfigArgs.builder()
                    .enabled(true)
                    .build())
                .build())
            .deletionProtection(true)
            .network("default")
            .subnetwork("default")
            .build());

        var basic = new BackupPlan("basic", BackupPlanArgs.builder()
            .name("rename-ns")
            .cluster(primary.id())
            .location("us-central1")
            .backupConfig(BackupPlanBackupConfigArgs.builder()
                .includeVolumeData(true)
                .includeSecrets(true)
                .allNamespaces(true)
                .build())
            .build());

        var renameNs = new RestorePlan("renameNs", RestorePlanArgs.builder()
            .name("rename-ns-rp")
            .location("us-central1")
            .backupPlan(basic.id())
            .cluster(primary.id())
            .restoreConfig(RestorePlanRestoreConfigArgs.builder()
                .selectedNamespaces(RestorePlanRestoreConfigSelectedNamespacesArgs.builder()
                    .namespaces("ns1")
                    .build())
                .namespacedResourceRestoreMode("FAIL_ON_CONFLICT")
                .volumeDataRestorePolicy("REUSE_VOLUME_HANDLE_FROM_BACKUP")
                .clusterResourceRestoreScope(RestorePlanRestoreConfigClusterResourceRestoreScopeArgs.builder()
                    .noGroupKinds(true)
                    .build())
                .transformationRules(                
                    RestorePlanRestoreConfigTransformationRuleArgs.builder()
                        .description("rename namespace from ns1 to ns2")
                        .resourceFilter(RestorePlanRestoreConfigTransformationRuleResourceFilterArgs.builder()
                            .groupKinds(RestorePlanRestoreConfigTransformationRuleResourceFilterGroupKindArgs.builder()
                                .resourceKind("Namespace")
                                .build())
                            .jsonPath(".metadata[?(@.name == 'ns1')]")
                            .build())
                        .fieldActions(RestorePlanRestoreConfigTransformationRuleFieldActionArgs.builder()
                            .op("REPLACE")
                            .path("/metadata/name")
                            .value("ns2")
                            .build())
                        .build(),
                    RestorePlanRestoreConfigTransformationRuleArgs.builder()
                        .description("move all resources from ns1 to ns2")
                        .resourceFilter(RestorePlanRestoreConfigTransformationRuleResourceFilterArgs.builder()
                            .namespaces("ns1")
                            .build())
                        .fieldActions(RestorePlanRestoreConfigTransformationRuleFieldActionArgs.builder()
                            .op("REPLACE")
                            .path("/metadata/namespace")
                            .value("ns2")
                            .build())
                        .build())
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:container:Cluster
    properties:
      name: rename-ns-cluster
      location: us-central1
      initialNodeCount: 1
      workloadIdentityConfig:
        workloadPool: my-project-name.svc.id.goog
      addonsConfig:
        gkeBackupAgentConfig:
          enabled: true
      deletionProtection: true
      network: default
      subnetwork: default
  basic:
    type: gcp:gkebackup:BackupPlan
    properties:
      name: rename-ns
      cluster: ${primary.id}
      location: us-central1
      backupConfig:
        includeVolumeData: true
        includeSecrets: true
        allNamespaces: true
  renameNs:
    type: gcp:gkebackup:RestorePlan
    name: rename_ns
    properties:
      name: rename-ns-rp
      location: us-central1
      backupPlan: ${basic.id}
      cluster: ${primary.id}
      restoreConfig:
        selectedNamespaces:
          namespaces:
            - ns1
        namespacedResourceRestoreMode: FAIL_ON_CONFLICT
        volumeDataRestorePolicy: REUSE_VOLUME_HANDLE_FROM_BACKUP
        clusterResourceRestoreScope:
          noGroupKinds: true
        transformationRules:
          - description: rename namespace from ns1 to ns2
            resourceFilter:
              groupKinds:
                - resourceKind: Namespace
              jsonPath: .metadata[?(@.name == 'ns1')]
            fieldActions:
              - op: REPLACE
                path: /metadata/name
                value: ns2
          - description: move all resources from ns1 to ns2
            resourceFilter:
              namespaces:
                - ns1
            fieldActions:
              - op: REPLACE
                path: /metadata/namespace
                value: ns2

The transformationRules array applies modifications during restore. Each rule has a resourceFilter that selects which resources to transform using jsonPath expressions, and fieldActions that specify modifications. The first rule renames the namespace resource itself from “ns1” to “ns2” by replacing the metadata name field. The second rule updates the namespace reference in all resources from “ns1” to “ns2”, moving them to the new namespace.

Merge restored resources with existing state

GitOps workflows need to restore backup data without overwriting resources managed by continuous deployment pipelines.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const primary = new gcp.container.Cluster("primary", {
    name: "gitops-mode-cluster",
    location: "us-central1",
    initialNodeCount: 1,
    workloadIdentityConfig: {
        workloadPool: "my-project-name.svc.id.goog",
    },
    addonsConfig: {
        gkeBackupAgentConfig: {
            enabled: true,
        },
    },
    deletionProtection: true,
    network: "default",
    subnetwork: "default",
});
const basic = new gcp.gkebackup.BackupPlan("basic", {
    name: "gitops-mode",
    cluster: primary.id,
    location: "us-central1",
    backupConfig: {
        includeVolumeData: true,
        includeSecrets: true,
        allNamespaces: true,
    },
});
const gitopsMode = new gcp.gkebackup.RestorePlan("gitops_mode", {
    name: "gitops-mode",
    location: "us-central1",
    backupPlan: basic.id,
    cluster: primary.id,
    restoreConfig: {
        allNamespaces: true,
        namespacedResourceRestoreMode: "MERGE_SKIP_ON_CONFLICT",
        volumeDataRestorePolicy: "RESTORE_VOLUME_DATA_FROM_BACKUP",
        clusterResourceRestoreScope: {
            allGroupKinds: true,
        },
        clusterResourceConflictPolicy: "USE_EXISTING_VERSION",
    },
});
import pulumi
import pulumi_gcp as gcp

primary = gcp.container.Cluster("primary",
    name="gitops-mode-cluster",
    location="us-central1",
    initial_node_count=1,
    workload_identity_config={
        "workload_pool": "my-project-name.svc.id.goog",
    },
    addons_config={
        "gke_backup_agent_config": {
            "enabled": True,
        },
    },
    deletion_protection=True,
    network="default",
    subnetwork="default")
basic = gcp.gkebackup.BackupPlan("basic",
    name="gitops-mode",
    cluster=primary.id,
    location="us-central1",
    backup_config={
        "include_volume_data": True,
        "include_secrets": True,
        "all_namespaces": True,
    })
gitops_mode = gcp.gkebackup.RestorePlan("gitops_mode",
    name="gitops-mode",
    location="us-central1",
    backup_plan=basic.id,
    cluster=primary.id,
    restore_config={
        "all_namespaces": True,
        "namespaced_resource_restore_mode": "MERGE_SKIP_ON_CONFLICT",
        "volume_data_restore_policy": "RESTORE_VOLUME_DATA_FROM_BACKUP",
        "cluster_resource_restore_scope": {
            "all_group_kinds": True,
        },
        "cluster_resource_conflict_policy": "USE_EXISTING_VERSION",
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/container"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/gkebackup"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		primary, err := container.NewCluster(ctx, "primary", &container.ClusterArgs{
			Name:             pulumi.String("gitops-mode-cluster"),
			Location:         pulumi.String("us-central1"),
			InitialNodeCount: pulumi.Int(1),
			WorkloadIdentityConfig: &container.ClusterWorkloadIdentityConfigArgs{
				WorkloadPool: pulumi.String("my-project-name.svc.id.goog"),
			},
			AddonsConfig: &container.ClusterAddonsConfigArgs{
				GkeBackupAgentConfig: &container.ClusterAddonsConfigGkeBackupAgentConfigArgs{
					Enabled: pulumi.Bool(true),
				},
			},
			DeletionProtection: pulumi.Bool(true),
			Network:            pulumi.String("default"),
			Subnetwork:         pulumi.String("default"),
		})
		if err != nil {
			return err
		}
		basic, err := gkebackup.NewBackupPlan(ctx, "basic", &gkebackup.BackupPlanArgs{
			Name:     pulumi.String("gitops-mode"),
			Cluster:  primary.ID(),
			Location: pulumi.String("us-central1"),
			BackupConfig: &gkebackup.BackupPlanBackupConfigArgs{
				IncludeVolumeData: pulumi.Bool(true),
				IncludeSecrets:    pulumi.Bool(true),
				AllNamespaces:     pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		_, err = gkebackup.NewRestorePlan(ctx, "gitops_mode", &gkebackup.RestorePlanArgs{
			Name:       pulumi.String("gitops-mode"),
			Location:   pulumi.String("us-central1"),
			BackupPlan: basic.ID(),
			Cluster:    primary.ID(),
			RestoreConfig: &gkebackup.RestorePlanRestoreConfigArgs{
				AllNamespaces:                 pulumi.Bool(true),
				NamespacedResourceRestoreMode: pulumi.String("MERGE_SKIP_ON_CONFLICT"),
				VolumeDataRestorePolicy:       pulumi.String("RESTORE_VOLUME_DATA_FROM_BACKUP"),
				ClusterResourceRestoreScope: &gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs{
					AllGroupKinds: pulumi.Bool(true),
				},
				ClusterResourceConflictPolicy: pulumi.String("USE_EXISTING_VERSION"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var primary = new Gcp.Container.Cluster("primary", new()
    {
        Name = "gitops-mode-cluster",
        Location = "us-central1",
        InitialNodeCount = 1,
        WorkloadIdentityConfig = new Gcp.Container.Inputs.ClusterWorkloadIdentityConfigArgs
        {
            WorkloadPool = "my-project-name.svc.id.goog",
        },
        AddonsConfig = new Gcp.Container.Inputs.ClusterAddonsConfigArgs
        {
            GkeBackupAgentConfig = new Gcp.Container.Inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs
            {
                Enabled = true,
            },
        },
        DeletionProtection = true,
        Network = "default",
        Subnetwork = "default",
    });

    var basic = new Gcp.GkeBackup.BackupPlan("basic", new()
    {
        Name = "gitops-mode",
        Cluster = primary.Id,
        Location = "us-central1",
        BackupConfig = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigArgs
        {
            IncludeVolumeData = true,
            IncludeSecrets = true,
            AllNamespaces = true,
        },
    });

    var gitopsMode = new Gcp.GkeBackup.RestorePlan("gitops_mode", new()
    {
        Name = "gitops-mode",
        Location = "us-central1",
        BackupPlan = basic.Id,
        Cluster = primary.Id,
        RestoreConfig = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigArgs
        {
            AllNamespaces = true,
            NamespacedResourceRestoreMode = "MERGE_SKIP_ON_CONFLICT",
            VolumeDataRestorePolicy = "RESTORE_VOLUME_DATA_FROM_BACKUP",
            ClusterResourceRestoreScope = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs
            {
                AllGroupKinds = true,
            },
            ClusterResourceConflictPolicy = "USE_EXISTING_VERSION",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.container.Cluster;
import com.pulumi.gcp.container.ClusterArgs;
import com.pulumi.gcp.container.inputs.ClusterWorkloadIdentityConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs;
import com.pulumi.gcp.gkebackup.BackupPlan;
import com.pulumi.gcp.gkebackup.BackupPlanArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupConfigArgs;
import com.pulumi.gcp.gkebackup.RestorePlan;
import com.pulumi.gcp.gkebackup.RestorePlanArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var primary = new Cluster("primary", ClusterArgs.builder()
            .name("gitops-mode-cluster")
            .location("us-central1")
            .initialNodeCount(1)
            .workloadIdentityConfig(ClusterWorkloadIdentityConfigArgs.builder()
                .workloadPool("my-project-name.svc.id.goog")
                .build())
            .addonsConfig(ClusterAddonsConfigArgs.builder()
                .gkeBackupAgentConfig(ClusterAddonsConfigGkeBackupAgentConfigArgs.builder()
                    .enabled(true)
                    .build())
                .build())
            .deletionProtection(true)
            .network("default")
            .subnetwork("default")
            .build());

        var basic = new BackupPlan("basic", BackupPlanArgs.builder()
            .name("gitops-mode")
            .cluster(primary.id())
            .location("us-central1")
            .backupConfig(BackupPlanBackupConfigArgs.builder()
                .includeVolumeData(true)
                .includeSecrets(true)
                .allNamespaces(true)
                .build())
            .build());

        var gitopsMode = new RestorePlan("gitopsMode", RestorePlanArgs.builder()
            .name("gitops-mode")
            .location("us-central1")
            .backupPlan(basic.id())
            .cluster(primary.id())
            .restoreConfig(RestorePlanRestoreConfigArgs.builder()
                .allNamespaces(true)
                .namespacedResourceRestoreMode("MERGE_SKIP_ON_CONFLICT")
                .volumeDataRestorePolicy("RESTORE_VOLUME_DATA_FROM_BACKUP")
                .clusterResourceRestoreScope(RestorePlanRestoreConfigClusterResourceRestoreScopeArgs.builder()
                    .allGroupKinds(true)
                    .build())
                .clusterResourceConflictPolicy("USE_EXISTING_VERSION")
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:container:Cluster
    properties:
      name: gitops-mode-cluster
      location: us-central1
      initialNodeCount: 1
      workloadIdentityConfig:
        workloadPool: my-project-name.svc.id.goog
      addonsConfig:
        gkeBackupAgentConfig:
          enabled: true
      deletionProtection: true
      network: default
      subnetwork: default
  basic:
    type: gcp:gkebackup:BackupPlan
    properties:
      name: gitops-mode
      cluster: ${primary.id}
      location: us-central1
      backupConfig:
        includeVolumeData: true
        includeSecrets: true
        allNamespaces: true
  gitopsMode:
    type: gcp:gkebackup:RestorePlan
    name: gitops_mode
    properties:
      name: gitops-mode
      location: us-central1
      backupPlan: ${basic.id}
      cluster: ${primary.id}
      restoreConfig:
        allNamespaces: true
        namespacedResourceRestoreMode: MERGE_SKIP_ON_CONFLICT
        volumeDataRestorePolicy: RESTORE_VOLUME_DATA_FROM_BACKUP
        clusterResourceRestoreScope:
          allGroupKinds: true
        clusterResourceConflictPolicy: USE_EXISTING_VERSION

Setting namespacedResourceRestoreMode to MERGE_SKIP_ON_CONFLICT allows the restore to proceed even when resources already exist. Instead of failing or deleting existing resources, this mode skips conflicting resources and only restores what’s missing. This preserves resources managed by GitOps tooling while recovering data from backup.

Control resource restoration order with dependencies

Complex applications with custom resources often require specific restoration order to satisfy dependencies between resource types.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const primary = new gcp.container.Cluster("primary", {
    name: "restore-order-cluster",
    location: "us-central1",
    initialNodeCount: 1,
    workloadIdentityConfig: {
        workloadPool: "my-project-name.svc.id.goog",
    },
    addonsConfig: {
        gkeBackupAgentConfig: {
            enabled: true,
        },
    },
    deletionProtection: true,
    network: "default",
    subnetwork: "default",
});
const basic = new gcp.gkebackup.BackupPlan("basic", {
    name: "restore-order",
    cluster: primary.id,
    location: "us-central1",
    backupConfig: {
        includeVolumeData: true,
        includeSecrets: true,
        allNamespaces: true,
    },
});
const restoreOrder = new gcp.gkebackup.RestorePlan("restore_order", {
    name: "restore-order",
    location: "us-central1",
    backupPlan: basic.id,
    cluster: primary.id,
    restoreConfig: {
        allNamespaces: true,
        namespacedResourceRestoreMode: "FAIL_ON_CONFLICT",
        volumeDataRestorePolicy: "RESTORE_VOLUME_DATA_FROM_BACKUP",
        clusterResourceRestoreScope: {
            allGroupKinds: true,
        },
        clusterResourceConflictPolicy: "USE_EXISTING_VERSION",
        restoreOrder: {
            groupKindDependencies: [
                {
                    satisfying: {
                        resourceGroup: "stable.example.com",
                        resourceKind: "kindA",
                    },
                    requiring: {
                        resourceGroup: "stable.example.com",
                        resourceKind: "kindB",
                    },
                },
                {
                    satisfying: {
                        resourceGroup: "stable.example.com",
                        resourceKind: "kindB",
                    },
                    requiring: {
                        resourceGroup: "stable.example.com",
                        resourceKind: "kindC",
                    },
                },
            ],
        },
    },
});
import pulumi
import pulumi_gcp as gcp

primary = gcp.container.Cluster("primary",
    name="restore-order-cluster",
    location="us-central1",
    initial_node_count=1,
    workload_identity_config={
        "workload_pool": "my-project-name.svc.id.goog",
    },
    addons_config={
        "gke_backup_agent_config": {
            "enabled": True,
        },
    },
    deletion_protection=True,
    network="default",
    subnetwork="default")
basic = gcp.gkebackup.BackupPlan("basic",
    name="restore-order",
    cluster=primary.id,
    location="us-central1",
    backup_config={
        "include_volume_data": True,
        "include_secrets": True,
        "all_namespaces": True,
    })
restore_order = gcp.gkebackup.RestorePlan("restore_order",
    name="restore-order",
    location="us-central1",
    backup_plan=basic.id,
    cluster=primary.id,
    restore_config={
        "all_namespaces": True,
        "namespaced_resource_restore_mode": "FAIL_ON_CONFLICT",
        "volume_data_restore_policy": "RESTORE_VOLUME_DATA_FROM_BACKUP",
        "cluster_resource_restore_scope": {
            "all_group_kinds": True,
        },
        "cluster_resource_conflict_policy": "USE_EXISTING_VERSION",
        "restore_order": {
            "group_kind_dependencies": [
                {
                    "satisfying": {
                        "resource_group": "stable.example.com",
                        "resource_kind": "kindA",
                    },
                    "requiring": {
                        "resource_group": "stable.example.com",
                        "resource_kind": "kindB",
                    },
                },
                {
                    "satisfying": {
                        "resource_group": "stable.example.com",
                        "resource_kind": "kindB",
                    },
                    "requiring": {
                        "resource_group": "stable.example.com",
                        "resource_kind": "kindC",
                    },
                },
            ],
        },
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/container"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/gkebackup"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		primary, err := container.NewCluster(ctx, "primary", &container.ClusterArgs{
			Name:             pulumi.String("restore-order-cluster"),
			Location:         pulumi.String("us-central1"),
			InitialNodeCount: pulumi.Int(1),
			WorkloadIdentityConfig: &container.ClusterWorkloadIdentityConfigArgs{
				WorkloadPool: pulumi.String("my-project-name.svc.id.goog"),
			},
			AddonsConfig: &container.ClusterAddonsConfigArgs{
				GkeBackupAgentConfig: &container.ClusterAddonsConfigGkeBackupAgentConfigArgs{
					Enabled: pulumi.Bool(true),
				},
			},
			DeletionProtection: pulumi.Bool(true),
			Network:            pulumi.String("default"),
			Subnetwork:         pulumi.String("default"),
		})
		if err != nil {
			return err
		}
		basic, err := gkebackup.NewBackupPlan(ctx, "basic", &gkebackup.BackupPlanArgs{
			Name:     pulumi.String("restore-order"),
			Cluster:  primary.ID(),
			Location: pulumi.String("us-central1"),
			BackupConfig: &gkebackup.BackupPlanBackupConfigArgs{
				IncludeVolumeData: pulumi.Bool(true),
				IncludeSecrets:    pulumi.Bool(true),
				AllNamespaces:     pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		_, err = gkebackup.NewRestorePlan(ctx, "restore_order", &gkebackup.RestorePlanArgs{
			Name:       pulumi.String("restore-order"),
			Location:   pulumi.String("us-central1"),
			BackupPlan: basic.ID(),
			Cluster:    primary.ID(),
			RestoreConfig: &gkebackup.RestorePlanRestoreConfigArgs{
				AllNamespaces:                 pulumi.Bool(true),
				NamespacedResourceRestoreMode: pulumi.String("FAIL_ON_CONFLICT"),
				VolumeDataRestorePolicy:       pulumi.String("RESTORE_VOLUME_DATA_FROM_BACKUP"),
				ClusterResourceRestoreScope: &gkebackup.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs{
					AllGroupKinds: pulumi.Bool(true),
				},
				ClusterResourceConflictPolicy: pulumi.String("USE_EXISTING_VERSION"),
				RestoreOrder: &gkebackup.RestorePlanRestoreConfigRestoreOrderArgs{
					GroupKindDependencies: gkebackup.RestorePlanRestoreConfigRestoreOrderGroupKindDependencyArray{
						&gkebackup.RestorePlanRestoreConfigRestoreOrderGroupKindDependencyArgs{
							Satisfying: &gkebackup.RestorePlanRestoreConfigRestoreOrderGroupKindDependencySatisfyingArgs{
								ResourceGroup: pulumi.String("stable.example.com"),
								ResourceKind:  pulumi.String("kindA"),
							},
							Requiring: &gkebackup.RestorePlanRestoreConfigRestoreOrderGroupKindDependencyRequiringArgs{
								ResourceGroup: pulumi.String("stable.example.com"),
								ResourceKind:  pulumi.String("kindB"),
							},
						},
						&gkebackup.RestorePlanRestoreConfigRestoreOrderGroupKindDependencyArgs{
							Satisfying: &gkebackup.RestorePlanRestoreConfigRestoreOrderGroupKindDependencySatisfyingArgs{
								ResourceGroup: pulumi.String("stable.example.com"),
								ResourceKind:  pulumi.String("kindB"),
							},
							Requiring: &gkebackup.RestorePlanRestoreConfigRestoreOrderGroupKindDependencyRequiringArgs{
								ResourceGroup: pulumi.String("stable.example.com"),
								ResourceKind:  pulumi.String("kindC"),
							},
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var primary = new Gcp.Container.Cluster("primary", new()
    {
        Name = "restore-order-cluster",
        Location = "us-central1",
        InitialNodeCount = 1,
        WorkloadIdentityConfig = new Gcp.Container.Inputs.ClusterWorkloadIdentityConfigArgs
        {
            WorkloadPool = "my-project-name.svc.id.goog",
        },
        AddonsConfig = new Gcp.Container.Inputs.ClusterAddonsConfigArgs
        {
            GkeBackupAgentConfig = new Gcp.Container.Inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs
            {
                Enabled = true,
            },
        },
        DeletionProtection = true,
        Network = "default",
        Subnetwork = "default",
    });

    var basic = new Gcp.GkeBackup.BackupPlan("basic", new()
    {
        Name = "restore-order",
        Cluster = primary.Id,
        Location = "us-central1",
        BackupConfig = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigArgs
        {
            IncludeVolumeData = true,
            IncludeSecrets = true,
            AllNamespaces = true,
        },
    });

    var restoreOrder = new Gcp.GkeBackup.RestorePlan("restore_order", new()
    {
        Name = "restore-order",
        Location = "us-central1",
        BackupPlan = basic.Id,
        Cluster = primary.Id,
        RestoreConfig = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigArgs
        {
            AllNamespaces = true,
            NamespacedResourceRestoreMode = "FAIL_ON_CONFLICT",
            VolumeDataRestorePolicy = "RESTORE_VOLUME_DATA_FROM_BACKUP",
            ClusterResourceRestoreScope = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs
            {
                AllGroupKinds = true,
            },
            ClusterResourceConflictPolicy = "USE_EXISTING_VERSION",
            RestoreOrder = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigRestoreOrderArgs
            {
                GroupKindDependencies = new[]
                {
                    new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigRestoreOrderGroupKindDependencyArgs
                    {
                        Satisfying = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigRestoreOrderGroupKindDependencySatisfyingArgs
                        {
                            ResourceGroup = "stable.example.com",
                            ResourceKind = "kindA",
                        },
                        Requiring = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigRestoreOrderGroupKindDependencyRequiringArgs
                        {
                            ResourceGroup = "stable.example.com",
                            ResourceKind = "kindB",
                        },
                    },
                    new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigRestoreOrderGroupKindDependencyArgs
                    {
                        Satisfying = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigRestoreOrderGroupKindDependencySatisfyingArgs
                        {
                            ResourceGroup = "stable.example.com",
                            ResourceKind = "kindB",
                        },
                        Requiring = new Gcp.GkeBackup.Inputs.RestorePlanRestoreConfigRestoreOrderGroupKindDependencyRequiringArgs
                        {
                            ResourceGroup = "stable.example.com",
                            ResourceKind = "kindC",
                        },
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.container.Cluster;
import com.pulumi.gcp.container.ClusterArgs;
import com.pulumi.gcp.container.inputs.ClusterWorkloadIdentityConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigArgs;
import com.pulumi.gcp.container.inputs.ClusterAddonsConfigGkeBackupAgentConfigArgs;
import com.pulumi.gcp.gkebackup.BackupPlan;
import com.pulumi.gcp.gkebackup.BackupPlanArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupConfigArgs;
import com.pulumi.gcp.gkebackup.RestorePlan;
import com.pulumi.gcp.gkebackup.RestorePlanArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigClusterResourceRestoreScopeArgs;
import com.pulumi.gcp.gkebackup.inputs.RestorePlanRestoreConfigRestoreOrderArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var primary = new Cluster("primary", ClusterArgs.builder()
            .name("restore-order-cluster")
            .location("us-central1")
            .initialNodeCount(1)
            .workloadIdentityConfig(ClusterWorkloadIdentityConfigArgs.builder()
                .workloadPool("my-project-name.svc.id.goog")
                .build())
            .addonsConfig(ClusterAddonsConfigArgs.builder()
                .gkeBackupAgentConfig(ClusterAddonsConfigGkeBackupAgentConfigArgs.builder()
                    .enabled(true)
                    .build())
                .build())
            .deletionProtection(true)
            .network("default")
            .subnetwork("default")
            .build());

        var basic = new BackupPlan("basic", BackupPlanArgs.builder()
            .name("restore-order")
            .cluster(primary.id())
            .location("us-central1")
            .backupConfig(BackupPlanBackupConfigArgs.builder()
                .includeVolumeData(true)
                .includeSecrets(true)
                .allNamespaces(true)
                .build())
            .build());

        var restoreOrder = new RestorePlan("restoreOrder", RestorePlanArgs.builder()
            .name("restore-order")
            .location("us-central1")
            .backupPlan(basic.id())
            .cluster(primary.id())
            .restoreConfig(RestorePlanRestoreConfigArgs.builder()
                .allNamespaces(true)
                .namespacedResourceRestoreMode("FAIL_ON_CONFLICT")
                .volumeDataRestorePolicy("RESTORE_VOLUME_DATA_FROM_BACKUP")
                .clusterResourceRestoreScope(RestorePlanRestoreConfigClusterResourceRestoreScopeArgs.builder()
                    .allGroupKinds(true)
                    .build())
                .clusterResourceConflictPolicy("USE_EXISTING_VERSION")
                .restoreOrder(RestorePlanRestoreConfigRestoreOrderArgs.builder()
                    .groupKindDependencies(                    
                        RestorePlanRestoreConfigRestoreOrderGroupKindDependencyArgs.builder()
                            .satisfying(RestorePlanRestoreConfigRestoreOrderGroupKindDependencySatisfyingArgs.builder()
                                .resourceGroup("stable.example.com")
                                .resourceKind("kindA")
                                .build())
                            .requiring(RestorePlanRestoreConfigRestoreOrderGroupKindDependencyRequiringArgs.builder()
                                .resourceGroup("stable.example.com")
                                .resourceKind("kindB")
                                .build())
                            .build(),
                        RestorePlanRestoreConfigRestoreOrderGroupKindDependencyArgs.builder()
                            .satisfying(RestorePlanRestoreConfigRestoreOrderGroupKindDependencySatisfyingArgs.builder()
                                .resourceGroup("stable.example.com")
                                .resourceKind("kindB")
                                .build())
                            .requiring(RestorePlanRestoreConfigRestoreOrderGroupKindDependencyRequiringArgs.builder()
                                .resourceGroup("stable.example.com")
                                .resourceKind("kindC")
                                .build())
                            .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:container:Cluster
    properties:
      name: restore-order-cluster
      location: us-central1
      initialNodeCount: 1
      workloadIdentityConfig:
        workloadPool: my-project-name.svc.id.goog
      addonsConfig:
        gkeBackupAgentConfig:
          enabled: true
      deletionProtection: true
      network: default
      subnetwork: default
  basic:
    type: gcp:gkebackup:BackupPlan
    properties:
      name: restore-order
      cluster: ${primary.id}
      location: us-central1
      backupConfig:
        includeVolumeData: true
        includeSecrets: true
        allNamespaces: true
  restoreOrder:
    type: gcp:gkebackup:RestorePlan
    name: restore_order
    properties:
      name: restore-order
      location: us-central1
      backupPlan: ${basic.id}
      cluster: ${primary.id}
      restoreConfig:
        allNamespaces: true
        namespacedResourceRestoreMode: FAIL_ON_CONFLICT
        volumeDataRestorePolicy: RESTORE_VOLUME_DATA_FROM_BACKUP
        clusterResourceRestoreScope:
          allGroupKinds: true
        clusterResourceConflictPolicy: USE_EXISTING_VERSION
        restoreOrder:
          groupKindDependencies:
            - satisfying:
                resourceGroup: stable.example.com
                resourceKind: kindA
              requiring:
                resourceGroup: stable.example.com
                resourceKind: kindB
            - satisfying:
                resourceGroup: stable.example.com
                resourceKind: kindB
              requiring:
                resourceGroup: stable.example.com
                resourceKind: kindC

The restoreOrder property defines dependency relationships between resource types. Each groupKindDependency specifies a satisfying resource that must be restored before a requiring resource. In this configuration, kindA must be restored before kindB, and kindB before kindC. This ensures custom resources are created in the correct order to satisfy their dependencies.

Beyond these examples

These snippets focus on specific restore plan features: namespace and application-level scoping, conflict resolution strategies, and transformation rules and restore ordering. They’re intentionally minimal rather than full disaster recovery solutions.

The examples reference pre-existing infrastructure such as GKE clusters with Backup Agent addon enabled, and BackupPlans with completed backups. They focus on configuring the restore plan rather than provisioning the backup infrastructure.

To keep things focused, common restore patterns are omitted, including:

  • Volume restoration policy variations (NO_VOLUME_DATA_RESTORATION, volumeDataRestorePolicyBindings)
  • Cluster resource filtering (excludedGroupKinds, excludedNamespaces)
  • Labels and descriptions for organizational metadata
  • Complex transformation rules (COPY operations, multiple field actions)

These omissions are intentional: the goal is to illustrate how each restore feature is wired, not provide drop-in disaster recovery modules. See the GKE Backup RestorePlan resource reference for all available configuration options.

Let's configure GCP GKE Backup Restore Plans

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Configuration & Immutability
What properties can't I change after creating a restore plan?
The backupPlan, cluster, location, name, and project properties are immutable. Changes to these require replacing the resource.
What's the difference between labels and effectiveLabels?
The labels field is non-authoritative and only manages labels in your configuration. Use effectiveLabels to see all labels on the resource, including those set by other clients.
Namespace & Application Selection
What are my options for selecting which namespaces to restore?

You have four options in restoreConfig:

  1. All namespaces - Set allNamespaces: true
  2. Specific namespaces - Use selectedNamespaces with a list
  3. Exclude namespaces - Use excludedNamespaces to restore all except specified ones
  4. No namespaces - Set noNamespaces: true to restore only cluster-scoped resources
How do I restore only a specific application instead of entire namespaces?
Use selectedApplications in restoreConfig with namespacedNames specifying the application name and namespace.
Conflict Resolution
What are the different conflict resolution modes for namespaced resources?

The namespacedResourceRestoreMode has three options:

  1. FAIL_ON_CONFLICT - Restore fails if resources already exist
  2. DELETE_AND_RESTORE - Deletes existing resources before restoring (useful for rollbacks)
  3. MERGE_SKIP_ON_CONFLICT - Merges with existing resources, skipping conflicts (GitOps mode)
How are conflicts handled for cluster-scoped resources?
Use clusterResourceConflictPolicy in restoreConfig. The common option is USE_EXISTING_VERSION, which keeps existing cluster resources instead of overwriting them.
Volume Data Restoration
What are my options for restoring volume data?

The volumeDataRestorePolicy has three options:

  1. RESTORE_VOLUME_DATA_FROM_BACKUP - Restores volume data from the backup
  2. REUSE_VOLUME_HANDLE_FROM_BACKUP - Reuses the original volume handle without copying data
  3. NO_VOLUME_DATA_RESTORATION - Skips volume data restoration entirely
Can I use different volume restore policies for different volume types?
Yes, use volumeDataRestorePolicyBindings to override the default policy for specific volume types like GCE_PERSISTENT_DISK.
Cluster Resource Scope
How do I control which cluster-scoped resources are restored?

Configure clusterResourceRestoreScope with one of these options:

  1. allGroupKinds: true - Restore all cluster resources
  2. selectedGroupKinds - Restore only specified resource kinds (e.g., CustomResourceDefinition, StorageClass)
  3. excludedGroupKinds - Restore all except specified kinds
  4. noGroupKinds: true - Skip cluster resources entirely
Advanced Features
How do I rename or move resources during restore?
Use transformationRules with field actions. For example, to rename a namespace from ns1 to ns2, create two rules: one to REPLACE /metadata/name on the Namespace resource, and another to REPLACE /metadata/namespace on all resources in ns1.
Can I control the order in which resources are restored?
Yes, configure restoreOrder with groupKindDependencies to specify that certain resource kinds must be restored before others (e.g., kindA before kindB before kindC).

Using a different cloud?

Explore containers guides for other cloud providers: