Configure GCP Bigtable Garbage Collection Policies

The gcp:bigtable/gCPolicy:GCPolicy resource, part of the Pulumi GCP provider, defines garbage collection policies for Bigtable column families, controlling when cells are automatically deleted based on age or version count. This guide focuses on three capabilities: age-based deletion, version-based deletion, and union/intersection logic for multi-condition policies.

GC policies attach to existing column families within Bigtable tables. The instance, table, and column family must exist before creating the policy. The examples are intentionally small. Combine them with your own table schemas and retention requirements.

Delete cells older than a specific age

Bigtable tables accumulate historical data over time. Teams often need to automatically remove cells that exceed a retention window to control storage costs and comply with data retention policies.

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

const instance = new gcp.bigtable.Instance("instance", {
    name: "tf-instance",
    clusters: [{
        clusterId: "tf-instance-cluster",
        numNodes: 3,
        storageType: "HDD",
    }],
});
const table = new gcp.bigtable.Table("table", {
    name: "tf-table",
    instanceName: instance.name,
    columnFamilies: [{
        family: "name",
    }],
});
const policy = new gcp.bigtable.GCPolicy("policy", {
    instanceName: instance.name,
    table: table.name,
    columnFamily: "name",
    deletionPolicy: "ABANDON",
    gcRules: `  {
    \\"rules\\": [
      {
        \\"max_age\\": \\"168h\\"
      }
    ]
  }
`,
});
import pulumi
import pulumi_gcp as gcp

instance = gcp.bigtable.Instance("instance",
    name="tf-instance",
    clusters=[{
        "cluster_id": "tf-instance-cluster",
        "num_nodes": 3,
        "storage_type": "HDD",
    }])
table = gcp.bigtable.Table("table",
    name="tf-table",
    instance_name=instance.name,
    column_families=[{
        "family": "name",
    }])
policy = gcp.bigtable.GCPolicy("policy",
    instance_name=instance.name,
    table=table.name,
    column_family="name",
    deletion_policy="ABANDON",
    gc_rules="""  {
    \"rules\": [
      {
        \"max_age\": \"168h\"
      }
    ]
  }
""")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		instance, err := bigtable.NewInstance(ctx, "instance", &bigtable.InstanceArgs{
			Name: pulumi.String("tf-instance"),
			Clusters: bigtable.InstanceClusterArray{
				&bigtable.InstanceClusterArgs{
					ClusterId:   pulumi.String("tf-instance-cluster"),
					NumNodes:    pulumi.Int(3),
					StorageType: pulumi.String("HDD"),
				},
			},
		})
		if err != nil {
			return err
		}
		table, err := bigtable.NewTable(ctx, "table", &bigtable.TableArgs{
			Name:         pulumi.String("tf-table"),
			InstanceName: instance.Name,
			ColumnFamilies: bigtable.TableColumnFamilyArray{
				&bigtable.TableColumnFamilyArgs{
					Family: pulumi.String("name"),
				},
			},
		})
		if err != nil {
			return err
		}
		_, err = bigtable.NewGCPolicy(ctx, "policy", &bigtable.GCPolicyArgs{
			InstanceName:   instance.Name,
			Table:          table.Name,
			ColumnFamily:   pulumi.String("name"),
			DeletionPolicy: pulumi.String("ABANDON"),
			GcRules: pulumi.String(`  {
    \"rules\": [
      {
        \"max_age\": \"168h\"
      }
    ]
  }
`),
		})
		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 instance = new Gcp.BigTable.Instance("instance", new()
    {
        Name = "tf-instance",
        Clusters = new[]
        {
            new Gcp.BigTable.Inputs.InstanceClusterArgs
            {
                ClusterId = "tf-instance-cluster",
                NumNodes = 3,
                StorageType = "HDD",
            },
        },
    });

    var table = new Gcp.BigTable.Table("table", new()
    {
        Name = "tf-table",
        InstanceName = instance.Name,
        ColumnFamilies = new[]
        {
            new Gcp.BigTable.Inputs.TableColumnFamilyArgs
            {
                Family = "name",
            },
        },
    });

    var policy = new Gcp.BigTable.GCPolicy("policy", new()
    {
        InstanceName = instance.Name,
        Table = table.Name,
        ColumnFamily = "name",
        DeletionPolicy = "ABANDON",
        GcRules = @"  {
    \""rules\"": [
      {
        \""max_age\"": \""168h\""
      }
    ]
  }
",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.bigtable.Instance;
import com.pulumi.gcp.bigtable.InstanceArgs;
import com.pulumi.gcp.bigtable.inputs.InstanceClusterArgs;
import com.pulumi.gcp.bigtable.Table;
import com.pulumi.gcp.bigtable.TableArgs;
import com.pulumi.gcp.bigtable.inputs.TableColumnFamilyArgs;
import com.pulumi.gcp.bigtable.GCPolicy;
import com.pulumi.gcp.bigtable.GCPolicyArgs;
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 instance = new Instance("instance", InstanceArgs.builder()
            .name("tf-instance")
            .clusters(InstanceClusterArgs.builder()
                .clusterId("tf-instance-cluster")
                .numNodes(3)
                .storageType("HDD")
                .build())
            .build());

        var table = new Table("table", TableArgs.builder()
            .name("tf-table")
            .instanceName(instance.name())
            .columnFamilies(TableColumnFamilyArgs.builder()
                .family("name")
                .build())
            .build());

        var policy = new GCPolicy("policy", GCPolicyArgs.builder()
            .instanceName(instance.name())
            .table(table.name())
            .columnFamily("name")
            .deletionPolicy("ABANDON")
            .gcRules("""
  {
    \"rules\": [
      {
        \"max_age\": \"168h\"
      }
    ]
  }
            """)
            .build());

    }
}
resources:
  instance:
    type: gcp:bigtable:Instance
    properties:
      name: tf-instance
      clusters:
        - clusterId: tf-instance-cluster
          numNodes: 3
          storageType: HDD
  table:
    type: gcp:bigtable:Table
    properties:
      name: tf-table
      instanceName: ${instance.name}
      columnFamilies:
        - family: name
  policy:
    type: gcp:bigtable:GCPolicy
    properties:
      instanceName: ${instance.name}
      table: ${table.name}
      columnFamily: name
      deletionPolicy: ABANDON
      gcRules: |2
          {
            \"rules\": [
              {
                \"max_age\": \"168h\"
              }
            ]
          }

The gcRules property accepts a JSON string that defines the deletion criteria. Here, max_age specifies that cells older than 168 hours (7 days) are eligible for garbage collection. The deletionPolicy set to “ABANDON” allows the resource to be removed from Pulumi state without deleting the policy from Bigtable, which is necessary for replicated instances where policy deletion is restricted.

Combine age and version limits with union logic

Some workloads need flexible retention where cells are deleted if they meet any of several criteria, such as exceeding either an age threshold or a version count.

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

const policy = new gcp.bigtable.GCPolicy("policy", {
    instanceName: instance.name,
    table: table.name,
    columnFamily: "name",
    deletionPolicy: "ABANDON",
    gcRules: `  {
    \\"mode\\": \\"union\\",
    \\"rules\\": [
      {
        \\"max_age\\": \\"168h\\"
      },
      {
        \\"max_version\\": 10
      }
    ]
  }
`,
});
import pulumi
import pulumi_gcp as gcp

policy = gcp.bigtable.GCPolicy("policy",
    instance_name=instance["name"],
    table=table["name"],
    column_family="name",
    deletion_policy="ABANDON",
    gc_rules="""  {
    \"mode\": \"union\",
    \"rules\": [
      {
        \"max_age\": \"168h\"
      },
      {
        \"max_version\": 10
      }
    ]
  }
""")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := bigtable.NewGCPolicy(ctx, "policy", &bigtable.GCPolicyArgs{
			InstanceName:   pulumi.Any(instance.Name),
			Table:          pulumi.Any(table.Name),
			ColumnFamily:   pulumi.String("name"),
			DeletionPolicy: pulumi.String("ABANDON"),
			GcRules: pulumi.String(`  {
    \"mode\": \"union\",
    \"rules\": [
      {
        \"max_age\": \"168h\"
      },
      {
        \"max_version\": 10
      }
    ]
  }
`),
		})
		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 policy = new Gcp.BigTable.GCPolicy("policy", new()
    {
        InstanceName = instance.Name,
        Table = table.Name,
        ColumnFamily = "name",
        DeletionPolicy = "ABANDON",
        GcRules = @"  {
    \""mode\"": \""union\"",
    \""rules\"": [
      {
        \""max_age\"": \""168h\""
      },
      {
        \""max_version\"": 10
      }
    ]
  }
",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.bigtable.GCPolicy;
import com.pulumi.gcp.bigtable.GCPolicyArgs;
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 policy = new GCPolicy("policy", GCPolicyArgs.builder()
            .instanceName(instance.name())
            .table(table.name())
            .columnFamily("name")
            .deletionPolicy("ABANDON")
            .gcRules("""
  {
    \"mode\": \"union\",
    \"rules\": [
      {
        \"max_age\": \"168h\"
      },
      {
        \"max_version\": 10
      }
    ]
  }
            """)
            .build());

    }
}
resources:
  policy:
    type: gcp:bigtable:GCPolicy
    properties:
      instanceName: ${instance.name}
      table: ${table.name}
      columnFamily: name
      deletionPolicy: ABANDON
      gcRules: |2
          {
            \"mode\": \"union\",
            \"rules\": [
              {
                \"max_age\": \"168h\"
              },
              {
                \"max_version\": 10
              }
            ]
          }

The mode property set to “union” means cells are deleted when any sub-policy applies (OR logic). In this configuration, cells are removed if they’re older than 7 days OR if more than 10 versions exist. Union mode provides the most aggressive cleanup, applying whichever condition triggers first.

Build complex policies with nested union and intersection

Advanced retention scenarios require combining multiple conditions with both AND and OR logic, such as keeping only recent versions of recent data while allowing older data to persist longer.

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

const instance = new gcp.bigtable.Instance("instance", {
    name: "instance_name",
    clusters: [{
        clusterId: "cid",
        zone: "us-central1-b",
    }],
    instanceType: "DEVELOPMENT",
    deletionProtection: false,
});
const table = new gcp.bigtable.Table("table", {
    name: "your-table",
    instanceName: instance.id,
    columnFamilies: [{
        family: "cf1",
    }],
});
const policy = new gcp.bigtable.GCPolicy("policy", {
    instanceName: instance.id,
    table: table.name,
    columnFamily: "cf1",
    deletionPolicy: "ABANDON",
    gcRules: `  {
    \\"mode\\": \\"union\\",
    \\"rules\\": [
      {
        \\"max_age\\": \\"10h\\"
      },
      {
        \\"mode\\": \\"intersection\\",
        \\"rules\\": [
          {
            \\"max_age\\": \\"2h\\"
          },
          {
            \\"max_version\\": 2
          }
        ]
      }
    ]
  }
`,
});
import pulumi
import pulumi_gcp as gcp

instance = gcp.bigtable.Instance("instance",
    name="instance_name",
    clusters=[{
        "cluster_id": "cid",
        "zone": "us-central1-b",
    }],
    instance_type="DEVELOPMENT",
    deletion_protection=False)
table = gcp.bigtable.Table("table",
    name="your-table",
    instance_name=instance.id,
    column_families=[{
        "family": "cf1",
    }])
policy = gcp.bigtable.GCPolicy("policy",
    instance_name=instance.id,
    table=table.name,
    column_family="cf1",
    deletion_policy="ABANDON",
    gc_rules="""  {
    \"mode\": \"union\",
    \"rules\": [
      {
        \"max_age\": \"10h\"
      },
      {
        \"mode\": \"intersection\",
        \"rules\": [
          {
            \"max_age\": \"2h\"
          },
          {
            \"max_version\": 2
          }
        ]
      }
    ]
  }
""")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		instance, err := bigtable.NewInstance(ctx, "instance", &bigtable.InstanceArgs{
			Name: pulumi.String("instance_name"),
			Clusters: bigtable.InstanceClusterArray{
				&bigtable.InstanceClusterArgs{
					ClusterId: pulumi.String("cid"),
					Zone:      pulumi.String("us-central1-b"),
				},
			},
			InstanceType:       pulumi.String("DEVELOPMENT"),
			DeletionProtection: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		table, err := bigtable.NewTable(ctx, "table", &bigtable.TableArgs{
			Name:         pulumi.String("your-table"),
			InstanceName: instance.ID(),
			ColumnFamilies: bigtable.TableColumnFamilyArray{
				&bigtable.TableColumnFamilyArgs{
					Family: pulumi.String("cf1"),
				},
			},
		})
		if err != nil {
			return err
		}
		_, err = bigtable.NewGCPolicy(ctx, "policy", &bigtable.GCPolicyArgs{
			InstanceName:   instance.ID(),
			Table:          table.Name,
			ColumnFamily:   pulumi.String("cf1"),
			DeletionPolicy: pulumi.String("ABANDON"),
			GcRules: pulumi.String(`  {
    \"mode\": \"union\",
    \"rules\": [
      {
        \"max_age\": \"10h\"
      },
      {
        \"mode\": \"intersection\",
        \"rules\": [
          {
            \"max_age\": \"2h\"
          },
          {
            \"max_version\": 2
          }
        ]
      }
    ]
  }
`),
		})
		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 instance = new Gcp.BigTable.Instance("instance", new()
    {
        Name = "instance_name",
        Clusters = new[]
        {
            new Gcp.BigTable.Inputs.InstanceClusterArgs
            {
                ClusterId = "cid",
                Zone = "us-central1-b",
            },
        },
        InstanceType = "DEVELOPMENT",
        DeletionProtection = false,
    });

    var table = new Gcp.BigTable.Table("table", new()
    {
        Name = "your-table",
        InstanceName = instance.Id,
        ColumnFamilies = new[]
        {
            new Gcp.BigTable.Inputs.TableColumnFamilyArgs
            {
                Family = "cf1",
            },
        },
    });

    var policy = new Gcp.BigTable.GCPolicy("policy", new()
    {
        InstanceName = instance.Id,
        Table = table.Name,
        ColumnFamily = "cf1",
        DeletionPolicy = "ABANDON",
        GcRules = @"  {
    \""mode\"": \""union\"",
    \""rules\"": [
      {
        \""max_age\"": \""10h\""
      },
      {
        \""mode\"": \""intersection\"",
        \""rules\"": [
          {
            \""max_age\"": \""2h\""
          },
          {
            \""max_version\"": 2
          }
        ]
      }
    ]
  }
",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.bigtable.Instance;
import com.pulumi.gcp.bigtable.InstanceArgs;
import com.pulumi.gcp.bigtable.inputs.InstanceClusterArgs;
import com.pulumi.gcp.bigtable.Table;
import com.pulumi.gcp.bigtable.TableArgs;
import com.pulumi.gcp.bigtable.inputs.TableColumnFamilyArgs;
import com.pulumi.gcp.bigtable.GCPolicy;
import com.pulumi.gcp.bigtable.GCPolicyArgs;
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 instance = new Instance("instance", InstanceArgs.builder()
            .name("instance_name")
            .clusters(InstanceClusterArgs.builder()
                .clusterId("cid")
                .zone("us-central1-b")
                .build())
            .instanceType("DEVELOPMENT")
            .deletionProtection(false)
            .build());

        var table = new Table("table", TableArgs.builder()
            .name("your-table")
            .instanceName(instance.id())
            .columnFamilies(TableColumnFamilyArgs.builder()
                .family("cf1")
                .build())
            .build());

        var policy = new GCPolicy("policy", GCPolicyArgs.builder()
            .instanceName(instance.id())
            .table(table.name())
            .columnFamily("cf1")
            .deletionPolicy("ABANDON")
            .gcRules("""
  {
    \"mode\": \"union\",
    \"rules\": [
      {
        \"max_age\": \"10h\"
      },
      {
        \"mode\": \"intersection\",
        \"rules\": [
          {
            \"max_age\": \"2h\"
          },
          {
            \"max_version\": 2
          }
        ]
      }
    ]
  }
            """)
            .build());

    }
}
resources:
  instance:
    type: gcp:bigtable:Instance
    properties:
      name: instance_name
      clusters:
        - clusterId: cid
          zone: us-central1-b
      instanceType: DEVELOPMENT
      deletionProtection: false
  table:
    type: gcp:bigtable:Table
    properties:
      name: your-table
      instanceName: ${instance.id}
      columnFamilies:
        - family: cf1
  policy:
    type: gcp:bigtable:GCPolicy
    properties:
      instanceName: ${instance.id}
      table: ${table.name}
      columnFamily: cf1
      deletionPolicy: ABANDON
      gcRules: |2
          {
            \"mode\": \"union\",
            \"rules\": [
              {
                \"max_age\": \"10h\"
              },
              {
                \"mode\": \"intersection\",
                \"rules\": [
                  {
                    \"max_age\": \"2h\"
                  },
                  {
                    \"max_version\": 2
                  }
                ]
              }
            ]
          }

Nested rules allow you to combine union and intersection modes. This policy deletes cells that are either older than 10 hours OR (older than 2 hours AND have more than 2 versions). The outer union creates an OR relationship, while the inner intersection creates an AND relationship. The example shows the equivalent cbt command for validation: cbt setgcpolicy your-table cf1 "(maxage=2d and maxversions=2) or maxage=10h".

Beyond these examples

These snippets focus on specific GC policy features: age-based and version-based garbage collection, and union and intersection logic for multi-condition policies. They’re intentionally minimal rather than full table management solutions.

The examples reference pre-existing infrastructure such as Bigtable instances and tables with defined column families. They focus on configuring garbage collection rather than provisioning the table schema.

To keep things focused, common GC policy patterns are omitted, including:

  • Warning suppression for policy relaxation (ignoreWarnings)
  • Replication-aware policy management
  • Simple mode/maxAge/maxVersion properties (examples use gcRules JSON)

These omissions are intentional: the goal is to illustrate how each GC policy feature is wired, not provide drop-in data lifecycle modules. See the Bigtable GCPolicy resource reference for all available configuration options.

Let's configure GCP Bigtable Garbage Collection Policies

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Replication & Deletion
How do I handle deletion of GC policies on replicated instances?
GC policies on replicated tables can’t be destroyed directly because destroying translates to “never perform garbage collection,” which isn’t allowed. Set deletionPolicy to ABANDON to abandon the resource, or unreplicate the instance first by updating it to have one cluster.
Can I create multiple GC policies for the same column family?
No, multiple GC policies for the same column family may result in unexpected behavior. Use a single GC policy per column family.
Policy Configuration
Should I use gcRules or the simple property approach?
Choose one approach, not both. The gcRules property (serialized JSON) conflicts with mode, maxAge, and maxVersion. Use gcRules for complex nested policies, or use the simple properties for straightforward configurations.
What's the difference between UNION and INTERSECTION modes?
UNION applies OR logic (any sub-policy triggers garbage collection), while INTERSECTION applies AND logic (all sub-policies must apply). For example, (maxage=2d AND maxversions=2) OR maxage=10h uses both modes.
How do I create complex nested GC policies?
Use gcRules with nested JSON containing mode and rules arrays. The complex example shows a UNION with a nested INTERSECTION: {"mode": "union", "rules": [{"max_age": "10h"}, {"mode": "intersection", "rules": [...]}]}.
Immutability & Updates
What properties are immutable after creation?
The following properties can’t be changed: columnFamily, instanceName, table, project, maxAge, maxVersions, and mode. You’ll need to recreate the resource to change these.
What does ignoreWarnings do and what are the risks?
Setting ignoreWarnings to true allows relaxing the GC policy for replicated clusters by up to 90 days, but this may increase how long clusters remain inconsistent. Review the risks at https://cloud.google.com/bigtable/docs/garbage-collection#increasing before enabling.
How do I specify age-based garbage collection?
Use maxAge in your gcRules JSON (e.g., "max_age": "168h" for 7 days) or as a simple property. Age format follows duration syntax like “168h” or “7d”.
How do I specify version-based garbage collection?
Use maxVersion in your gcRules JSON (e.g., "max_version": 10) or as a simple property. This keeps only the N most recent versions of each cell.

Using a different cloud?

Explore database guides for other cloud providers: