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 FREEFrequently Asked Questions
Database Types & Editions
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.type: FIRESTORE_NATIVE and enables additional features: firestoreDataAccessMode, mongodbCompatibleDataAccessMode, and realtimeUpdatesMode. STANDARD edition doesn’t support these features.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
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.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
databaseEdition, locationId, name, project, cmekConfig, tags, firestoreDataAccessMode, mongodbCompatibleDataAccessMode, and realtimeUpdatesMode.tags property is immutable on the database resource. To apply tags to an existing database, use the gcp.tags.TagValue resource instead.Encryption & Security
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
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.name: '(default)' when creating the database. This is a special valid database ID for the default database.(default) is also valid.