Terraform
Infrastructure is managed with Terraform, using Terraform Cloud as the state backend.
Modules
All modules live under iac/modules/:
digitalocean/ssh_key
Generates an ED25519 SSH keypair using the tls_private_key resource and registers the public key with DigitalOcean. The private key lives only in Terraform state — it is never written to disk or stored in a vault.
Outputs: id, fingerprint, private_key_openssh (sensitive), public_key_openssh
digitalocean/droplet
Creates a DigitalOcean droplet with the specified size, region, and SSH keys.
Outputs: id, urn, ipv4_address
digitalocean/project
Groups resources under a DigitalOcean project for organization.
cloudflare/dns
Creates A records for a list of subdomains pointing at the server IP. Uses for_each over the subdomain list.
Inputs: dns_root_zone, server_ip, subdomains (list of FQDNs)
Stages
Each deployment stage has its own Terraform root module under iac/stages/<stage>/. Currently there is one stage: production.
The production stage wires the modules together:
module "ssh_key" { ... }
module "droplet" { ... } # uses ssh_key.id
module "project" { ... } # uses droplet.urn
module "dns" { ... } # uses droplet.ipv4_address
Running Terraform
Use the bin/iac wrapper, which exports all required variables and provider tokens from confit:
bin/iac production plan
bin/iac production apply
bin/iac production output server_ip
Or via Make:
make infra ARGS="plan"
make infra ARGS="apply"
The wrapper automatically resolves:
TF_VAR_project_nameandTF_VAR_dns_root_zonefromconfit.tomlTF_VAR_subdomainsby iterating service domains- Provider tokens (
DIGITALOCEAN_TOKEN,CLOUDFLARE_API_TOKEN,TF_TOKEN_app_terraform_io) from 1Password
Backend
State is stored in Terraform Cloud. The backend is configured in iac/stages/production/backend.tf:
terraform {
cloud {
organization = "krondor-generic-org"
workspaces {
name = "krondor-generic-production"
}
}
}
Adding a stage
- Copy
iac/stages/production/toiac/stages/<new-stage>/ - Update
backend.tfwith a new workspace name - Add a
[vars]default or pass--set stage=<new-stage>to confit