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 allocation, and backup scheduling and edition selection.

Spanner instances require a GCP project with the Spanner API enabled. Databases are created separately on the instance. The examples are intentionally small. Combine them with your own database resources and access controls.

Create a regional instance with node-based capacity

Most deployments start with a regional instance using node-based capacity for predictable compute and storage.

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 (e.g., “regional-us-central1”). The numNodes property sets capacity in full-node increments. The edition property determines feature availability and pricing tier. The defaultBackupScheduleType property controls whether new databases automatically get backup schedules; note that AUTOMATIC is not permitted for free instances.

Allocate capacity using processing units

For finer-grained capacity control, specify processing units directly instead of full nodes.

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 allows capacity allocation below full node increments (100 processing units equals one node). This provides more precise resource allocation and cost optimization. You cannot specify both numNodes and processingUnits; choose one capacity model.

Deploy across multiple regions for global availability

Applications serving global users 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

The config property uses a multi-regional value (e.g., “nam-eur-asia1”) that spans North America, Europe, and Asia. Multi-regional instances provide low-latency access to users across continents and higher availability through geographic redundancy. Capacity is still allocated using numNodes or processingUnits.

Beyond these examples

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

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

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

  • Autoscaling configuration (autoscalingConfig)
  • Force destroy for instances with manual backups (forceDestroy)
  • Free instance type configuration (instanceType)
  • 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 & Immutability
What properties can't I change after creating a Spanner instance?
The config, name, and project properties are immutable and cannot be changed after instance creation. Changing these requires recreating the instance.
What are valid values for the config property?
Use regional configs like regional-us-central1 or regional-europe-west1, or multi-regional configs like nam-eur-asia1. Consult the Configuration section of the docs for a complete list.
What are the naming requirements for a Spanner instance?
The name must be 6-30 characters long. If not provided, Pulumi auto-generates a name starting with tf-.
Capacity & Scaling
What's the difference between numNodes and processingUnits?
Both specify instance capacity but use different units. Use numNodes for node-based sizing (e.g., numNodes: 2) or processingUnits for finer-grained control (e.g., processingUnits: 200). Choose one approach, not both.
Which edition should I choose for my instance?
Choose from STANDARD, ENTERPRISE, or ENTERPRISE_PLUS based on your needs. Different editions provide different capabilities at different price points. Don’t configure edition for free instances.
Free Instances
What are the limitations of free Spanner instances?
Free instances (instanceType: "FREE_INSTANCE") cannot use automatic backups and should not have the edition field configured. Set defaultBackupScheduleType to NONE for free instances.
Backups & Deletion
How do I delete an instance that has manual backups?
Set forceDestroy to true to delete all backups when deleting the instance. This is required if you created backups manually in the console.
What backup options are available for new databases?
Set defaultBackupScheduleType to AUTOMATIC for automatic backups or NONE to disable default backups. Note that AUTOMATIC is not permitted for free instances.
Labels & Resource Management
Why don't I see all labels on my Spanner instance?
The labels field is non-authoritative and only manages labels in your Pulumi configuration. Use the effectiveLabels output to see all labels present on the resource, including those added by other clients or services.
Can I create both regional and multi-regional instances?
Yes. Use regional configs like regional-us-central1 for single-region instances, or multi-regional configs like nam-eur-asia1 for instances spanning multiple regions.

Using a different cloud?

Explore database guides for other cloud providers: