Deploy GCP Cloud Firestore Databases

The gcp:firestore/database:Database resource, part of the Pulumi GCP provider, provisions a Cloud Firestore database: its mode (Firestore Native or Datastore Mode), location, edition, and operational features like encryption and recovery. This guide focuses on four capabilities: database mode selection, protection and recovery features, customer-managed encryption, and Enterprise edition capabilities.

Firestore databases require a GCP project with the Firestore API enabled. CMEK configurations require KMS keys and IAM bindings; tagging requires pre-existing resource manager tags. The examples are intentionally small. Combine them with your own project configuration, security policies, and access controls.

Create the default Firestore Native database

Most projects start with a single default database that serves as the primary storage layer. The default database uses the special name “(default)” and requires minimal configuration.

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

const database = new gcp.firestore.Database("database", {
    project: "my-project-name",
    name: "(default)",
    locationId: "nam5",
    type: "FIRESTORE_NATIVE",
});
import pulumi
import pulumi_gcp as gcp

database = gcp.firestore.Database("database",
    project="my-project-name",
    name="(default)",
    location_id="nam5",
    type="FIRESTORE_NATIVE")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := firestore.NewDatabase(ctx, "database", &firestore.DatabaseArgs{
			Project:    pulumi.String("my-project-name"),
			Name:       pulumi.String("(default)"),
			LocationId: pulumi.String("nam5"),
			Type:       pulumi.String("FIRESTORE_NATIVE"),
		})
		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 database = new Gcp.Firestore.Database("database", new()
    {
        Project = "my-project-name",
        Name = "(default)",
        LocationId = "nam5",
        Type = "FIRESTORE_NATIVE",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.firestore.Database;
import com.pulumi.gcp.firestore.DatabaseArgs;
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 database = new Database("database", DatabaseArgs.builder()
            .project("my-project-name")
            .name("(default)")
            .locationId("nam5")
            .type("FIRESTORE_NATIVE")
            .build());

    }
}
resources:
  database:
    type: gcp:firestore:Database
    properties:
      project: my-project-name
      name: (default)
      locationId: nam5
      type: FIRESTORE_NATIVE

The name property accepts “(default)” as a special value for the primary database. The type property determines the data model: FIRESTORE_NATIVE provides document-based storage with real-time listeners, while DATASTORE_MODE offers entity-based storage compatible with Cloud Datastore. The locationId determines where data is stored; multi-region locations like “nam5” provide higher availability.

Configure a named database with protection and recovery

Production databases often require safeguards like point-in-time recovery for data restoration and delete protection to prevent accidental removal.

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

const database = new gcp.firestore.Database("database", {
    project: "my-project-name",
    name: "database-id",
    locationId: "nam5",
    type: "FIRESTORE_NATIVE",
    concurrencyMode: "OPTIMISTIC",
    appEngineIntegrationMode: "DISABLED",
    pointInTimeRecoveryEnablement: "POINT_IN_TIME_RECOVERY_ENABLED",
    deleteProtectionState: "DELETE_PROTECTION_ENABLED",
    deletionPolicy: "DELETE",
});
import pulumi
import pulumi_gcp as gcp

database = gcp.firestore.Database("database",
    project="my-project-name",
    name="database-id",
    location_id="nam5",
    type="FIRESTORE_NATIVE",
    concurrency_mode="OPTIMISTIC",
    app_engine_integration_mode="DISABLED",
    point_in_time_recovery_enablement="POINT_IN_TIME_RECOVERY_ENABLED",
    delete_protection_state="DELETE_PROTECTION_ENABLED",
    deletion_policy="DELETE")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := firestore.NewDatabase(ctx, "database", &firestore.DatabaseArgs{
			Project:                       pulumi.String("my-project-name"),
			Name:                          pulumi.String("database-id"),
			LocationId:                    pulumi.String("nam5"),
			Type:                          pulumi.String("FIRESTORE_NATIVE"),
			ConcurrencyMode:               pulumi.String("OPTIMISTIC"),
			AppEngineIntegrationMode:      pulumi.String("DISABLED"),
			PointInTimeRecoveryEnablement: pulumi.String("POINT_IN_TIME_RECOVERY_ENABLED"),
			DeleteProtectionState:         pulumi.String("DELETE_PROTECTION_ENABLED"),
			DeletionPolicy:                pulumi.String("DELETE"),
		})
		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 database = new Gcp.Firestore.Database("database", new()
    {
        Project = "my-project-name",
        Name = "database-id",
        LocationId = "nam5",
        Type = "FIRESTORE_NATIVE",
        ConcurrencyMode = "OPTIMISTIC",
        AppEngineIntegrationMode = "DISABLED",
        PointInTimeRecoveryEnablement = "POINT_IN_TIME_RECOVERY_ENABLED",
        DeleteProtectionState = "DELETE_PROTECTION_ENABLED",
        DeletionPolicy = "DELETE",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.firestore.Database;
import com.pulumi.gcp.firestore.DatabaseArgs;
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 database = new Database("database", DatabaseArgs.builder()
            .project("my-project-name")
            .name("database-id")
            .locationId("nam5")
            .type("FIRESTORE_NATIVE")
            .concurrencyMode("OPTIMISTIC")
            .appEngineIntegrationMode("DISABLED")
            .pointInTimeRecoveryEnablement("POINT_IN_TIME_RECOVERY_ENABLED")
            .deleteProtectionState("DELETE_PROTECTION_ENABLED")
            .deletionPolicy("DELETE")
            .build());

    }
}
resources:
  database:
    type: gcp:firestore:Database
    properties:
      project: my-project-name
      name: database-id
      locationId: nam5
      type: FIRESTORE_NATIVE
      concurrencyMode: OPTIMISTIC
      appEngineIntegrationMode: DISABLED
      pointInTimeRecoveryEnablement: POINT_IN_TIME_RECOVERY_ENABLED
      deleteProtectionState: DELETE_PROTECTION_ENABLED
      deletionPolicy: DELETE

When pointInTimeRecoveryEnablement is set to POINT_IN_TIME_RECOVERY_ENABLED, Firestore retains data versions for 7 days, allowing reads against any timestamp within the past hour and 1-minute snapshots beyond that. The deleteProtectionState prevents accidental deletion; when enabled, you must also set deletionPolicy to DELETE for Pulumi to remove the database. The concurrencyMode controls transaction behavior: OPTIMISTIC allows higher throughput with retry logic, while PESSIMISTIC uses locks.

Apply resource manager tags for organization

Organizations tracking costs or enforcing policies use resource manager tags to categorize databases by team, environment, or billing code.

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

const database = new gcp.firestore.Database("database", {
    project: "my-project-name",
    name: "database-with-tags-id",
    locationId: "nam5",
    type: "FIRESTORE_NATIVE",
    deleteProtectionState: "DELETE_PROTECTION_ENABLED",
    deletionPolicy: "DELETE",
    tags: {
        keyname: "valuename",
    },
});
import pulumi
import pulumi_gcp as gcp

database = gcp.firestore.Database("database",
    project="my-project-name",
    name="database-with-tags-id",
    location_id="nam5",
    type="FIRESTORE_NATIVE",
    delete_protection_state="DELETE_PROTECTION_ENABLED",
    deletion_policy="DELETE",
    tags={
        "keyname": "valuename",
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := firestore.NewDatabase(ctx, "database", &firestore.DatabaseArgs{
			Project:               pulumi.String("my-project-name"),
			Name:                  pulumi.String("database-with-tags-id"),
			LocationId:            pulumi.String("nam5"),
			Type:                  pulumi.String("FIRESTORE_NATIVE"),
			DeleteProtectionState: pulumi.String("DELETE_PROTECTION_ENABLED"),
			DeletionPolicy:        pulumi.String("DELETE"),
			Tags: pulumi.StringMap{
				"keyname": pulumi.String("valuename"),
			},
		})
		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 database = new Gcp.Firestore.Database("database", new()
    {
        Project = "my-project-name",
        Name = "database-with-tags-id",
        LocationId = "nam5",
        Type = "FIRESTORE_NATIVE",
        DeleteProtectionState = "DELETE_PROTECTION_ENABLED",
        DeletionPolicy = "DELETE",
        Tags = 
        {
            { "keyname", "valuename" },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.firestore.Database;
import com.pulumi.gcp.firestore.DatabaseArgs;
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 database = new Database("database", DatabaseArgs.builder()
            .project("my-project-name")
            .name("database-with-tags-id")
            .locationId("nam5")
            .type("FIRESTORE_NATIVE")
            .deleteProtectionState("DELETE_PROTECTION_ENABLED")
            .deletionPolicy("DELETE")
            .tags(Map.of("keyname", "valuename"))
            .build());

    }
}
resources:
  database:
    type: gcp:firestore:Database
    properties:
      project: my-project-name
      name: database-with-tags-id
      locationId: nam5
      type: FIRESTORE_NATIVE
      deleteProtectionState: DELETE_PROTECTION_ENABLED
      deletionPolicy: DELETE
      tags:
        keyname: valuename

The tags property accepts resource manager tag key-value pairs in the format tagKeys/{tag_key_id} and tagValues/{tag_value_id}. These tags are immutable after creation and cause resource replacement when changed. For applying tags to existing databases, use the gcp.tags.TagValue resource instead.

Encrypt data with customer-managed keys

Compliance requirements often mandate customer-managed encryption keys (CMEK) rather than Google’s default encryption, giving organizations direct control over key lifecycle and access.

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

const project = gcp.organizations.getProject({});
const keyRing = new gcp.kms.KeyRing("key_ring", {
    name: "kms-key-ring",
    location: "us",
});
const cryptoKey = new gcp.kms.CryptoKey("crypto_key", {
    name: "kms-key",
    keyRing: keyRing.id,
    purpose: "ENCRYPT_DECRYPT",
});
const firestoreCmekKeyuser = new gcp.kms.CryptoKeyIAMBinding("firestore_cmek_keyuser", {
    cryptoKeyId: cryptoKey.id,
    role: "roles/cloudkms.cryptoKeyEncrypterDecrypter",
    members: [project.then(project => `serviceAccount:service-${project.number}@gcp-sa-firestore.iam.gserviceaccount.com`)],
});
const database = new gcp.firestore.Database("database", {
    project: "my-project-name",
    name: "cmek-database-id",
    locationId: "nam5",
    type: "FIRESTORE_NATIVE",
    concurrencyMode: "OPTIMISTIC",
    appEngineIntegrationMode: "DISABLED",
    pointInTimeRecoveryEnablement: "POINT_IN_TIME_RECOVERY_ENABLED",
    deleteProtectionState: "DELETE_PROTECTION_ENABLED",
    deletionPolicy: "DELETE",
    cmekConfig: {
        kmsKeyName: cryptoKey.id,
    },
}, {
    dependsOn: [firestoreCmekKeyuser],
});
import pulumi
import pulumi_gcp as gcp

project = gcp.organizations.get_project()
key_ring = gcp.kms.KeyRing("key_ring",
    name="kms-key-ring",
    location="us")
crypto_key = gcp.kms.CryptoKey("crypto_key",
    name="kms-key",
    key_ring=key_ring.id,
    purpose="ENCRYPT_DECRYPT")
firestore_cmek_keyuser = gcp.kms.CryptoKeyIAMBinding("firestore_cmek_keyuser",
    crypto_key_id=crypto_key.id,
    role="roles/cloudkms.cryptoKeyEncrypterDecrypter",
    members=[f"serviceAccount:service-{project.number}@gcp-sa-firestore.iam.gserviceaccount.com"])
database = gcp.firestore.Database("database",
    project="my-project-name",
    name="cmek-database-id",
    location_id="nam5",
    type="FIRESTORE_NATIVE",
    concurrency_mode="OPTIMISTIC",
    app_engine_integration_mode="DISABLED",
    point_in_time_recovery_enablement="POINT_IN_TIME_RECOVERY_ENABLED",
    delete_protection_state="DELETE_PROTECTION_ENABLED",
    deletion_policy="DELETE",
    cmek_config={
        "kms_key_name": crypto_key.id,
    },
    opts = pulumi.ResourceOptions(depends_on=[firestore_cmek_keyuser]))
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/firestore"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/kms"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
		if err != nil {
			return err
		}
		keyRing, err := kms.NewKeyRing(ctx, "key_ring", &kms.KeyRingArgs{
			Name:     pulumi.String("kms-key-ring"),
			Location: pulumi.String("us"),
		})
		if err != nil {
			return err
		}
		cryptoKey, err := kms.NewCryptoKey(ctx, "crypto_key", &kms.CryptoKeyArgs{
			Name:    pulumi.String("kms-key"),
			KeyRing: keyRing.ID(),
			Purpose: pulumi.String("ENCRYPT_DECRYPT"),
		})
		if err != nil {
			return err
		}
		firestoreCmekKeyuser, err := kms.NewCryptoKeyIAMBinding(ctx, "firestore_cmek_keyuser", &kms.CryptoKeyIAMBindingArgs{
			CryptoKeyId: cryptoKey.ID(),
			Role:        pulumi.String("roles/cloudkms.cryptoKeyEncrypterDecrypter"),
			Members: pulumi.StringArray{
				pulumi.Sprintf("serviceAccount:service-%v@gcp-sa-firestore.iam.gserviceaccount.com", project.Number),
			},
		})
		if err != nil {
			return err
		}
		_, err = firestore.NewDatabase(ctx, "database", &firestore.DatabaseArgs{
			Project:                       pulumi.String("my-project-name"),
			Name:                          pulumi.String("cmek-database-id"),
			LocationId:                    pulumi.String("nam5"),
			Type:                          pulumi.String("FIRESTORE_NATIVE"),
			ConcurrencyMode:               pulumi.String("OPTIMISTIC"),
			AppEngineIntegrationMode:      pulumi.String("DISABLED"),
			PointInTimeRecoveryEnablement: pulumi.String("POINT_IN_TIME_RECOVERY_ENABLED"),
			DeleteProtectionState:         pulumi.String("DELETE_PROTECTION_ENABLED"),
			DeletionPolicy:                pulumi.String("DELETE"),
			CmekConfig: &firestore.DatabaseCmekConfigArgs{
				KmsKeyName: cryptoKey.ID(),
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			firestoreCmekKeyuser,
		}))
		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 project = Gcp.Organizations.GetProject.Invoke();

    var keyRing = new Gcp.Kms.KeyRing("key_ring", new()
    {
        Name = "kms-key-ring",
        Location = "us",
    });

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

    var firestoreCmekKeyuser = new Gcp.Kms.CryptoKeyIAMBinding("firestore_cmek_keyuser", new()
    {
        CryptoKeyId = cryptoKey.Id,
        Role = "roles/cloudkms.cryptoKeyEncrypterDecrypter",
        Members = new[]
        {
            $"serviceAccount:service-{project.Apply(getProjectResult => getProjectResult.Number)}@gcp-sa-firestore.iam.gserviceaccount.com",
        },
    });

    var database = new Gcp.Firestore.Database("database", new()
    {
        Project = "my-project-name",
        Name = "cmek-database-id",
        LocationId = "nam5",
        Type = "FIRESTORE_NATIVE",
        ConcurrencyMode = "OPTIMISTIC",
        AppEngineIntegrationMode = "DISABLED",
        PointInTimeRecoveryEnablement = "POINT_IN_TIME_RECOVERY_ENABLED",
        DeleteProtectionState = "DELETE_PROTECTION_ENABLED",
        DeletionPolicy = "DELETE",
        CmekConfig = new Gcp.Firestore.Inputs.DatabaseCmekConfigArgs
        {
            KmsKeyName = cryptoKey.Id,
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            firestoreCmekKeyuser,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
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.kms.CryptoKeyIAMBinding;
import com.pulumi.gcp.kms.CryptoKeyIAMBindingArgs;
import com.pulumi.gcp.firestore.Database;
import com.pulumi.gcp.firestore.DatabaseArgs;
import com.pulumi.gcp.firestore.inputs.DatabaseCmekConfigArgs;
import com.pulumi.resources.CustomResourceOptions;
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) {
        final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
            .build());

        var keyRing = new KeyRing("keyRing", KeyRingArgs.builder()
            .name("kms-key-ring")
            .location("us")
            .build());

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

        var firestoreCmekKeyuser = new CryptoKeyIAMBinding("firestoreCmekKeyuser", CryptoKeyIAMBindingArgs.builder()
            .cryptoKeyId(cryptoKey.id())
            .role("roles/cloudkms.cryptoKeyEncrypterDecrypter")
            .members(String.format("serviceAccount:service-%s@gcp-sa-firestore.iam.gserviceaccount.com", project.number()))
            .build());

        var database = new Database("database", DatabaseArgs.builder()
            .project("my-project-name")
            .name("cmek-database-id")
            .locationId("nam5")
            .type("FIRESTORE_NATIVE")
            .concurrencyMode("OPTIMISTIC")
            .appEngineIntegrationMode("DISABLED")
            .pointInTimeRecoveryEnablement("POINT_IN_TIME_RECOVERY_ENABLED")
            .deleteProtectionState("DELETE_PROTECTION_ENABLED")
            .deletionPolicy("DELETE")
            .cmekConfig(DatabaseCmekConfigArgs.builder()
                .kmsKeyName(cryptoKey.id())
                .build())
            .build(), CustomResourceOptions.builder()
                .dependsOn(firestoreCmekKeyuser)
                .build());

    }
}
resources:
  database:
    type: gcp:firestore:Database
    properties:
      project: my-project-name
      name: cmek-database-id
      locationId: nam5
      type: FIRESTORE_NATIVE
      concurrencyMode: OPTIMISTIC
      appEngineIntegrationMode: DISABLED
      pointInTimeRecoveryEnablement: POINT_IN_TIME_RECOVERY_ENABLED
      deleteProtectionState: DELETE_PROTECTION_ENABLED
      deletionPolicy: DELETE
      cmekConfig:
        kmsKeyName: ${cryptoKey.id}
    options:
      dependsOn:
        - ${firestoreCmekKeyuser}
  cryptoKey:
    type: gcp:kms:CryptoKey
    name: crypto_key
    properties:
      name: kms-key
      keyRing: ${keyRing.id}
      purpose: ENCRYPT_DECRYPT
  keyRing:
    type: gcp:kms:KeyRing
    name: key_ring
    properties:
      name: kms-key-ring
      location: us
  firestoreCmekKeyuser:
    type: gcp:kms:CryptoKeyIAMBinding
    name: firestore_cmek_keyuser
    properties:
      cryptoKeyId: ${cryptoKey.id}
      role: roles/cloudkms.cryptoKeyEncrypterDecrypter
      members:
        - serviceAccount:service-${project.number}@gcp-sa-firestore.iam.gserviceaccount.com
variables:
  project:
    fn::invoke:
      function: gcp:organizations:getProject
      arguments: {}

The cmekConfig block references a KMS crypto key for encryption. Before creating the database, you must grant the Firestore service account (service-{project_number}@gcp-sa-firestore.iam.gserviceaccount.com) the cloudkms.cryptoKeyEncrypterDecrypter role on the key. The dependsOn ensures Pulumi creates the IAM binding before the database. The KMS key location must be compatible with the database location.

Create a Datastore Mode database for legacy compatibility

Applications migrating from Cloud Datastore or requiring Datastore’s entity-based model use DATASTORE_MODE instead of the document-based Firestore Native mode.

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

const datastoreModeDatabase = new gcp.firestore.Database("datastore_mode_database", {
    project: "my-project-name",
    name: "(default)",
    locationId: "nam5",
    type: "DATASTORE_MODE",
});
import pulumi
import pulumi_gcp as gcp

datastore_mode_database = gcp.firestore.Database("datastore_mode_database",
    project="my-project-name",
    name="(default)",
    location_id="nam5",
    type="DATASTORE_MODE")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := firestore.NewDatabase(ctx, "datastore_mode_database", &firestore.DatabaseArgs{
			Project:    pulumi.String("my-project-name"),
			Name:       pulumi.String("(default)"),
			LocationId: pulumi.String("nam5"),
			Type:       pulumi.String("DATASTORE_MODE"),
		})
		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 datastoreModeDatabase = new Gcp.Firestore.Database("datastore_mode_database", new()
    {
        Project = "my-project-name",
        Name = "(default)",
        LocationId = "nam5",
        Type = "DATASTORE_MODE",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.firestore.Database;
import com.pulumi.gcp.firestore.DatabaseArgs;
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 datastoreModeDatabase = new Database("datastoreModeDatabase", DatabaseArgs.builder()
            .project("my-project-name")
            .name("(default)")
            .locationId("nam5")
            .type("DATASTORE_MODE")
            .build());

    }
}
resources:
  datastoreModeDatabase:
    type: gcp:firestore:Database
    name: datastore_mode_database
    properties:
      project: my-project-name
      name: (default)
      locationId: nam5
      type: DATASTORE_MODE

Setting type to DATASTORE_MODE creates a database compatible with the Cloud Datastore API. This mode uses entities and keys rather than documents and collections. Once set, the type cannot be changed; you must create a new database to switch modes.

Enable Enterprise edition for advanced features

High-throughput applications requiring enhanced performance, SLAs, or advanced data access modes use the Enterprise edition, which unlocks capabilities not available in the Standard edition.

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

const enterprise_db = new gcp.firestore.Database("enterprise-db", {
    project: "my-project-name",
    name: "database-id",
    locationId: "nam5",
    type: "FIRESTORE_NATIVE",
    databaseEdition: "ENTERPRISE",
    deletionPolicy: "DELETE",
});
import pulumi
import pulumi_gcp as gcp

enterprise_db = gcp.firestore.Database("enterprise-db",
    project="my-project-name",
    name="database-id",
    location_id="nam5",
    type="FIRESTORE_NATIVE",
    database_edition="ENTERPRISE",
    deletion_policy="DELETE")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := firestore.NewDatabase(ctx, "enterprise-db", &firestore.DatabaseArgs{
			Project:         pulumi.String("my-project-name"),
			Name:            pulumi.String("database-id"),
			LocationId:      pulumi.String("nam5"),
			Type:            pulumi.String("FIRESTORE_NATIVE"),
			DatabaseEdition: pulumi.String("ENTERPRISE"),
			DeletionPolicy:  pulumi.String("DELETE"),
		})
		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 enterprise_db = new Gcp.Firestore.Database("enterprise-db", new()
    {
        Project = "my-project-name",
        Name = "database-id",
        LocationId = "nam5",
        Type = "FIRESTORE_NATIVE",
        DatabaseEdition = "ENTERPRISE",
        DeletionPolicy = "DELETE",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.firestore.Database;
import com.pulumi.gcp.firestore.DatabaseArgs;
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 enterprise_db = new Database("enterprise-db", DatabaseArgs.builder()
            .project("my-project-name")
            .name("database-id")
            .locationId("nam5")
            .type("FIRESTORE_NATIVE")
            .databaseEdition("ENTERPRISE")
            .deletionPolicy("DELETE")
            .build());

    }
}
resources:
  enterprise-db:
    type: gcp:firestore:Database
    properties:
      project: my-project-name
      name: database-id
      locationId: nam5
      type: FIRESTORE_NATIVE
      databaseEdition: ENTERPRISE
      deletionPolicy: DELETE

The databaseEdition property controls which feature set is available. ENTERPRISE edition provides higher performance guarantees and enables specialized data access modes. When using ENTERPRISE, the type must be FIRESTORE_NATIVE. The edition is immutable after creation.

Configure data access modes for Enterprise databases

Enterprise databases can enable specialized data access modes like Firestore API access, MongoDB-compatible APIs, or realtime updates, each serving different application integration patterns.

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

const firestoreAccessDatabase = new gcp.firestore.Database("firestore_access_database", {
    project: "my-project-name",
    name: "data-access-database-id",
    locationId: "nam5",
    type: "FIRESTORE_NATIVE",
    databaseEdition: "ENTERPRISE",
    firestoreDataAccessMode: "DATA_ACCESS_MODE_ENABLED",
    mongodbCompatibleDataAccessMode: "DATA_ACCESS_MODE_DISABLED",
    realtimeUpdatesMode: "REALTIME_UPDATES_MODE_DISABLED",
});
import pulumi
import pulumi_gcp as gcp

firestore_access_database = gcp.firestore.Database("firestore_access_database",
    project="my-project-name",
    name="data-access-database-id",
    location_id="nam5",
    type="FIRESTORE_NATIVE",
    database_edition="ENTERPRISE",
    firestore_data_access_mode="DATA_ACCESS_MODE_ENABLED",
    mongodb_compatible_data_access_mode="DATA_ACCESS_MODE_DISABLED",
    realtime_updates_mode="REALTIME_UPDATES_MODE_DISABLED")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := firestore.NewDatabase(ctx, "firestore_access_database", &firestore.DatabaseArgs{
			Project:                         pulumi.String("my-project-name"),
			Name:                            pulumi.String("data-access-database-id"),
			LocationId:                      pulumi.String("nam5"),
			Type:                            pulumi.String("FIRESTORE_NATIVE"),
			DatabaseEdition:                 pulumi.String("ENTERPRISE"),
			FirestoreDataAccessMode:         pulumi.String("DATA_ACCESS_MODE_ENABLED"),
			MongodbCompatibleDataAccessMode: pulumi.String("DATA_ACCESS_MODE_DISABLED"),
			RealtimeUpdatesMode:             pulumi.String("REALTIME_UPDATES_MODE_DISABLED"),
		})
		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 firestoreAccessDatabase = new Gcp.Firestore.Database("firestore_access_database", new()
    {
        Project = "my-project-name",
        Name = "data-access-database-id",
        LocationId = "nam5",
        Type = "FIRESTORE_NATIVE",
        DatabaseEdition = "ENTERPRISE",
        FirestoreDataAccessMode = "DATA_ACCESS_MODE_ENABLED",
        MongodbCompatibleDataAccessMode = "DATA_ACCESS_MODE_DISABLED",
        RealtimeUpdatesMode = "REALTIME_UPDATES_MODE_DISABLED",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.firestore.Database;
import com.pulumi.gcp.firestore.DatabaseArgs;
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 firestoreAccessDatabase = new Database("firestoreAccessDatabase", DatabaseArgs.builder()
            .project("my-project-name")
            .name("data-access-database-id")
            .locationId("nam5")
            .type("FIRESTORE_NATIVE")
            .databaseEdition("ENTERPRISE")
            .firestoreDataAccessMode("DATA_ACCESS_MODE_ENABLED")
            .mongodbCompatibleDataAccessMode("DATA_ACCESS_MODE_DISABLED")
            .realtimeUpdatesMode("REALTIME_UPDATES_MODE_DISABLED")
            .build());

    }
}
resources:
  firestoreAccessDatabase:
    type: gcp:firestore:Database
    name: firestore_access_database
    properties:
      project: my-project-name
      name: data-access-database-id
      locationId: nam5
      type: FIRESTORE_NATIVE
      databaseEdition: ENTERPRISE
      firestoreDataAccessMode: DATA_ACCESS_MODE_ENABLED
      mongodbCompatibleDataAccessMode: DATA_ACCESS_MODE_DISABLED
      realtimeUpdatesMode: REALTIME_UPDATES_MODE_DISABLED

The firestoreDataAccessMode, mongodbCompatibleDataAccessMode, and realtimeUpdatesMode properties control which APIs and features are available. These modes can only be specified for ENTERPRISE edition databases and are immutable after creation. Setting DATA_ACCESS_MODE_ENABLED for firestoreDataAccessMode allows standard Firestore API access, while mongodbCompatibleDataAccessMode enables MongoDB wire protocol compatibility. The realtimeUpdatesMode controls whether clients can subscribe to real-time data changes.

Beyond these examples

These snippets focus on specific database-level features: database modes (Firestore Native vs Datastore Mode), protection and recovery (delete protection, PITR), encryption and tagging, and Enterprise edition and data access modes. They’re intentionally minimal rather than full data platform deployments.

The examples may reference pre-existing infrastructure such as GCP projects with Firestore API enabled, KMS key rings and crypto keys (for CMEK examples), resource manager tags (for tagging examples), and IAM permissions for Firestore service accounts. They focus on configuring the database rather than provisioning everything around it.

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

  • App Engine integration configuration (appEngineIntegrationMode details)
  • Concurrency mode selection (OPTIMISTIC vs PESSIMISTIC vs OPTIMISTIC_WITH_ENTITY_GROUPS)
  • Location selection strategy and multi-region considerations
  • Database import/export and migration workflows

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

Let's deploy GCP Cloud Firestore Databases

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Database Types & Editions
What's the difference between FIRESTORE_NATIVE and DATASTORE_MODE?
FIRESTORE_NATIVE is for Firestore databases, while DATASTORE_MODE is for Datastore databases. The choice affects data model and API compatibility. See the decision guide for details.
What's the difference between STANDARD and ENTERPRISE editions?
ENTERPRISE edition requires type: FIRESTORE_NATIVE and enables additional features: firestoreDataAccessMode, mongodbCompatibleDataAccessMode, and realtimeUpdatesMode. STANDARD edition doesn’t support these features.
When should I use gcp.appengine.Application instead of gcp.firestore.Database?
Use gcp.appengine.Application if you need Firestore with App Engine. Use gcp.firestore.Database for standalone Firestore databases. If migrating from App Engine to standalone, follow the migration instructions.
Deletion & Protection
How do I actually delete my Firestore database?
You must set BOTH deleteProtectionState: DELETE_PROTECTION_DISABLED AND deletionPolicy: DELETE. The default deletionPolicy is ABANDON, which only removes the database from Terraform state without deleting it from Google Cloud.
What happens if I destroy my database with the default settings?
With the default deletionPolicy: ABANDON, the database is removed from Terraform state but remains in Google Cloud. You’ll need to manually delete it from the console or set deletionPolicy: DELETE before destroying.
Immutability & Resource Replacement
Which properties can't I change after creating the database?
These properties are immutable and cause resource replacement: databaseEdition, locationId, name, project, cmekConfig, tags, firestoreDataAccessMode, mongodbCompatibleDataAccessMode, and realtimeUpdatesMode.
How can I add tags to an existing database without recreating it?
The tags property is immutable on the database resource. To apply tags to an existing database, use the gcp.tags.TagValue resource instead.
Encryption & Security
How do I set up customer-managed encryption (CMEK) for my database?
Configure cmekConfig with your KMS key ID. You must first create an IAM binding granting the Firestore service account (service-{PROJECT_NUMBER}@gcp-sa-firestore.iam.gserviceaccount.com) the roles/cloudkms.cryptoKeyEncrypterDecrypter role, then use dependsOn to ensure proper ordering.
Data Access & Recovery
What's the difference between PITR enabled and disabled?
With POINT_IN_TIME_RECOVERY_ENABLED, you get 7-day retention with reads against any timestamp within the past hour and 1-minute snapshots beyond 1 hour. With POINT_IN_TIME_RECOVERY_DISABLED (default), you only get 1-hour retention.
How do I create the default Firestore database?
Set name: '(default)' when creating the database. This is a special valid database ID for the default database.
What are the naming requirements for database IDs?
Database IDs must be 4-63 characters, start with a letter, end with a letter or number, and contain only lowercase letters, numbers, and hyphens. They cannot be UUID-like. The special value (default) is also valid.

Using a different cloud?

Explore database guides for other cloud providers: