Create GCP GKE Backup Plans

The gcp:gkebackup/backupPlan:BackupPlan resource, part of the Pulumi GCP provider, defines a GKE backup plan that controls what gets backed up, when backups run, and how long they’re retained. This guide focuses on four capabilities: namespace and application selection, customer-managed encryption, automated scheduling and retention, and RPO targets with exclusion windows.

Backup plans require GKE clusters with the Backup for GKE addon enabled and may reference Cloud KMS keys for encryption. The examples are intentionally small. Combine them with your own cluster configuration and retention requirements.

Back up all namespaces with volume data

Most GKE backup deployments start with a plan that captures all cluster namespaces, including persistent volume data and secrets.

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

const primary = new gcp.container.Cluster("primary", {
    name: "basic-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: "basic-plan",
    cluster: primary.id,
    location: "us-central1",
    backupConfig: {
        includeVolumeData: true,
        includeSecrets: true,
        allNamespaces: true,
    },
});
import pulumi
import pulumi_gcp as gcp

primary = gcp.container.Cluster("primary",
    name="basic-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="basic-plan",
    cluster=primary.id,
    location="us-central1",
    backup_config={
        "include_volume_data": True,
        "include_secrets": True,
        "all_namespaces": 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("basic-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
		}
		_, err = gkebackup.NewBackupPlan(ctx, "basic", &gkebackup.BackupPlanArgs{
			Name:     pulumi.String("basic-plan"),
			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
		}
		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 = "basic-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 = "basic-plan",
        Cluster = primary.Id,
        Location = "us-central1",
        BackupConfig = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigArgs
        {
            IncludeVolumeData = true,
            IncludeSecrets = true,
            AllNamespaces = 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 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("basic-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("basic-plan")
            .cluster(primary.id())
            .location("us-central1")
            .backupConfig(BackupPlanBackupConfigArgs.builder()
                .includeVolumeData(true)
                .includeSecrets(true)
                .allNamespaces(true)
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:container:Cluster
    properties:
      name: basic-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: basic-plan
      cluster: ${primary.id}
      location: us-central1
      backupConfig:
        includeVolumeData: true
        includeSecrets: true
        allNamespaces: true

The backupConfig property defines what gets included in each backup. Setting allNamespaces to true captures every namespace in the cluster. The includeVolumeData and includeSecrets flags control whether persistent volumes and Kubernetes secrets are included in the backup snapshot.

Encrypt backups with customer-managed keys

Organizations with compliance requirements often need to control encryption keys for backup data rather than relying on Google-managed encryption.

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

const primary = new gcp.container.Cluster("primary", {
    name: "cmek-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 keyRing = new gcp.kms.KeyRing("key_ring", {
    name: "backup-key",
    location: "us-central1",
});
const cryptoKey = new gcp.kms.CryptoKey("crypto_key", {
    name: "backup-key",
    keyRing: keyRing.id,
});
const cmek = new gcp.gkebackup.BackupPlan("cmek", {
    name: "cmek-plan",
    cluster: primary.id,
    location: "us-central1",
    backupConfig: {
        includeVolumeData: true,
        includeSecrets: true,
        selectedNamespaces: {
            namespaces: [
                "default",
                "test",
            ],
        },
        encryptionKey: {
            gcpKmsEncryptionKey: cryptoKey.id,
        },
    },
});
import pulumi
import pulumi_gcp as gcp

primary = gcp.container.Cluster("primary",
    name="cmek-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")
key_ring = gcp.kms.KeyRing("key_ring",
    name="backup-key",
    location="us-central1")
crypto_key = gcp.kms.CryptoKey("crypto_key",
    name="backup-key",
    key_ring=key_ring.id)
cmek = gcp.gkebackup.BackupPlan("cmek",
    name="cmek-plan",
    cluster=primary.id,
    location="us-central1",
    backup_config={
        "include_volume_data": True,
        "include_secrets": True,
        "selected_namespaces": {
            "namespaces": [
                "default",
                "test",
            ],
        },
        "encryption_key": {
            "gcp_kms_encryption_key": crypto_key.id,
        },
    })
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-gcp/sdk/v9/go/gcp/kms"
	"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("cmek-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
		}
		keyRing, err := kms.NewKeyRing(ctx, "key_ring", &kms.KeyRingArgs{
			Name:     pulumi.String("backup-key"),
			Location: pulumi.String("us-central1"),
		})
		if err != nil {
			return err
		}
		cryptoKey, err := kms.NewCryptoKey(ctx, "crypto_key", &kms.CryptoKeyArgs{
			Name:    pulumi.String("backup-key"),
			KeyRing: keyRing.ID(),
		})
		if err != nil {
			return err
		}
		_, err = gkebackup.NewBackupPlan(ctx, "cmek", &gkebackup.BackupPlanArgs{
			Name:     pulumi.String("cmek-plan"),
			Cluster:  primary.ID(),
			Location: pulumi.String("us-central1"),
			BackupConfig: &gkebackup.BackupPlanBackupConfigArgs{
				IncludeVolumeData: pulumi.Bool(true),
				IncludeSecrets:    pulumi.Bool(true),
				SelectedNamespaces: &gkebackup.BackupPlanBackupConfigSelectedNamespacesArgs{
					Namespaces: pulumi.StringArray{
						pulumi.String("default"),
						pulumi.String("test"),
					},
				},
				EncryptionKey: &gkebackup.BackupPlanBackupConfigEncryptionKeyArgs{
					GcpKmsEncryptionKey: cryptoKey.ID(),
				},
			},
		})
		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 = "cmek-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 keyRing = new Gcp.Kms.KeyRing("key_ring", new()
    {
        Name = "backup-key",
        Location = "us-central1",
    });

    var cryptoKey = new Gcp.Kms.CryptoKey("crypto_key", new()
    {
        Name = "backup-key",
        KeyRing = keyRing.Id,
    });

    var cmek = new Gcp.GkeBackup.BackupPlan("cmek", new()
    {
        Name = "cmek-plan",
        Cluster = primary.Id,
        Location = "us-central1",
        BackupConfig = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigArgs
        {
            IncludeVolumeData = true,
            IncludeSecrets = true,
            SelectedNamespaces = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigSelectedNamespacesArgs
            {
                Namespaces = new[]
                {
                    "default",
                    "test",
                },
            },
            EncryptionKey = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigEncryptionKeyArgs
            {
                GcpKmsEncryptionKey = cryptoKey.Id,
            },
        },
    });

});
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.kms.KeyRing;
import com.pulumi.gcp.kms.KeyRingArgs;
import com.pulumi.gcp.kms.CryptoKey;
import com.pulumi.gcp.kms.CryptoKeyArgs;
import com.pulumi.gcp.gkebackup.BackupPlan;
import com.pulumi.gcp.gkebackup.BackupPlanArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupConfigArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupConfigSelectedNamespacesArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupConfigEncryptionKeyArgs;
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("cmek-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 keyRing = new KeyRing("keyRing", KeyRingArgs.builder()
            .name("backup-key")
            .location("us-central1")
            .build());

        var cryptoKey = new CryptoKey("cryptoKey", CryptoKeyArgs.builder()
            .name("backup-key")
            .keyRing(keyRing.id())
            .build());

        var cmek = new BackupPlan("cmek", BackupPlanArgs.builder()
            .name("cmek-plan")
            .cluster(primary.id())
            .location("us-central1")
            .backupConfig(BackupPlanBackupConfigArgs.builder()
                .includeVolumeData(true)
                .includeSecrets(true)
                .selectedNamespaces(BackupPlanBackupConfigSelectedNamespacesArgs.builder()
                    .namespaces(                    
                        "default",
                        "test")
                    .build())
                .encryptionKey(BackupPlanBackupConfigEncryptionKeyArgs.builder()
                    .gcpKmsEncryptionKey(cryptoKey.id())
                    .build())
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:container:Cluster
    properties:
      name: cmek-cluster
      location: us-central1
      initialNodeCount: 1
      workloadIdentityConfig:
        workloadPool: my-project-name.svc.id.goog
      addonsConfig:
        gkeBackupAgentConfig:
          enabled: true
      deletionProtection: true
      network: default
      subnetwork: default
  cmek:
    type: gcp:gkebackup:BackupPlan
    properties:
      name: cmek-plan
      cluster: ${primary.id}
      location: us-central1
      backupConfig:
        includeVolumeData: true
        includeSecrets: true
        selectedNamespaces:
          namespaces:
            - default
            - test
        encryptionKey:
          gcpKmsEncryptionKey: ${cryptoKey.id}
  cryptoKey:
    type: gcp:kms:CryptoKey
    name: crypto_key
    properties:
      name: backup-key
      keyRing: ${keyRing.id}
  keyRing:
    type: gcp:kms:KeyRing
    name: key_ring
    properties:
      name: backup-key
      location: us-central1

The encryptionKey property points to a Cloud KMS CryptoKey that encrypts backup data at rest. Instead of allNamespaces, this configuration uses selectedNamespaces to back up only the “default” and “test” namespaces. The backup service account needs the cryptoKeyEncrypterDecrypter role on the specified key.

Filter namespaces by label selectors

Teams managing multi-tenant clusters often organize workloads with namespace labels, allowing backup plans to target specific environments or teams.

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

const primary = new gcp.container.Cluster("primary", {
    name: "nslabels-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 nslabels = new gcp.gkebackup.BackupPlan("nslabels", {
    name: "nslabels-plan",
    cluster: primary.id,
    location: "us-central1",
    backupConfig: {
        includeVolumeData: true,
        includeSecrets: true,
        selectedNamespaceLabels: {
            resourceLabels: [{
                key: "key1",
                value: "value1",
            }],
        },
    },
});
import pulumi
import pulumi_gcp as gcp

primary = gcp.container.Cluster("primary",
    name="nslabels-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")
nslabels = gcp.gkebackup.BackupPlan("nslabels",
    name="nslabels-plan",
    cluster=primary.id,
    location="us-central1",
    backup_config={
        "include_volume_data": True,
        "include_secrets": True,
        "selected_namespace_labels": {
            "resource_labels": [{
                "key": "key1",
                "value": "value1",
            }],
        },
    })
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("nslabels-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
		}
		_, err = gkebackup.NewBackupPlan(ctx, "nslabels", &gkebackup.BackupPlanArgs{
			Name:     pulumi.String("nslabels-plan"),
			Cluster:  primary.ID(),
			Location: pulumi.String("us-central1"),
			BackupConfig: &gkebackup.BackupPlanBackupConfigArgs{
				IncludeVolumeData: pulumi.Bool(true),
				IncludeSecrets:    pulumi.Bool(true),
				SelectedNamespaceLabels: &gkebackup.BackupPlanBackupConfigSelectedNamespaceLabelsArgs{
					ResourceLabels: gkebackup.BackupPlanBackupConfigSelectedNamespaceLabelsResourceLabelArray{
						&gkebackup.BackupPlanBackupConfigSelectedNamespaceLabelsResourceLabelArgs{
							Key:   pulumi.String("key1"),
							Value: pulumi.String("value1"),
						},
					},
				},
			},
		})
		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 = "nslabels-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 nslabels = new Gcp.GkeBackup.BackupPlan("nslabels", new()
    {
        Name = "nslabels-plan",
        Cluster = primary.Id,
        Location = "us-central1",
        BackupConfig = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigArgs
        {
            IncludeVolumeData = true,
            IncludeSecrets = true,
            SelectedNamespaceLabels = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigSelectedNamespaceLabelsArgs
            {
                ResourceLabels = new[]
                {
                    new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigSelectedNamespaceLabelsResourceLabelArgs
                    {
                        Key = "key1",
                        Value = "value1",
                    },
                },
            },
        },
    });

});
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.inputs.BackupPlanBackupConfigSelectedNamespaceLabelsArgs;
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("nslabels-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 nslabels = new BackupPlan("nslabels", BackupPlanArgs.builder()
            .name("nslabels-plan")
            .cluster(primary.id())
            .location("us-central1")
            .backupConfig(BackupPlanBackupConfigArgs.builder()
                .includeVolumeData(true)
                .includeSecrets(true)
                .selectedNamespaceLabels(BackupPlanBackupConfigSelectedNamespaceLabelsArgs.builder()
                    .resourceLabels(BackupPlanBackupConfigSelectedNamespaceLabelsResourceLabelArgs.builder()
                        .key("key1")
                        .value("value1")
                        .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:container:Cluster
    properties:
      name: nslabels-cluster
      location: us-central1
      initialNodeCount: 1
      workloadIdentityConfig:
        workloadPool: my-project-name.svc.id.goog
      addonsConfig:
        gkeBackupAgentConfig:
          enabled: true
      deletionProtection: true
      network: default
      subnetwork: default
  nslabels:
    type: gcp:gkebackup:BackupPlan
    properties:
      name: nslabels-plan
      cluster: ${primary.id}
      location: us-central1
      backupConfig:
        includeVolumeData: true
        includeSecrets: true
        selectedNamespaceLabels:
          resourceLabels:
            - key: key1
              value: value1

The selectedNamespaceLabels property filters namespaces by label criteria. Only namespaces with matching resourceLabels are included in backups. This approach works well when you organize workloads by environment (dev, staging, prod) or team ownership using namespace labels.

Schedule backups with retention policies

Production environments typically automate backup creation on a schedule and enforce retention rules to balance recovery needs with storage costs.

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

const primary = new gcp.container.Cluster("primary", {
    name: "full-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 full = new gcp.gkebackup.BackupPlan("full", {
    name: "full-plan",
    cluster: primary.id,
    location: "us-central1",
    retentionPolicy: {
        backupDeleteLockDays: 30,
        backupRetainDays: 180,
    },
    backupSchedule: {
        cronSchedule: "0 9 * * 1",
    },
    backupConfig: {
        includeVolumeData: true,
        includeSecrets: true,
        selectedApplications: {
            namespacedNames: [
                {
                    name: "app1",
                    namespace: "ns1",
                },
                {
                    name: "app2",
                    namespace: "ns2",
                },
            ],
        },
    },
});
import pulumi
import pulumi_gcp as gcp

primary = gcp.container.Cluster("primary",
    name="full-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")
full = gcp.gkebackup.BackupPlan("full",
    name="full-plan",
    cluster=primary.id,
    location="us-central1",
    retention_policy={
        "backup_delete_lock_days": 30,
        "backup_retain_days": 180,
    },
    backup_schedule={
        "cron_schedule": "0 9 * * 1",
    },
    backup_config={
        "include_volume_data": True,
        "include_secrets": True,
        "selected_applications": {
            "namespaced_names": [
                {
                    "name": "app1",
                    "namespace": "ns1",
                },
                {
                    "name": "app2",
                    "namespace": "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("full-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
		}
		_, err = gkebackup.NewBackupPlan(ctx, "full", &gkebackup.BackupPlanArgs{
			Name:     pulumi.String("full-plan"),
			Cluster:  primary.ID(),
			Location: pulumi.String("us-central1"),
			RetentionPolicy: &gkebackup.BackupPlanRetentionPolicyArgs{
				BackupDeleteLockDays: pulumi.Int(30),
				BackupRetainDays:     pulumi.Int(180),
			},
			BackupSchedule: &gkebackup.BackupPlanBackupScheduleArgs{
				CronSchedule: pulumi.String("0 9 * * 1"),
			},
			BackupConfig: &gkebackup.BackupPlanBackupConfigArgs{
				IncludeVolumeData: pulumi.Bool(true),
				IncludeSecrets:    pulumi.Bool(true),
				SelectedApplications: &gkebackup.BackupPlanBackupConfigSelectedApplicationsArgs{
					NamespacedNames: gkebackup.BackupPlanBackupConfigSelectedApplicationsNamespacedNameArray{
						&gkebackup.BackupPlanBackupConfigSelectedApplicationsNamespacedNameArgs{
							Name:      pulumi.String("app1"),
							Namespace: pulumi.String("ns1"),
						},
						&gkebackup.BackupPlanBackupConfigSelectedApplicationsNamespacedNameArgs{
							Name:      pulumi.String("app2"),
							Namespace: 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 = "full-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 full = new Gcp.GkeBackup.BackupPlan("full", new()
    {
        Name = "full-plan",
        Cluster = primary.Id,
        Location = "us-central1",
        RetentionPolicy = new Gcp.GkeBackup.Inputs.BackupPlanRetentionPolicyArgs
        {
            BackupDeleteLockDays = 30,
            BackupRetainDays = 180,
        },
        BackupSchedule = new Gcp.GkeBackup.Inputs.BackupPlanBackupScheduleArgs
        {
            CronSchedule = "0 9 * * 1",
        },
        BackupConfig = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigArgs
        {
            IncludeVolumeData = true,
            IncludeSecrets = true,
            SelectedApplications = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigSelectedApplicationsArgs
            {
                NamespacedNames = new[]
                {
                    new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigSelectedApplicationsNamespacedNameArgs
                    {
                        Name = "app1",
                        Namespace = "ns1",
                    },
                    new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigSelectedApplicationsNamespacedNameArgs
                    {
                        Name = "app2",
                        Namespace = "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.BackupPlanRetentionPolicyArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupScheduleArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupConfigArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupConfigSelectedApplicationsArgs;
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("full-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 full = new BackupPlan("full", BackupPlanArgs.builder()
            .name("full-plan")
            .cluster(primary.id())
            .location("us-central1")
            .retentionPolicy(BackupPlanRetentionPolicyArgs.builder()
                .backupDeleteLockDays(30)
                .backupRetainDays(180)
                .build())
            .backupSchedule(BackupPlanBackupScheduleArgs.builder()
                .cronSchedule("0 9 * * 1")
                .build())
            .backupConfig(BackupPlanBackupConfigArgs.builder()
                .includeVolumeData(true)
                .includeSecrets(true)
                .selectedApplications(BackupPlanBackupConfigSelectedApplicationsArgs.builder()
                    .namespacedNames(                    
                        BackupPlanBackupConfigSelectedApplicationsNamespacedNameArgs.builder()
                            .name("app1")
                            .namespace("ns1")
                            .build(),
                        BackupPlanBackupConfigSelectedApplicationsNamespacedNameArgs.builder()
                            .name("app2")
                            .namespace("ns2")
                            .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:container:Cluster
    properties:
      name: full-cluster
      location: us-central1
      initialNodeCount: 1
      workloadIdentityConfig:
        workloadPool: my-project-name.svc.id.goog
      addonsConfig:
        gkeBackupAgentConfig:
          enabled: true
      deletionProtection: true
      network: default
      subnetwork: default
  full:
    type: gcp:gkebackup:BackupPlan
    properties:
      name: full-plan
      cluster: ${primary.id}
      location: us-central1
      retentionPolicy:
        backupDeleteLockDays: 30
        backupRetainDays: 180
      backupSchedule:
        cronSchedule: 0 9 * * 1
      backupConfig:
        includeVolumeData: true
        includeSecrets: true
        selectedApplications:
          namespacedNames:
            - name: app1
              namespace: ns1
            - name: app2
              namespace: ns2

The backupSchedule property defines when backups run using cron syntax. The retentionPolicy controls how long backups are kept (backupRetainDays) and prevents accidental deletion for a specified period (backupDeleteLockDays). This configuration targets specific applications using selectedApplications with namespacedNames, backing up only “app1” in “ns1” and “app2” in “ns2”.

Define RPO targets with exclusion windows

Applications with strict recovery point objectives need frequent backups but may require blackout windows during maintenance or high-traffic periods.

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

const primary = new gcp.container.Cluster("primary", {
    name: "rpo-daily-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 rpoDailyWindow = new gcp.gkebackup.BackupPlan("rpo_daily_window", {
    name: "rpo-daily-window",
    cluster: primary.id,
    location: "us-central1",
    retentionPolicy: {
        backupDeleteLockDays: 30,
        backupRetainDays: 180,
    },
    backupSchedule: {
        paused: true,
        rpoConfig: {
            targetRpoMinutes: 1440,
            exclusionWindows: [
                {
                    startTime: {
                        hours: 12,
                    },
                    duration: "7200s",
                    daily: true,
                },
                {
                    startTime: {
                        hours: 8,
                        minutes: 40,
                        seconds: 1,
                        nanos: 100,
                    },
                    duration: "3600s",
                    singleOccurrenceDate: {
                        year: 2024,
                        month: 3,
                        day: 16,
                    },
                },
            ],
        },
    },
    backupConfig: {
        includeVolumeData: true,
        includeSecrets: true,
        allNamespaces: true,
    },
});
import pulumi
import pulumi_gcp as gcp

primary = gcp.container.Cluster("primary",
    name="rpo-daily-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")
rpo_daily_window = gcp.gkebackup.BackupPlan("rpo_daily_window",
    name="rpo-daily-window",
    cluster=primary.id,
    location="us-central1",
    retention_policy={
        "backup_delete_lock_days": 30,
        "backup_retain_days": 180,
    },
    backup_schedule={
        "paused": True,
        "rpo_config": {
            "target_rpo_minutes": 1440,
            "exclusion_windows": [
                {
                    "start_time": {
                        "hours": 12,
                    },
                    "duration": "7200s",
                    "daily": True,
                },
                {
                    "start_time": {
                        "hours": 8,
                        "minutes": 40,
                        "seconds": 1,
                        "nanos": 100,
                    },
                    "duration": "3600s",
                    "single_occurrence_date": {
                        "year": 2024,
                        "month": 3,
                        "day": 16,
                    },
                },
            ],
        },
    },
    backup_config={
        "include_volume_data": True,
        "include_secrets": True,
        "all_namespaces": 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("rpo-daily-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
		}
		_, err = gkebackup.NewBackupPlan(ctx, "rpo_daily_window", &gkebackup.BackupPlanArgs{
			Name:     pulumi.String("rpo-daily-window"),
			Cluster:  primary.ID(),
			Location: pulumi.String("us-central1"),
			RetentionPolicy: &gkebackup.BackupPlanRetentionPolicyArgs{
				BackupDeleteLockDays: pulumi.Int(30),
				BackupRetainDays:     pulumi.Int(180),
			},
			BackupSchedule: &gkebackup.BackupPlanBackupScheduleArgs{
				Paused: pulumi.Bool(true),
				RpoConfig: &gkebackup.BackupPlanBackupScheduleRpoConfigArgs{
					TargetRpoMinutes: pulumi.Int(1440),
					ExclusionWindows: gkebackup.BackupPlanBackupScheduleRpoConfigExclusionWindowArray{
						&gkebackup.BackupPlanBackupScheduleRpoConfigExclusionWindowArgs{
							StartTime: &gkebackup.BackupPlanBackupScheduleRpoConfigExclusionWindowStartTimeArgs{
								Hours: pulumi.Int(12),
							},
							Duration: pulumi.String("7200s"),
							Daily:    pulumi.Bool(true),
						},
						&gkebackup.BackupPlanBackupScheduleRpoConfigExclusionWindowArgs{
							StartTime: &gkebackup.BackupPlanBackupScheduleRpoConfigExclusionWindowStartTimeArgs{
								Hours:   pulumi.Int(8),
								Minutes: pulumi.Int(40),
								Seconds: pulumi.Int(1),
								Nanos:   pulumi.Int(100),
							},
							Duration: pulumi.String("3600s"),
							SingleOccurrenceDate: &gkebackup.BackupPlanBackupScheduleRpoConfigExclusionWindowSingleOccurrenceDateArgs{
								Year:  pulumi.Int(2024),
								Month: pulumi.Int(3),
								Day:   pulumi.Int(16),
							},
						},
					},
				},
			},
			BackupConfig: &gkebackup.BackupPlanBackupConfigArgs{
				IncludeVolumeData: pulumi.Bool(true),
				IncludeSecrets:    pulumi.Bool(true),
				AllNamespaces:     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 = "rpo-daily-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 rpoDailyWindow = new Gcp.GkeBackup.BackupPlan("rpo_daily_window", new()
    {
        Name = "rpo-daily-window",
        Cluster = primary.Id,
        Location = "us-central1",
        RetentionPolicy = new Gcp.GkeBackup.Inputs.BackupPlanRetentionPolicyArgs
        {
            BackupDeleteLockDays = 30,
            BackupRetainDays = 180,
        },
        BackupSchedule = new Gcp.GkeBackup.Inputs.BackupPlanBackupScheduleArgs
        {
            Paused = true,
            RpoConfig = new Gcp.GkeBackup.Inputs.BackupPlanBackupScheduleRpoConfigArgs
            {
                TargetRpoMinutes = 1440,
                ExclusionWindows = new[]
                {
                    new Gcp.GkeBackup.Inputs.BackupPlanBackupScheduleRpoConfigExclusionWindowArgs
                    {
                        StartTime = new Gcp.GkeBackup.Inputs.BackupPlanBackupScheduleRpoConfigExclusionWindowStartTimeArgs
                        {
                            Hours = 12,
                        },
                        Duration = "7200s",
                        Daily = true,
                    },
                    new Gcp.GkeBackup.Inputs.BackupPlanBackupScheduleRpoConfigExclusionWindowArgs
                    {
                        StartTime = new Gcp.GkeBackup.Inputs.BackupPlanBackupScheduleRpoConfigExclusionWindowStartTimeArgs
                        {
                            Hours = 8,
                            Minutes = 40,
                            Seconds = 1,
                            Nanos = 100,
                        },
                        Duration = "3600s",
                        SingleOccurrenceDate = new Gcp.GkeBackup.Inputs.BackupPlanBackupScheduleRpoConfigExclusionWindowSingleOccurrenceDateArgs
                        {
                            Year = 2024,
                            Month = 3,
                            Day = 16,
                        },
                    },
                },
            },
        },
        BackupConfig = new Gcp.GkeBackup.Inputs.BackupPlanBackupConfigArgs
        {
            IncludeVolumeData = true,
            IncludeSecrets = true,
            AllNamespaces = 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.BackupPlanRetentionPolicyArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupScheduleArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupScheduleRpoConfigArgs;
import com.pulumi.gcp.gkebackup.inputs.BackupPlanBackupConfigArgs;
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("rpo-daily-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 rpoDailyWindow = new BackupPlan("rpoDailyWindow", BackupPlanArgs.builder()
            .name("rpo-daily-window")
            .cluster(primary.id())
            .location("us-central1")
            .retentionPolicy(BackupPlanRetentionPolicyArgs.builder()
                .backupDeleteLockDays(30)
                .backupRetainDays(180)
                .build())
            .backupSchedule(BackupPlanBackupScheduleArgs.builder()
                .paused(true)
                .rpoConfig(BackupPlanBackupScheduleRpoConfigArgs.builder()
                    .targetRpoMinutes(1440)
                    .exclusionWindows(                    
                        BackupPlanBackupScheduleRpoConfigExclusionWindowArgs.builder()
                            .startTime(BackupPlanBackupScheduleRpoConfigExclusionWindowStartTimeArgs.builder()
                                .hours(12)
                                .build())
                            .duration("7200s")
                            .daily(true)
                            .build(),
                        BackupPlanBackupScheduleRpoConfigExclusionWindowArgs.builder()
                            .startTime(BackupPlanBackupScheduleRpoConfigExclusionWindowStartTimeArgs.builder()
                                .hours(8)
                                .minutes(40)
                                .seconds(1)
                                .nanos(100)
                                .build())
                            .duration("3600s")
                            .singleOccurrenceDate(BackupPlanBackupScheduleRpoConfigExclusionWindowSingleOccurrenceDateArgs.builder()
                                .year(2024)
                                .month(3)
                                .day(16)
                                .build())
                            .build())
                    .build())
                .build())
            .backupConfig(BackupPlanBackupConfigArgs.builder()
                .includeVolumeData(true)
                .includeSecrets(true)
                .allNamespaces(true)
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:container:Cluster
    properties:
      name: rpo-daily-cluster
      location: us-central1
      initialNodeCount: 1
      workloadIdentityConfig:
        workloadPool: my-project-name.svc.id.goog
      addonsConfig:
        gkeBackupAgentConfig:
          enabled: true
      deletionProtection: true
      network: default
      subnetwork: default
  rpoDailyWindow:
    type: gcp:gkebackup:BackupPlan
    name: rpo_daily_window
    properties:
      name: rpo-daily-window
      cluster: ${primary.id}
      location: us-central1
      retentionPolicy:
        backupDeleteLockDays: 30
        backupRetainDays: 180
      backupSchedule:
        paused: true
        rpoConfig:
          targetRpoMinutes: 1440
          exclusionWindows:
            - startTime:
                hours: 12
              duration: 7200s
              daily: true
            - startTime:
                hours: 8
                minutes: 40
                seconds: 1
                nanos: 100
              duration: 3600s
              singleOccurrenceDate:
                year: 2024
                month: 3
                day: 16
      backupConfig:
        includeVolumeData: true
        includeSecrets: true
        allNamespaces: true

The rpoConfig property sets a target recovery point objective in minutes (1440 minutes = 24 hours). Backup for GKE automatically schedules backups to meet this target. The exclusionWindows array defines periods when backups should not run: daily recurring windows (daily: true) and one-time windows (singleOccurrenceDate). Each window specifies a startTime and duration in seconds.

Beyond these examples

These snippets focus on specific backup plan features: namespace and application selection strategies, encryption, retention, and scheduling, and RPO-based backup automation. They’re intentionally minimal rather than full disaster recovery solutions.

The examples may reference pre-existing infrastructure such as GKE clusters with Backup for GKE addon enabled, Cloud KMS keys for customer-managed encryption, and Workload Identity configuration on clusters. They focus on configuring the backup plan rather than provisioning the underlying cluster infrastructure.

To keep things focused, common backup plan patterns are omitted, including:

  • Deactivation and plan lifecycle (deactivated property)
  • Permissive mode for partial backup success
  • Autopilot cluster-specific configuration
  • Weekly exclusion windows (daysOfWeek)

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

Let's create GCP GKE Backup 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 & Setup
Do I need to enable anything on my GKE cluster before creating a backup plan?
Yes, you must enable the GKE Backup agent addon on your cluster. Set addonsConfig.gkeBackupAgentConfig.enabled to true in your cluster configuration.
What properties can't I change after creating a backup plan?
The cluster, location, name, and project properties are immutable. Changes to these require recreating the BackupPlan resource.
Why aren't my labels showing up on the backup plan?
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 or services.
Backup Scope & Selection
How do I select which namespaces to back up?

You have four options in backupConfig:

  1. All namespaces - Set allNamespaces to true
  2. Specific namespaces - Use selectedNamespaces with a list of namespace names
  3. Label-based - Use selectedNamespaceLabels to match namespaces by labels
  4. Specific applications - Use selectedApplications with namespaced app names
What does permissiveMode do in the backup configuration?
Setting permissiveMode to true allows backups to proceed even if some resources fail to back up, rather than failing the entire backup operation.
Scheduling & Retention
What's the difference between cronSchedule and rpoConfig for scheduling backups?
cronSchedule uses cron syntax for fixed schedules (e.g., "0 9 * * 1" for weekly backups). rpoConfig uses targetRpoMinutes to define recovery point objectives and supports exclusionWindows for more flexible scheduling.
How do I exclude certain time windows from automatic backups?
Configure exclusionWindows in rpoConfig with startTime and duration. You can specify daily windows, weekly windows using daysOfWeek, or single-occurrence windows using singleOccurrenceDate.
How do I configure backup retention policies?
Use retentionPolicy to set backupDeleteLockDays (minimum retention period) and backupRetainDays (maximum retention period) for backups created by this plan.
Security & Encryption
How do I encrypt backups with customer-managed encryption keys?
Configure encryptionKey.gcpKmsEncryptionKey in backupConfig with your Cloud KMS CryptoKey ID.
Management & Lifecycle
What happens when I set deactivated to true?
Setting deactivated to true locks the BackupPlan, preventing all updates except deletes. It also prevents any new backups from being created, including scheduled backups. This is typically done before deletion.

Using a different cloud?

Explore containers guides for other cloud providers: