Skip to content

Terraform & OpenTofu

The Aether provider manages clusters, node pools, network policy, members, webhooks, the registry and the billing budget. It works with Terraform 1.5+ and OpenTofu 1.6+ under the same source address, plane-shift/aether. Every command below works with terraform or tofu.

The provider is available from its first release, v0.1.0, on the Terraform Registry and the OpenTofu Registry. Until that release is published, terraform init cannot download it and the registry links on this page do not resolve.

Create a token in the portal under Settings → API Tokens. A token belongs to one organization, and the provider uses that organization for every resource. Tokens are managed in the portal only; the provider has no resource for them. Keep it out of your files: export AETHER_API_TOKEN=aether_....

terraform {
required_version = ">= 1.5" # OpenTofu >= 1.6 works the same way
required_providers {
aether = {
source = "plane-shift/aether"
version = "~> 0.1"
}
}
}
variable "aether_api_token" {
type = string
sensitive = true
default = null # unset here so the provider falls back to AETHER_API_TOKEN
}
# Create an API token in the Aether portal first (Settings → API Tokens).
provider "aether" {
api_token = var.aether_api_token # or AETHER_API_TOKEN
# endpoint defaults to https://api.aetherplatform.cloud (production);
# use https://dev.api.aetherplatform.cloud for the dev environment (or AETHER_ENDPOINT).
}

endpoint selects the API (production by default). Resources take no organization: it comes from the token. The region is eu-fi.

data "aether_k8s_versions" "offered" {}
resource "aether_cluster" "prod" {
name = "prod"
region = "eu-fi"
# latest is null while the platform's version window is unavailable; pin a
# minor instead (e.g. "1.37") if that's not acceptable.
k8s_version = data.aether_k8s_versions.offered.latest
# Defaults to true: the public API endpoint is billed as one public IP (€4/month).
public_endpoint = true
}
# Fixed pool: a flat monthly fee for desired_nodes nodes.
resource "aether_node_pool" "workers" {
cluster_id = aether_cluster.prod.id
name = "workers"
vcpus = 4
memory_gb = 16
disk_gb = 80
desired_nodes = 3
}
# Autoscaling pool: a vCPU/memory budget with max > min makes it autoscaling (billed hourly per node).
resource "aether_node_pool" "burst" {
cluster_id = aether_cluster.prod.id
name = "burst"
vcpus = 4
memory_gb = 16
disk_gb = 80
min_vcpus = 4
max_vcpus = 32
min_memory_gb = 16
max_memory_gb = 128
}
data "aether_kubeconfig" "prod" {
cluster_id = aether_cluster.prod.id
}
output "kubeconfig" {
value = data.aether_kubeconfig.prod.content
sensitive = true
}

apply creates the cluster and pools and waits until each is running. A cluster that ends degraded is reported as a warning.

Your organization is created when you register, so the provider cannot create one. Adopt it instead. The aether_organization data source exposes its id.

# Import-only: this resource cannot create an organization (see the import command below).
# The slug cannot be changed from Terraform; "" clears an optional field.
resource "aether_organization" "this" {
name = "Example Ltd"
billing_email = "billing@example.com"
vat_id = "FI12345678"
}
Terminal window
terraform import aether_organization.this 00000000-0000-0000-0000-000000000000
# OpenTofu: tofu import aether_organization.this 00000000-0000-0000-0000-000000000000

After the import, run terraform show to read the organization’s real name, billing_email and vat_id, then replace the placeholder values in the block above with them before you apply. Applying with the placeholders still in place would overwrite the organization’s real values with the example ones.

terraform output -raw kubeconfig > kubeconfig writes the admin kubeconfig, which has full control of the cluster. It is readable only while the cluster is running. Your state is a secret: it holds this kubeconfig, registry robot secrets and webhook signing secrets in plain text. Keep state in an encrypted backend with restricted access.

  • Public endpoint: public_endpoint defaults to true and costs €4/month.
  • Destroying aether_registry deletes every robot account of the organization. Images are kept unless purge_on_destroy = true, which is irreversible.
  • An enabled delete cleanup policy runs within about a minute of the apply. Start with action = "protect" or enabled = false.
  • aether_audit_log reads a single page (set limit). It is not the full history.
  • Secrets appear once. Robot secrets and the registry credentials are returned only on create; webhook signing secrets on create and on rotation (rotate_secret). They are empty after an import.
  • Some changes replace. Changing a pool’s name, vcpus, memory_gb or disk_gb, or a member’s role, destroys and recreates it.
  • Destroying aether_cluster_network opens the public endpoint to every address.

Full reference: from the first release (v0.1.0), the provider docs will be on the Terraform Registry and the OpenTofu Registry.