Terraform provider
The capy-base/capydb provider for Terraform and OpenTofu — projects, preview databases, API keys, and webhook endpoints as code.
What it is
The official Terraform/OpenTofu provider for CapyDB, built on terraform-plugin-framework (protocol v6; Terraform ≥ 1.0 and OpenTofu). It manages the control-plane surface — it does not manage what is inside your database; that stays plain SQL and migrations.
Provider configuration
terraform {
required_providers {
capydb = {
source = "capy-base/capydb"
version = "~> 0.1"
}
}
}
provider "capydb" {
api_key = var.capydb_api_key # sensitive; falls back to CAPYDB_API_KEY
# api_url falls back to CAPYDB_API_URL, then https://capydb.dev/api/capydb
}The API key is an organization key (capy_live_...). The provider's operations need these scopes: projects:read/projects:write (projects, previews), credentials:read (the connection data source), and jobs:read (waiting on async jobs).
Resources and data sources
| Resource | Purpose |
|---|---|
capydb_project | A managed logical Postgres database. Async provision/delete; environment updatable in place; importable by id. |
capydb_preview_database | Disposable preview/branch database with a TTL. Raising ttl_hours extends in place; lowering forces replacement. |
capydb_api_key | Org (or project-scoped) API key. Plaintext captured once into the sensitive token attribute; delete revokes. |
capydb_webhook_endpoint | Outbound webhook receiver. Bump secret_version to rotate the HMAC signing secret in place. |
| Data source | Purpose |
|---|---|
capydb_clusters | Active regions projects can be placed on. |
capydb_project | Look up a project by id or slug (exactly one). |
capydb_project_connection | Pooled/direct connection URLs with credentials embedded (sensitive). |
capydb_organization | The org behind the configured key, including billing plan/status. |
Quickstart
resource "capydb_project" "app" {
name = "my-app"
environment = "production"
}
# Ephemeral clone of the production database for a pull request.
resource "capydb_preview_database" "pr" {
project_id = capydb_project.app.id
name = "pr-42"
mode = "clone"
ttl_hours = 48
}
data "capydb_project_connection" "app" {
project_id = capydb_project.app.id
}
output "database_url" {
value = data.capydb_project_connection.app.pooled_url
sensitive = true
}Asynchronous jobs and timeouts
Project provision/delete and preview create/delete are asynchronous jobs. The provider enqueues the job and polls /v1/jobs/{id} every 5 seconds until it reaches completed or failed, bounded by the resource's timeouts attribute (create/delete, both defaulting to 20 minutes):
resource "capydb_project" "app" {
name = "my-app"
timeouts = {
create = "30m"
delete = "30m"
}
}On create, the resource id is written to state before waiting, so a timed-out or failed job never orphans the project or preview — re-run terraform apply or inspect the job in the dashboard.
Secrets land in state
capydb_api_key.token, capydb_webhook_endpoint.signing_secret, and the capydb_project_connection URLs are sensitive values — the first two are returned exactly once by the API, and the connection URLs embed live credentials. All of them are persisted in Terraform state. Treat the state file accordingly: encrypted remote backend, restricted access. If state leaks, rotate.
A practical note on empty lists
CapyDB list endpoints may serialize empty lists as JSON null. The provider normalizes every list it reads to an empty, non-null list, so length(data.capydb_clusters.available.clusters) and friends are always safe.