Skip to main content
Skip to main content

Terraform / OpenTofu

Use the Zerops provider to manage projects, services, deployments and routing as code. It works with both Terraform and OpenTofu.

Install​

terraform {
required_providers {
zerops = {
source = "zeropsio/zerops"
version = "~> 0.1"
}
}
}

provider "zerops" {}
  • Create a token in token management (see the guide) and export it as ZEROPS_TOKEN.
  • If you use OpenTofu, set source = "zeropsio/zerops". The provider is listed on the OpenTofu Registry, and tofu init installs the signed provider.
  • Explicit provider attributes override environment variables.

You can configure the provider with the following settings:

SettingEnvironment variableDefault / behavior
tokenZEROPS_TOKENRequired.
organization_idZEROPS_ORGANIZATION_IDAuto-selected when the token has one organization.
endpointZEROPS_ENDPOINThttps://api.app-prg1.zerops.io
postgresql_min_disk_gbZEROPS_POSTGRESQL_MIN_DISK_GB2 GB. PostgreSQL disk default and floor.
None, environment onlyZEROPS_CLICKHOUSE_MIN_RAM_GB0.375 GB. ClickHouse RAM floor.
None, environment onlyZEROPS_ELASTICSEARCH_MIN_RAM_GB2 GB. Elasticsearch RAM floor.
routing_force_syncZEROPS_ROUTING_FORCE_SYNCfalse. Synchronizes foreign staged routing changes only when enabled.

Resource Map​

The following table maps Zerops concepts to provider resources and data sources.

Zerops conceptResourcesData sources
Projectzerops_projectzerops_project, zerops_projects
Runtime / managed databasezerops_servicezerops_service
Project environment variableszerops_project_envNone
Service environment variableszerops_service_envzerops_service_env_vars
Object storagezerops_object_storageNone
Generated subdomainzerops_service_subdomain_accessNone
Custom domains / HTTP routingzerops_public_http_routingNone
Port routingzerops_public_port_routingNone
Dedicated IPv4zerops_project_dedicated_ipv4None
Deployzerops_service_deployNone
Restartzerops_service_restartNone
Git integrationzerops_service_git_integrationNone
Backupszerops_service_backup_configNone
VPN peerzerops_project_vpn_peerNone
L7 balancerzerops_project_l7_balancer_configNone
Remote loggingzerops_project_remote_loggingNone
Prometheus configurationzerops_project_prometheus_configNone
YAML project import / exportzerops_project_importzerops_project_export
Integration tokenszerops_integration_tokenNone
CatalogsNonezerops_regions, zerops_locations, zerops_service_types
Organization lookupNonezerops_organization

Minimal Example​

main.tf
terraform {
required_providers {
zerops = {
source = "zeropsio/zerops"
version = "~> 0.1"
}
}
}

provider "zerops" {}

resource "zerops_project" "example" {
name = "terraform-example"
}

resource "zerops_service" "app" {
project_id = zerops_project.example.id
hostname = "app"
type = "nodejs@22"
}

resource "zerops_service" "db" {
project_id = zerops_project.example.id
hostname = "db"
type = "postgresql:single@16"
}

resource "zerops_service_env" "database_url" {
service_id = zerops_service.app.id
key = "DATABASE_URL"
content = "$${db_connectionString}"
}

resource "zerops_service_deploy" "app" {
service_id = zerops_service.app.id
source_dir = "${path.module}/app"

zerops_yaml = <<-YAML
zerops:
- setup: app
build:
base: nodejs@22
buildCommands:
- npm ci
deployFiles: ./
run:
base: nodejs@22
ports:
- port: 3000
httpSupport: true
start: npm start
YAML

depends_on = [zerops_service_env.database_url, zerops_service.db]
}

resource "zerops_service_subdomain_access" "app" {
service_id = zerops_service.app.id
depends_on = [zerops_service_deploy.app]
}
  • app/ holds the Node.js app (package.json, package-lock.json, and npm start listening on port 3000). Keep Terraform files and state outside app/.
  • $$ escapes Terraform interpolation. Zerops resolves ${db_connectionString}. See referencing variables.
  • Subdomain access requires a deployed service with HTTP ports.

Deploy​

SourceConfigurationUse when
Local directorysource_dirUpload local source. .git directories are excluded.
Archivearchive_path; build = false for prebuilt archivesUpload a local .tar.gz source or ready-to-run archive.
Provider Gitgit = { mode = "provider", url = "...", ref = "..." }Pin a branch, tag, or full commit SHA. Use ZEROPS_GIT_TOKEN for private HTTPS repositories.
Server Gitgit = { mode = "server", url = "...", ref = "..." }Build a public repository's branch HEAD on Zerops.
  • Set exactly one source: source_dir, archive_path, or git.
  • Set zerops_yaml in every mode. See the zerops.yaml specification.
  • Server Git also requires a matching zerops.yaml in the repository.
  • Server Git builds the branch HEAD at build time, not the planned commit.
  • Deployments are immutable: input changes create a new application version. Destroying a deployment only forgets the invocation. Import is unsupported.

Import​

ResourceImport ID
zerops_projectPROJECT_ID
zerops_serviceSERVICE_ID or PROJECT_ID/HOSTNAME
zerops_object_storageSTORAGE_SERVICE_ID
zerops_project_envPROJECT_ID/KEY or canonical ENV_ID
zerops_service_envSERVICE_ID/KEY
zerops_public_http_routing, zerops_public_port_routingROUTING_ID
zerops_project_dedicated_ipv4PROJECT_ID (existing allocation only)
zerops_service_backup_config, zerops_service_git_integrationSERVICE_ID
zerops_service_subdomain_accessSERVICE_ID (access already enabled)
zerops_project_vpn_peerPROJECT_ID/PUBLIC_KEY
zerops_project_l7_balancer_config, zerops_project_remote_logging, zerops_project_prometheus_configPROJECT_ID
zerops_integration_tokenORGANIZATION_ID/TOKEN_ID (secret not recovered)
  • zerops_service_deploy, zerops_service_restart, and zerops_project_import cannot be imported.
  • Routing imports do not synchronize staged changes.

Behavior and Limitations​

  • Resources wait for asynchronous Zerops processes. Set per-resource timeouts. Operations and defaults are listed in each resource schema.
  • Routing mutations synchronize staged routes. A per-route force_sync overrides the provider's routing_force_sync, and forcing permits foreign staged changes.
  • Non-sudo tokens cannot detect secret-content drift when a refresh returns masked values. Importing masked secrets requires sudo credentials. Secret contents remain in state, so protect state and backend access.
  • Managed services that offer both single-node and highly available modes need an explicit mode in type, for example postgresql:single@16 or postgresql:ha@16. The shorthand postgresql@16 creates an HA service, and provider v0.1.2 and later reject it at plan time.
  • The PostgreSQL disk floor is set by postgresql_min_disk_gb. Adjust it when the catalog changes.
  • Project max_credit_limit is write-only: it cannot be read back or cleared.
  • Server-side Git supports only public repositories and branches.
  • A dedicated IPv4 address cannot be released. Destroying it only drops Terraform ownership, and billing continues until project deletion.
  • The Zerops public API is in beta, and the provider is pre-1.0. Breaking changes are possible in 0.x releases.
  • Avoid the reserved service hostnames core and l7httpbalancer.
  • Failed API calls may leave partial resources that require dashboard cleanup.