modules/controlplane
Source: github.com/spin-stack/spin-terraform-aws//modules/controlplane?ref=<tag>. Generated from its variables.tf and outputs.tf.
Variables
Section titled “Variables”| Variable | Type | Default |
|---|---|---|
spin_version | string | required |
domain | string | required |
identity_record_days | number | 400 |
collector | object({ metric_interval = optional(string, "60s") }) | {} |
name | string | "spin" |
internal_zone | string | "spin.internal" |
acme_email | string | "" |
vpc_cidr | string | "10.42.0.0/16" |
availability_zones | number | 3 |
instance_type | string | "t8i.medium" |
root_volume_gb | number | 40 |
admin_email | string | "" |
flow_logs | bool | true |
dns_query_logs | bool | false |
log_retention_days | number | 30 |
proxy_allowed_cidrs | list(string) | ["0.0.0.0/0"] |
proxy_instance_type | string | "t8i.micro" |
decommission | bool | false |
object_lock_days | number | 30 |
database_lock_days | number | 14 |
runner_policy | object({ shutdown_grace = optional(string, "100s") preemption_source = optional(string, "aws") }) | {} |
installation_config | string | "" |
runner_idle_minutes | number | 60 |
quiet_hours | string | "" |
time_zone | string | "UTC" |
tags | map(string) | {} |
image_ids | map(string) | {} |
monthly_budget_usd | number | the module’s own |
budget_emails | list(string) | [] |
image_measurements | map(list(object({ pcr4 = string pcr7 = string pcr12 = string statement = optional(string) bundle = optional(string) # Why the build is not to be run, as measurements.json says of a revoked one: no machine of it # is trusted, and a release whose newest build is revoked is not installed (attested_key.tf). revoked = optional(string) }))) | the module’s own |
spin_version
Section titled “spin_version”The release the installation runs, as v<YYYYMMDD>.<N>: every machine boots the Spin OS image of it (spin-stack/ami), published into this account and tagged spin:version.
- Type:
string - Required
domain
Section titled “domain”The base domain: the dashboard is app.<domain>, workspaces are *.ws.<domain>.
- Type:
string - Required
identity_record_days
Section titled “identity_record_days”How many days the record of each identity a host signs is kept (identity/issued/ in the volumes bucket): longer than the bucket’s lock, and long enough to answer who took an identity when.
- Type:
number - Default:
400
collector
Section titled “collector”The collector: how often metrics are pushed to it. Which Alloy it is, is the image’s.
- Type:
object({ metric_interval = optional(string, "60s") }) - Default:
{}
The installation’s name, one per region of an account: every resource’s prefix, the SSM path (/spin/<name>/…) each machine’s document is at, and - with the region - every IAM name. Two installations of one account may share a name in two regions, and not in one (claim.tf).
- Type:
string - Default:
"spin"
internal_zone
Section titled “internal_zone”The private zone the components reach each other by: cp.<this> and proxy.<this>, resolved only inside the VPC.
- Type:
string - Default:
"spin.internal"
acme_email
Section titled “acme_email”The address Let’s Encrypt writes to; empty is admin@<domain>.
- Type:
string - Default:
""
vpc_cidr
Section titled “vpc_cidr”The VPC’s range, a /16. One /20 public subnet per availability zone is carved from its first half, and one /24 for the proxy per zone from x.x.136.0 on.
- Type:
string - Default:
"10.42.0.0/16"
availability_zones
Section titled “availability_zones”How many zones to make subnets in. The control plane, the proxy and the runners may each be started in any of them, which is what gives a group somewhere to go when a zone runs out.
- Type:
number - Default:
3
instance_type
Section titled “instance_type”The control plane’s machine: the control plane, its database (PostgreSQL), the collector, and the stores of logs, traces and metrics it runs.
- Type:
string - Default:
"t8i.medium"
root_volume_gb
Section titled “root_volume_gb”The control plane’s disk: beside the read-only OS, /var takes the rest - the database, the logs’ and metrics’ stores and their caches. A new size is a new machine, replaced beside the old.
- Type:
number - Default:
40
admin_email
Section titled “admin_email”Who the first administrator signs in as; empty is admin@<domain>. The one-time password is in the admin_password_parameter output’s parameter.
- Type:
string - Default:
""
flow_logs
Section titled “flow_logs”Keep the VPC’s flow log in CloudWatch: every connection a security group accepted or refused. At a few hosts it is cents a month; a fleet whose workspaces move a lot of data pays about $0.50 a GB of flow records.
- Type:
bool - Default:
true
dns_query_logs
Section titled “dns_query_logs”Keep the VPC resolver’s query log in CloudWatch: every name looked up, a workspace’s included. It is Route 53 Resolver’s, and off unless asked for. Each installation that turns it on takes one of the ten CloudWatch Logs resource policies a region of an account may have.
- Type:
bool - Default:
false
log_retention_days
Section titled “log_retention_days”How long the flow and query logs are kept.
- Type:
number - Default:
30
proxy_allowed_cidrs
Section titled “proxy_allowed_cidrs”Where users may reach the proxy on 443 from: the dashboard, workspaces and SSH. Anywhere by default; an office’s or a VPN’s ranges close the installation to everyone else. The runners reach it from inside the VPC whatever this says, and 80 stays open for the ACME challenge.
- Type:
list(string) - Default:
["0.0.0.0/0"]
proxy_instance_type
Section titled “proxy_instance_type”The proxy’s machine: Caddy and nothing else, the one address the internet reaches.
- Type:
string - Default:
"t8i.micro"
decommission
Section titled “decommission”Set to true, and applied, before destroying the installation: its buckets lose the policies that refuse every object write from outside the VPC - the destroy runs from outside it - and a destroy empties every bucket, data included, bypassing the GOVERNANCE retention once the legal holds are lifted (README.md, “Removing an installation”). Never set on an installation that is to keep its data.
- Type:
bool - Default:
false
object_lock_days
Section titled “object_lock_days”The bucket’s default Object Lock retention, in GOVERNANCE mode. A deleted object’s bytes last this long, and the bucket’s lifecycle (bucket.tf) removes them a day after.
- Type:
number - Default:
30
database_lock_days
Section titled “database_lock_days”The database archive’s default Object Lock retention, in GOVERNANCE mode: how long what a delete or an overwrite left there can still be restored. The lifecycle removes it a day after (database_bucket.tf).
- Type:
number - Default:
14
runner_policy
Section titled “runner_policy”The host policy a runner starts with when it joins: how long it stays up once told to stop - a little less than the two minutes a spot reclaim gives - and the cloud that announces a reclaim.
- Type:
object({ shutdown_grace = optional(string, "100s") preemption_source = optional(string, "aws") }) - Default:
{}
installation_config
Section titled “installation_config”The installation’s config file (spin’s configs/spin-example.yaml), as YAML. This module adds what it knows - the domain, who joins as a host, the autoscaling settings below - and writes it where the control plane reads it at every start, which seeds the database from it before it serves. What the file later says differently is shown in the dashboard to apply or dismiss, and nothing is written over what an administrator decided.
- Type:
string - Default:
""
runner_idle_minutes
Section titled “runner_idle_minutes”How long the fleet has no workspace running, starting or waiting before the runners’ group is emptied.
- Type:
number - Default:
60
quiet_hours
Section titled “quiet_hours”HH:MM-HH:MM in time_zone when an idle fleet is emptied at once rather than after runner_idle_minutes. A workspace asked for then still brings a runner up. Empty is none.
- Type:
string - Default:
""
time_zone
Section titled “time_zone”The IANA zone quiet_hours are in.
- Type:
string - Default:
"UTC"
Tags on everything this module creates.
- Type:
map(string) - Default:
{}
image_ids
Section titled “image_ids”Spin OS AMIs of your own, one per role - control-plane, runner and proxy - instead of those images.json names for spin_version in this region. Each must carry that release, laid for that role: a machine refuses a document of another release, and an image laid for another role.
- Type:
map(string) - Default:
{}
monthly_budget_usd
Section titled “monthly_budget_usd”What the installation may cost in a month, in US dollars: an AWS Budget on its spin:installation tag (budget.tf), which the control plane reads and alerts reach. Null is no budget.
- Type:
number - Default:
null(the module’s own default)
budget_emails
Section titled “budget_emails”Who AWS emails when the month’s cost passes 80 % or 100 % of monthly_budget_usd, or is forecast to pass it.
- Type:
list(string) - Default:
[]
image_measurements
Section titled “image_measurements”The builds of each role’s image - control-plane, runner and proxy - oldest first, each the PCRs an instance of it measures (its manifest’s measurements, SHA384 in lowercase hex), instead of measurements.json’s: the control plane’s key is answered to the control plane’s builds (attested_key.tf), a host joins only as the runner’s, and the proxy is given its token only as the proxy’s (host_attestation). The control plane’s and the runner’s also carry statement and bundle, base64, as spin-os images sign writes them: what their images’ publisher signed of those PCRs. The last of a role’s is the image image_ids names. Required with image_ids, and for a release published before its PCRs were.
- Type:
map(list(object({ pcr4 = string pcr7 = string pcr12 = string statement = optional(string) bundle = optional(string) # Why the build is not to be run, as measurements.json says of a revoked one: no machine of it # is trusted, and a release whose newest build is revoked is not installed (attested_key.tf). revoked = optional(string) }))) - Default:
null(the module’s own default)
Outputs
Section titled “Outputs”| Output | What it is |
|---|---|
name | The installation’s name, claimed in its region (claim.tf), which the runners’ group and security group are named for. |
iam_name | What the installation’s IAM names begin with: its name and its region, since IAM is the account’s and two regions may each have an installation of one name. The runners’ role and instance profile are named for it. |
vpc_id | The VPC the runners go in. |
subnet_ids | One public subnet per zone, for the runners’ group. |
security_group_id | The control plane’s group; the runners module opens 8080 on it to its own. |
session_manager_policy_arn | Session Manager into a machine, and nothing else of SSM: what the runners’ role is given in place of AmazonSSMManagedInstanceCore, which reads every parameter. |
url | The control plane as runners reach it, the address its certificate names. |
ca_cert | The installation’s CA certificate, which every component trusts the control plane by. |
runner_document_arn | The runners’ document, the one object of the documents’ bucket the runners’ role reads. |
runner_user_data | A runner’s user data: its role and the runners’ document, as the systemd credentials Spin OS’s spin-boot takes. |
images | The Spin OS images of the installation’s release, by role - the control plane’s, the runners’ and the proxy’s: each one’s id and root device. The runners module boots the runner’s. |
boot_log_group | The CloudWatch log group every machine’s boot writes to, a stream per <role>/<step>/<instance-id>: a role’s machines are one stream prefix. |
boot_log_policy_arn | Writing a machine’s boot to that group, and nothing else: the runners module attaches it to the runners’ role. |
standard_vcpus | The on-demand standard vCPUs the control plane’s and the proxy’s machines take at most: two of each while an update replaces one. The runners module counts them against the quota on-demand runners share. |
name_servers | The domain’s public zone’s nameservers, which its registrar points at. |
domain | The base domain; the runners’ relay is tunnel.app.<domain>, dialled at relay_dial. |
public_zone_id | The domain’s public zone, where modules/identity-issuer puts id.<domain>. |
identity_key_arn | The KMS key the installation’s identity key is sealed under, which only an attested runner opens. |
identity_documents | The commands that write the identity issuer’s documents - discovery, key set, allowed signers and SPIFFE bundle - for modules/identity-issuer to publish: the identity key as the control plane keeps it in its bucket (its public half is all that is read) and the X.509 CA the first host to sign an SVID made beside it, then the documents from them. Run them with credentials that may read both, and commit what they write. |
internal_zone_id | The private zone the components reach each other by, cp.<internal_zone> and proxy.<internal_zone>. |
relay_dial | Where a runner dials the relay, host:port: the proxy inside the VPC, by the name the proxy of the moment points at itself. |
collector | Where every process of the installation pushes its telemetry, host:port. Where the collector sends it is set in the dashboard. |
boundary_arn | The permissions boundary every role of the installation carries; the runners module puts it on the runners’ role. |
proxy_ip | The proxy’s elastic IP, where app.<domain>, tunnel.app.<domain> and *.ws.<domain> point: the one address of the installation the internet reaches. |
proxy_group | The proxy’s group of one. Its machine, for aws ssm start-session --target (it has no SSH): aws autoscaling describe-auto-scaling-groups —auto-scaling-group-names <this> —query ‘AutoScalingGroups[0].Instances[0].InstanceId’. |
bucket | The volumes’ bucket: every workspace’s disk, versioned under Object Lock, reached only through the VPC’s endpoint. |
database_bucket | The database’s archive: the base backups and WAL the control plane ships as it writes, and restores from when its machine is replaced. Its role alone reaches it. |
controlplane_group | The control plane’s group of one. aws autoscaling start-instance-refresh --auto-scaling-group-name <this> replaces its machine beside itself; its machine is reached with aws ssm start-session, as the proxy’s is. |
admin_email | Who the first administrator signs in as. |
admin_password_parameter | The SSM SecureString holding the first administrator’s one-time password: aws ssm get-parameter —with-decryption —name <this>. The first sign-in asks for a new one. |
region | The region everything here is in, read from the provider rather than asked for. |
runner_idle_minutes | How long the fleet is idle before the runners’ group is emptied, which is why a first workspace waits for a machine’s boot. |
Generated by task reference from spin-terraform-aws/modules/controlplane/variables.tf at 1557e9668.
Documents Spinbox v20261019.02. This page as Markdown, for a model or a terminal.

