Skip to main content
Terraform is an open-source infrastructure-as-code tool that lets you define and manage cloud resources through declarative configuration files. With the PlanetScale Terraform provider, you can programmatically provision and manage your PlanetScale databases, branches, passwords, and other resources alongside the rest of your infrastructure.

Conceptual model

The PlanetScale Terraform provider is organized around a clear distinction between Vitess and Postgres resources and is focused on long-lived infrastructure objects.
  • There are separate resources for each database kind—Vitess or Postgres.
  • The provider maps cleanly onto PlanetScale’s public API while exposing Terraform-friendly fields and lifecycle behavior.
PlanetScale databases themselves (the logical database containers) are not managed directly as Terraform resources in v1.0. Instead:
  • Create databases in PlanetScale first (via the PlanetScale UI or API).
  • Use Terraform to manage branches and related resources (roles, passwords, etc.) within those databases.

Credential models

Vitess and Postgres use different terminology for database credentials:
Database TypeResourceHow permissions work
Vitessplanetscale_vitess_branch_passwordSet the role attribute to reader, writer, readwriter, or admin
Postgresplanetscale_postgres_branch_roleUse inherited_roles to inherit from built-in Postgres roles like pg_read_all_data or pg_write_all_data

Quick start

This complete example creates a Postgres branch with application credentials:
terraform {
  required_providers {
    planetscale = {
      source  = "planetscale/planetscale"
      version = "~> 1.0"
    }
  }
}

# Manage a branch (database must already exist in PlanetScale)
resource "planetscale_postgres_branch" "main" {
  organization = "my-org"
  database     = "my-db" # prefer database id over name
  name         = "main"
}

# Create application credentials
resource "planetscale_postgres_branch_role" "app" {
  organization    = "my-org"
  database        = "my-db"
  branch          = planetscale_postgres_branch.main.id
  name            = "app"
  inherited_roles = ["pg_read_all_data", "pg_write_all_data"]
}

# Output connection details
output "connection_string" {
  sensitive = true
  value     = "postgres://${planetscale_postgres_branch_role.app.username}:${planetscale_postgres_branch_role.app.password}@${planetscale_postgres_branch_role.app.access_host_url}/${planetscale_postgres_branch_role.app.database_name}"
}
Run:
export PLANETSCALE_SERVICE_TOKEN_ID="your-token-id"
export PLANETSCALE_SERVICE_TOKEN="your-token"
terraform init
terraform apply
terraform output -raw connection_string

Available resources

The provider offers resources for the most common automation scenarios:
  • planetscale_vitess_branch
  • planetscale_vitess_branch_password
  • planetscale_postgres_branch
  • planetscale_postgres_branch_role

Data sources

The provider also offers data sources for reading existing resources:
  • planetscale_databases
  • planetscale_database_postgres
  • planetscale_database_vitess
  • planetscale_organization
  • planetscale_organizations
  • planetscale_postgres_branch
  • planetscale_postgres_branch_role
  • planetscale_postgres_branch_roles
  • planetscale_vitess_branch
  • planetscale_vitess_branch_password
  • planetscale_vitess_branch_passwords

Data source behavior

Data SourceRequired InputsNotes
planetscale_databasesorganizationLists all databases. Optional q parameter for filtering by name.
planetscale_database_postgresorganizationReturns Postgres database info. Does not accept a database name filter.
planetscale_database_vitessorganizationReturns Vitess database info. Does not accept a database name filter.
planetscale_postgres_branchorganization, databaseReturns the default branch. Does not accept a branch name parameter.
planetscale_postgres_branch_rolesorganization, database, branchLists all roles on a branch.
planetscale_postgres_branch_roleorganization, database, branch, idFetches a specific role by ID. Useful for importing existing roles.
planetscale_vitess_branchorganization, databaseReturns the default branch. Does not accept a branch name parameter.
planetscale_vitess_branch_passwordsorganization, database, branchLists all passwords on a branch.
planetscale_vitess_branch_passwordorganization, database, branch, idFetches a specific password by ID.
planetscale_organizationorganizationReturns organization details.
planetscale_organizations(none)Lists all accessible organizations.
Refer to the Terraform Registry and provider documentation for the full, up-to-date list of available resources and data sources.

Provider configuration

The provider supports authentication using service tokens. Example configuration:
terraform {
  required_providers {
    planetscale = {
      source  = "planetscale/planetscale"
      version = "~> 1.0"
    }
  }
}

provider "planetscale" {
  # Credentials are read from environment variables by default:
  #   PLANETSCALE_SERVICE_TOKEN_ID
  #   PLANETSCALE_SERVICE_TOKEN
  #
  # You can also configure them explicitly (not recommended for production):
  # service_token_id = "..."
  # service_token    = "..."
}
Provider AttributeEnvironment VariableDescription
service_token_idPLANETSCALE_SERVICE_TOKEN_IDPlanetScale Service Token ID
service_tokenPLANETSCALE_SERVICE_TOKENPlanetScale Service Token
server_urlPLANETSCALE_SERVER_URLOptional server URL override

Example usage

Vitess branch and password

resource "planetscale_vitess_branch" "app" {
  organization = "my-org"
  database     = "my-vitess-db"
  name         = "app-main"

  # Optional configuration:
  # region        = "us-east-1"   # Region for the branch
  # parent_branch = "main"        # Fork from an existing branch
  # backup_id     = "..."         # Restore from a specific backup
}

resource "planetscale_vitess_branch_password" "app_rw" {
  organization = "my-org"
  database     = "my-vitess-db"
  branch       = planetscale_vitess_branch.app.name
  name         = "app-rw"
  role         = "admin"  # Options: reader, writer, readwriter, admin

  # Optional configuration:
  # replica       = false              # Connect to read replica
  # direct_vtgate = false              # Direct VTGate connection
  # ttl           = 86400              # Credentials expire after N seconds
  # cidrs         = ["10.0.0.0/8"]     # IP allowlist
}

Postgres branch and role

resource "planetscale_postgres_branch" "primary" {
  organization = "my-org"
  database     = "my-postgres-db"
  name         = "primary"

  # Optional configuration:
  # cluster_size  = "PS_10_AWS_ARM"
  # region        = "us-east-1"                # Region for the branch
  # parent_branch = "main"                     # Fork from an existing branch
  # major_version = "18"                       # PostgreSQL major version
  # backup_id     = "..."                      # Restore from a specific backup
  # restore_point = "2024-01-01T00:00:00Z"     # Point-in-time recovery (Postgres only)
}

resource "planetscale_postgres_branch_role" "app_role" {
  organization = "my-org"
  database     = "my-postgres-db"
  branch       = planetscale_postgres_branch.primary.id
  name         = "app_role"

  # Grant permissions by inheriting from built-in Postgres roles
  inherited_roles = [
    "pg_read_all_data",
    "pg_write_all_data",
  ]

  # Optional configuration:
  # ttl       = 86400        # Credentials expire after N seconds
  # successor = "other-role" # Role to reassign ownership to before dropping
}
Immutable attributes: Changing region, parent_branch, major_version, backup_id, or restore_point will destroy and recreate the branch. Use lifecycle { prevent_destroy = true } to protect production branches from accidental replacement.

Retrieving connection details

After creating a role or password, use Terraform outputs to retrieve connection information:

Postgres

output "postgres_connection" {
  description = "Postgres connection details"
  sensitive   = true
  value = {
    host     = planetscale_postgres_branch_role.app_role.access_host_url
    username = planetscale_postgres_branch_role.app_role.username
    password = planetscale_postgres_branch_role.app_role.password
    database = planetscale_postgres_branch_role.app_role.database_name
  }
}

Vitess

output "vitess_connection" {
  description = "Vitess connection details"
  sensitive   = true
  value = {
    host     = planetscale_vitess_branch_password.app_rw.access_host_url
    username = planetscale_vitess_branch_password.app_rw.username
    password = planetscale_vitess_branch_password.app_rw.plain_text
  }
}
The password (Postgres) and plain_text (Vitess) fields are only available after the initial terraform apply. They are marked as sensitive and stored in Terraform state.

Practical examples

Development branch workflow

Create a development branch forked from your main branch. Optionally specify a larger cluster size if you need more resources than the default:
resource "planetscale_postgres_branch" "feature_auth" {
  organization  = "my-org"
  database      = "my-db"
  name          = "feature-auth"
  parent_branch = "main"
  # cluster_size = "PS_10_AWS_ARM"  # Optional: specify a larger cluster size
}

Read-only role for reporting

Create a role with read-only access for analytics and reporting:
resource "planetscale_postgres_branch_role" "reporting" {
  organization    = "my-org"
  database        = "my-db"
  branch          = "main"
  name            = "reporting"
  inherited_roles = ["pg_read_all_data"]  # Read-only access
}

Short-lived CI/CD credentials

Create credentials that automatically expire for CI/CD pipelines:
resource "planetscale_postgres_branch_role" "ci_runner" {
  organization    = "my-org"
  database        = "my-db"
  branch          = "main"
  name            = "ci-runner"
  inherited_roles = ["pg_read_all_data", "pg_write_all_data"]
  ttl             = 3600  # Expires in 1 hour
}

Configuration reference

AttributeValid Values
cluster_sizeUse the Clusters API to get the list of available cluster sizes
regionSee available regions
major_version (Postgres)17, 18
role (Vitess)reader, writer, readwriter, admin
inherited_roles (Postgres)pg_read_all_data, pg_write_all_data, or any custom role

Deletion protection

Branch deletion is one of the most sensitive operations in infrastructure automation. To reduce risk, use Terraform’s lifecycle block to prevent accidental destruction of critical resources:
resource "planetscale_vitess_branch" "production" {
  organization = "my-org"
  database     = "my-vitess-db"
  name         = "main"

  lifecycle {
    prevent_destroy = true
  }
}
You should enforce your own review and approval processes around Terraform apply, particularly for changes that delete or recreate branches.

Upgrading from v0.x to v1.x

The original PlanetScale Terraform provider was not officially supported for production use and is no longer maintained. Because the new v1 provider is a breaking rewrite, migration from v0.x to v1.x is a one-time, manual transition. Projects using the v0 provider will continue to work as normal, but this version will not receive further updates.
  1. Pin v0.x in existing workloads
    • Ensure all existing Terraform projects using the PlanetScale provider are pinned to ~> 0.6.1 (or a specific v0.x version) to avoid unintentional upgrades.
  2. Create a new Terraform project for v1.x
    • Create a separate directory with a new configuration using the v1 provider.
    • Use the v1 resource types for Vitess/Postgres branches, roles, passwords, and other supported resources.
    • Run terraform init to download the v1 provider and initialize your working directory. This creates a new, independent state file.
  3. Import existing resources into v1.x state
    • For each resource that you want Terraform to manage going forward, run terraform import for the corresponding v1 resource.
    • Expect import to use IDs that align with the v1 API design (for example, IDs rather than purely name-based identifiers).
  4. Cut over automation
    • After resources are imported and terraform plan shows no unexpected changes, switch your automation (CI pipelines, etc.) to apply the v1 project.
  5. Sunset v0.x usage
    • Once you have validated v1.x in production, retire the v0.x configurations and keep them only for historical reference, if needed.

Importing existing resources

Resources are imported using JSON-encoded identifiers:
# Import a Postgres branch
terraform import planetscale_postgres_branch.main \
  '{"organization": "my-org", "database": "my-db", "id": "branch-id"}'

# Import a Postgres branch role
terraform import planetscale_postgres_branch_role.app \
  '{"organization": "my-org", "database": "my-db", "branch": "main", "id": "role-id"}'

# Import a Vitess branch
terraform import planetscale_vitess_branch.main \
  '{"organization": "my-org", "database": "my-db", "id": "branch-id"}'

# Import a Vitess branch password
terraform import planetscale_vitess_branch_password.app \
  '{"organization": "my-org", "database": "my-db", "branch": "main", "id": "password-id"}'
You can find resource IDs in the PlanetScale dashboard URL or via the API.

Need help?

Get help from the PlanetScale Support team, or join our Discord community to see how others are using PlanetScale.