Create GCP Cloud Spanner Instances

The gcp:spanner/instance:Instance resource, part of the Pulumi GCP provider, provisions a Cloud Spanner instance: an isolated set of compute and storage resources that hosts databases. This guide focuses on three capabilities: regional and multi-regional placement, node-based vs processing-unit capacity models, and edition selection and backup scheduling.

Spanner instances require a GCP project with the Spanner API enabled. The examples are intentionally small. Combine them with your own database definitions and access controls.

Provision a regional instance with fixed node count

Most deployments start with a regional instance that uses a fixed number of nodes for predictable capacity and cost.

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

const example = new gcp.spanner.Instance("example", {
    config: "regional-us-central1",
    displayName: "Test Spanner Instance",
    numNodes: 2,
    edition: "STANDARD",
    defaultBackupScheduleType: "AUTOMATIC",
    labels: {
        foo: "bar",
    },
});
import pulumi
import pulumi_gcp as gcp

example = gcp.spanner.Instance("example",
    config="regional-us-central1",
    display_name="Test Spanner Instance",
    num_nodes=2,
    edition="STANDARD",
    default_backup_schedule_type="AUTOMATIC",
    labels={
        "foo": "bar",
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := spanner.NewInstance(ctx, "example", &spanner.InstanceArgs{
			Config:                    pulumi.String("regional-us-central1"),
			DisplayName:               pulumi.String("Test Spanner Instance"),
			NumNodes:                  pulumi.Int(2),
			Edition:                   pulumi.String("STANDARD"),
			DefaultBackupScheduleType: pulumi.String("AUTOMATIC"),
			Labels: pulumi.StringMap{
				"foo": pulumi.String("bar"),
			},
		})
		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 example = new Gcp.Spanner.Instance("example", new()
    {
        Config = "regional-us-central1",
        DisplayName = "Test Spanner Instance",
        NumNodes = 2,
        Edition = "STANDARD",
        DefaultBackupScheduleType = "AUTOMATIC",
        Labels = 
        {
            { "foo", "bar" },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.spanner.Instance;
import com.pulumi.gcp.spanner.InstanceArgs;
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 example = new Instance("example", InstanceArgs.builder()
            .config("regional-us-central1")
            .displayName("Test Spanner Instance")
            .numNodes(2)
            .edition("STANDARD")
            .defaultBackupScheduleType("AUTOMATIC")
            .labels(Map.of("foo", "bar"))
            .build());

    }
}
resources:
  example:
    type: gcp:spanner:Instance
    properties:
      config: regional-us-central1
      displayName: Test Spanner Instance
      numNodes: 2
      edition: STANDARD
      defaultBackupScheduleType: AUTOMATIC
      labels:
        foo: bar

The config property defines geographic placement and replication strategy. Regional configurations like “regional-us-central1” store data in a single region with automatic zone replication. The numNodes property sets compute capacity; each node provides 1000 processing units. The edition property determines feature availability and pricing tier (STANDARD, ENTERPRISE, or ENTERPRISE_PLUS). The defaultBackupScheduleType controls whether new databases automatically create backup schedules; AUTOMATIC is not permitted for free instances.

Allocate capacity using processing units

Processing units provide finer-grained capacity control than nodes, allowing you to provision compute in increments of 100 processing units.

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

const example = new gcp.spanner.Instance("example", {
    config: "regional-us-central1",
    displayName: "Test Spanner Instance",
    processingUnits: 200,
    labels: {
        foo: "bar",
    },
});
import pulumi
import pulumi_gcp as gcp

example = gcp.spanner.Instance("example",
    config="regional-us-central1",
    display_name="Test Spanner Instance",
    processing_units=200,
    labels={
        "foo": "bar",
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := spanner.NewInstance(ctx, "example", &spanner.InstanceArgs{
			Config:          pulumi.String("regional-us-central1"),
			DisplayName:     pulumi.String("Test Spanner Instance"),
			ProcessingUnits: pulumi.Int(200),
			Labels: pulumi.StringMap{
				"foo": pulumi.String("bar"),
			},
		})
		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 example = new Gcp.Spanner.Instance("example", new()
    {
        Config = "regional-us-central1",
        DisplayName = "Test Spanner Instance",
        ProcessingUnits = 200,
        Labels = 
        {
            { "foo", "bar" },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.spanner.Instance;
import com.pulumi.gcp.spanner.InstanceArgs;
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 example = new Instance("example", InstanceArgs.builder()
            .config("regional-us-central1")
            .displayName("Test Spanner Instance")
            .processingUnits(200)
            .labels(Map.of("foo", "bar"))
            .build());

    }
}
resources:
  example:
    type: gcp:spanner:Instance
    properties:
      config: regional-us-central1
      displayName: Test Spanner Instance
      processingUnits: 200
      labels:
        foo: bar

The processingUnits property replaces numNodes for sub-node granularity. One node equals 1000 processing units, so this example provisions 0.2 nodes worth of capacity. You must specify exactly one of numNodes, processingUnits, or autoscalingConfig (except for FREE_INSTANCE types).

Deploy across multiple continents for global access

Applications serving users worldwide benefit from multi-regional instances that replicate data across continents.

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

const example = new gcp.spanner.Instance("example", {
    config: "nam-eur-asia1",
    displayName: "Multi Regional Instance",
    numNodes: 2,
    labels: {
        foo: "bar",
    },
});
import pulumi
import pulumi_gcp as gcp

example = gcp.spanner.Instance("example",
    config="nam-eur-asia1",
    display_name="Multi Regional Instance",
    num_nodes=2,
    labels={
        "foo": "bar",
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := spanner.NewInstance(ctx, "example", &spanner.InstanceArgs{
			Config:      pulumi.String("nam-eur-asia1"),
			DisplayName: pulumi.String("Multi Regional Instance"),
			NumNodes:    pulumi.Int(2),
			Labels: pulumi.StringMap{
				"foo": pulumi.String("bar"),
			},
		})
		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 example = new Gcp.Spanner.Instance("example", new()
    {
        Config = "nam-eur-asia1",
        DisplayName = "Multi Regional Instance",
        NumNodes = 2,
        Labels = 
        {
            { "foo", "bar" },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.spanner.Instance;
import com.pulumi.gcp.spanner.InstanceArgs;
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 example = new Instance("example", InstanceArgs.builder()
            .config("nam-eur-asia1")
            .displayName("Multi Regional Instance")
            .numNodes(2)
            .labels(Map.of("foo", "bar"))
            .build());

    }
}
resources:
  example:
    type: gcp:spanner:Instance
    properties:
      config: nam-eur-asia1
      displayName: Multi Regional Instance
      numNodes: 2
      labels:
        foo: bar

Multi-regional configurations like “nam-eur-asia1” span North America, Europe, and Asia. Spanner automatically replicates data across these regions, reducing read latency for geographically distributed clients. Write latency increases compared to regional instances due to cross-region consensus requirements.

Beyond these examples

These snippets focus on specific instance-level features: regional and multi-regional placement, node-based and processing-unit capacity models, and edition selection and backup scheduling. They’re intentionally minimal rather than full database deployments.

The examples assume pre-existing infrastructure such as a GCP project with Spanner API enabled. They focus on configuring the instance rather than provisioning databases or access controls.

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

  • Autoscaling configuration (autoscalingConfig)
  • Free instance tier (instanceType: FREE_INSTANCE)
  • Force destroy for backup cleanup (forceDestroy)
  • Custom labels beyond basic examples

These omissions are intentional: the goal is to illustrate how each instance feature is wired, not provide drop-in database modules. See the Spanner Instance resource reference for all available configuration options.

Let's create GCP Cloud Spanner Instances

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Instance Configuration & Capacity
What's the difference between numNodes, processingUnits, and autoscalingConfig?
You must choose exactly one capacity option: numNodes for node-based allocation, processingUnits for granular control (e.g., 200 units), or autoscalingConfig for automatic scaling. Free instances don’t require any of these.
Can I set numNodes when using autoscaling?
No. When autoscalingConfig is enabled, numNodes and processingUnits become read-only output fields that reflect current capacity.
How do I choose a config value for my instance?
The config determines geographic placement and replication. Use regional values like regional-us-central1 for single regions, or multi-regional values like nam-eur-asia1 for broader coverage.
What are the different Spanner editions?
Spanner offers STANDARD, ENTERPRISE, and ENTERPRISE_PLUS editions, each with different capabilities and pricing. Don’t set edition for free instances.
Free Instance Limitations
What are the limitations of free Spanner instances?
Free instances (instanceType = FREE_INSTANCE) cannot use automatic backups, cannot configure the edition field, and don’t require capacity settings (numNodes, processingUnits, or autoscalingConfig).
Immutability & Lifecycle
What properties can't I change after creating an instance?
The config, name, and project properties are immutable. Changing them requires recreating the instance.
Why is my instance deletion failing?
If you created backups manually in the console, you must set forceDestroy to true to delete the instance and its backups.
Labels & Metadata
What happens to labels I create outside Pulumi?
The labels field is non-authoritative, meaning Pulumi only manages labels in your configuration. Use effectiveLabels to see all labels on the resource.
What are the naming requirements for Spanner instances?
The name must be 6-30 characters. If not provided, a random name starting with tf- is generated. The displayName must be 4-30 characters and unique per project.
Can I use automatic backups with my instance?
Yes, set defaultBackupScheduleType to AUTOMATIC for new databases. However, automatic backups aren’t permitted for free instances.

Using a different cloud?

Explore database guides for other cloud providers: