deplaneDocs Dashboard

Terraform and OpenTofu

Manage your apps, databases and variables from .tf files, with tofu apply or terraform apply, through the Deplane provider.

The provider works with OpenTofu 1.11 or newer and Terraform 1.11 or newer. It is not in the public registries yet: you install it from a folder (below).

Install

  1. Get the provider folder for your version (for example terraform-provider/0.1.0/) from your Deplane contact. It holds one zip per platform and a SHA256SUMS list.
  2. Check the zip for your platform against SHA256SUMS: sha256sum -c SHA256SUMS --ignore-missing.
  3. Tell OpenTofu (or Terraform) to take the provider from that folder. In ~/.tofurc (%APPDATA%\tofu.rc on Windows; for Terraform ~/.terraformrc or %APPDATA%\terraform.rc):
provider_installation {
  filesystem_mirror {
    path    = "/path/to/terraform-provider/0.1.0"
    include = ["deplane-cloud/deplane"]
  }
  direct {
    exclude = ["deplane-cloud/deplane"]
  }
}
  1. Run tofu init in your folder.

Your .tf files name the provider as deplane-cloud/deplane, which will not change when it is published.

Sign in

Create a CI token with deplane tokens create (it asks for your passkey) and give it to Terraform as DEPLANE_TOKEN:

export DEPLANE_TOKEN=...   # from a secret store, not a file in the repository
tofu plan

The provider block can also take token and api_url. A token is only ever sent to the address it was given with: a token from DEPLANE_TOKEN goes to DEPLANE_API_URL (or the default), never to an api_url written only in the file.

Example

terraform {
  required_providers {
    deplane = { source = "deplane-cloud/deplane", version = "~> 0.1" }
  }
}

provider "deplane" {}

variable "stripe_key" {
  type      = string
  sensitive = true
  ephemeral = true # kept out of saved plan files too
}

resource "deplane_postgres" "db" {
  name    = "shop-db"
  version = "18"
}

resource "deplane_app" "web" {
  name         = "shop-web"
  image        = "ghcr.io/acme/shop-web:1.4.2"
  port         = 3000
  health_check = "/healthz"
  instances    = 2
}

resource "deplane_attachment" "web_db" {
  app      = deplane_app.web.name
  database = deplane_postgres.db.name
}

resource "deplane_app_env" "web" {
  app       = deplane_app.web.name
  values    = { NODE_ENV = "production" }
  sensitive = { STRIPE_KEY = var.stripe_key }
  refs      = { DB_HOST = "$${postgres.host}" }
  depends_on = [deplane_attachment.web_db]
}

resource "deplane_worker" "jobs" {
  app     = deplane_app.web.name
  name    = "jobs"
  command = "node worker.js"
}

resource "deplane_domain" "www" {
  app      = deplane_app.web.name
  hostname = "www.acme.example"
}

Resources

ResourceImport idWhat it is
deplane_appshop-webAn app: image (optional), port, health_check, release_command, size, instances, max_instances, sleep_after_minutes, auto_rollback, private_network. Reads url and private_url.
deplane_app_envshop-web, or shop-web/buildSome of an app's variables, in one scope.
deplane_postgresshop-dbA Postgres database: version (17 or 18), size.
deplane_mysqlshop-mysqlA MySQL database: size (medium or large).
deplane_redisshop-cacheA Redis.
deplane_attachmentshop-web/postgres/shop-dbGives an app a database (database, mysql or redis): DATABASE_URL, MYSQL_URL or REDIS_URL.
deplane_workershop-web/jobsA worker: command, instances, size.
deplane_cron_jobshop-web/nightlyA cron job: command, schedule (UTC), size.
deplane_domainshop-web/www.acme.exampleA custom domain. Reads the DNS records to set: cname_target, txt_name, txt_value.

Data sources: deplane_app (an app by name) and deplane_database (by name and engine: postgres, mysql or redis). Neither ever returns a password or a variable's value.

Bring an existing object under Terraform with tofu import deplane_app.web shop-web, or an import block.

Fields you leave out of deplane_app keep what the app has. Every limit (sizes, instances, names, your plan's limits) comes from Deplane, and its error says what to change, with the same code as in Troubleshooting and error codes.

Images and deploys

Variables

Deleting

Deleting an app or a database needs a person's passkey, which a CI token cannot give. When a plan deletes one (it left the file, or a change such as a new Postgres version needs a new database), the apply stops at the delete and names the command:

Error: Terraform cannot delete shop-web

deplane_app: deleting an app needs a person's passkey, which a CI token cannot give.
Run "deplane apps destroy shop-web", then run tofu apply again: it will see shop-web is gone.

Run that command (it asks for your passkey), then tofu apply again. Nothing is created before the delete, so you never have two databases or none.

Variables, attachments, workers, cron jobs and domains are removed by the provider itself.

From a stack

deplane stack export NAME --format hcl > main.tf writes a stack as a .tf file with import blocks, so the first tofu apply takes over its existing apps and databases. What the provider cannot manage yet (a disk, ClickHouse, a GitHub or folder source) and values Deplane generated are left out and stay as they are; the file and the command list them. That first apply also deploys each app's image once more and sets its variables again with the same values. After it, run deplane stack forget NAME.

Not yet

This page is also available as Markdown. AI agents can start at /llms.txt.