The official Rivestack Terraform provider lets you manage clusters, databases, users, extensions, grants, and backups as code.
Installation
Add the provider to your Terraform configuration:
terraform {
required_providers {
rivestack = {
source = "rivestack/rivestack"
version = "~> 0.1"
}
}
}
provider "rivestack" {}
Requires Terraform >= 1.0.
Authentication
Get your API key from the Rivestack Dashboard. API keys use the rsk_ prefix.
export RIVESTACK_API_KEY="rsk_your_api_key"
provider "rivestack" {
api_key = "rsk_your_api_key"
}
Node capacity
HA clusters run on prepaid node capacity, not per-cluster billing. You buy a pool of nodes once, then Terraform (or the API) provisions clusters that draw from it — there’s no checkout in your terraform apply loop.
Buy capacity in the dashboard
In the Rivestack Dashboard, go to Billing → Subscribe to Nodes, choose a server type (starter, growth, or scale) and a quantity, then complete checkout. This is the only step that involves payment. Provision against the pool
terraform apply draws nodes from your pool automatically, matching on server_type. A growth cluster with node_count = 2 consumes two growth nodes from your growth pool. There is no extra charge per cluster.
If your pool has no free nodes of the requested server_type, the API returns 402 Payment Required (no available nodes; subscribe to nodes from the billing page first) and terraform apply fails. Buy more capacity of that server type, then re-run.
Provision with the REST API
If you don’t use Terraform, call the same endpoint directly. Create an API key, buy capacity, then:
# Provision a cluster from your prepaid pool (draws a matching node automatically)
curl -X POST https://api.rivestack.io/api/ha/provision \
-H "Authorization: Bearer rsk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "production-cluster",
"region": "eu-central",
"server_type": "growth",
"node_count": 2,
"postgresql_version": 18
}'
# region accepts "eu-central", "us-east", or "ap" (Singapore)
# → { "id": 42, "tenant_id": "rs-…", "status": "provisioning", … }
# Poll until status is "active"
curl https://api.rivestack.io/api/ha/42 \
-H "Authorization: Bearer rsk_your_api_key"
Omit server_type to default to starter. The cluster is matched to a pool of the same server type and capacity is found automatically — no pool ID needed.
Quick start
This example creates a 2-node HA cluster with a database and user:
terraform {
required_providers {
rivestack = {
source = "rivestack/rivestack"
version = "~> 0.1"
}
}
}
provider "rivestack" {}
# Create an HA PostgreSQL cluster
resource "rivestack_cluster" "production" {
name = "production-cluster"
region = "eu-central"
server_type = "growth"
node_count = 2
postgresql_version = 18
}
# Create a database
resource "rivestack_cluster_database" "analytics" {
cluster_id = rivestack_cluster.production.id
name = "analytics"
owner = rivestack_cluster.production.db_user
}
# Create a user
resource "rivestack_cluster_user" "app_user" {
cluster_id = rivestack_cluster.production.id
username = "app_user"
}
# Outputs
output "cluster_host" {
value = rivestack_cluster.production.host
}
output "connection_string" {
value = rivestack_cluster.production.connection_string
sensitive = true
}
output "app_user_password" {
value = rivestack_cluster_user.app_user.password
sensitive = true
}
terraform init
terraform plan
terraform apply
Resources
rivestack_cluster
Manages an HA PostgreSQL cluster.
resource "rivestack_cluster" "example" {
name = "my-cluster"
region = "eu-central"
server_type = "starter"
node_count = 2
postgresql_version = 18
}
| Attribute | Type | Required | Description |
|---|
name | String | Yes | Display name for the cluster |
region | String | Yes | Region: eu-central, us-east, or ap (Singapore) |
server_type | String | No | Server size: starter, growth, or scale |
node_count | Number | No | Number of nodes (1-3) |
postgresql_version | Number | No | PostgreSQL major version (16, 17, or 18) |
db_name | String | No | Name of the default database |
db_type | String | No | Cluster type: ha or core_solo |
extensions | List | No | Extensions to install at creation time |
Read-only attributes: id, host, connection_string, db_user, db_password, status, tenant_id, created_at, updated_at
rivestack_cluster_database
Creates a database on a cluster.
resource "rivestack_cluster_database" "example" {
cluster_id = rivestack_cluster.example.id
name = "analytics"
owner = rivestack_cluster.example.db_user
}
| Attribute | Type | Required | Description |
|---|
cluster_id | String | Yes | ID of the cluster |
name | String | Yes | Database name |
owner | String | No | Database owner username (defaults to cluster’s default user) |
rivestack_cluster_user
Creates a database user with an auto-generated password.
resource "rivestack_cluster_user" "example" {
cluster_id = rivestack_cluster.example.id
username = "app_user"
}
| Attribute | Type | Required | Description |
|---|
cluster_id | String | Yes | ID of the cluster |
username | String | Yes | Username (letters, numbers, underscores; max 63 chars) |
Read-only attributes: id, password (sensitive)
rivestack_cluster_grant
Grants a user access to a database.
resource "rivestack_cluster_grant" "example" {
cluster_id = rivestack_cluster.example.id
username = rivestack_cluster_user.example.username
database = rivestack_cluster_database.example.name
access = "read"
}
| Attribute | Type | Required | Description |
|---|
cluster_id | String | Yes | ID of the cluster |
username | String | Yes | Username to grant access to |
database | String | Yes | Database to grant access on |
access | String | No | Access level: read (SELECT) or write (SELECT, INSERT, UPDATE, DELETE) |
Grant revocation is not currently supported by the API. Destroying this resource removes it from Terraform state only.
rivestack_cluster_extension
Installs a PostgreSQL extension on a database.
resource "rivestack_cluster_extension" "vector" {
cluster_id = rivestack_cluster.example.id
extension = "vector"
database = rivestack_cluster_database.example.name
}
| Attribute | Type | Required | Description |
|---|
cluster_id | String | Yes | ID of the cluster |
extension | String | Yes | Extension name (e.g., vector, pg_trgm, pgcrypto) |
database | String | No | Target database (defaults to cluster’s primary database) |
Extensions cannot be removed from running clusters. Destroying this resource removes it from Terraform state only.
rivestack_cluster_backup_config
Configures automated backups for a cluster.
resource "rivestack_cluster_backup_config" "example" {
cluster_id = rivestack_cluster.example.id
enabled = true
schedule = "0 3 * * *"
retention_full = 14
}
| Attribute | Type | Required | Description |
|---|
cluster_id | String | Yes | ID of the cluster |
enabled | Boolean | Yes | Whether automated backups are enabled |
schedule | String | No | Cron schedule (e.g., "0 3 * * *" for daily at 3 AM) |
retention_full | Number | No | Number of days to retain full backups |
Data sources
rivestack_cluster
Read information about an existing cluster.
data "rivestack_cluster" "existing" {
id = "42"
}
output "cluster_host" {
value = data.rivestack_cluster.existing.host
}
Read-only attributes: name, region, server_type, node_count, postgresql_version, host, connection_string, db_name, db_user, db_password, db_type, status, health_status, tenant_id, created_at, updated_at
rivestack_server_types
List available server configurations.
data "rivestack_server_types" "available" {}
rivestack_extensions
List supported PostgreSQL extensions.
data "rivestack_extensions" "available" {}
Import
Import existing resources into Terraform state:
# Cluster
terraform import rivestack_cluster.example CLUSTER_ID
# Database
terraform import rivestack_cluster_database.example CLUSTER_ID/DATABASE_NAME
# User
terraform import rivestack_cluster_user.example CLUSTER_ID/USERNAME
# Extension
terraform import rivestack_cluster_extension.example CLUSTER_ID/EXTENSION/DATABASE
# Grant
terraform import rivestack_cluster_grant.example CLUSTER_ID/USERNAME/DATABASE
# Backup config
terraform import rivestack_cluster_backup_config.example CLUSTER_ID
Links