iac/terraform
Sergei Poljanski 967ce9e5f6
terraform: drop the DNS import blocks
Both zones and all record sets are in state now; the import blocks
did their job. A plan without them shows no changes.

Assisted-by: Claude:opus-5-5
2026-11-12 23:23:56 +04:00
..
cloud-init terraform: never replace a running server 2026-11-12 06:47:08 +04:00
.gitignore terraform 2025-11-01 13:43:31 +02:00
dns.tf terraform: drop the DNS import blocks 2026-11-12 23:23:56 +04:00
firewall.tf terraform: XMPP and Xray ports on services, no Hetzner backups 2026-11-12 20:57:52 +04:00
keys.tf terraform 2025-11-01 13:43:31 +02:00
outputs.tf terraform 2025-11-01 13:43:31 +02:00
providers.tf terraform: require hcloud provider 1.70 2026-11-12 16:01:58 +04:00
README.md terraform: manage DNS for asxp.io and czsk.it 2026-11-12 22:46:53 +04:00
s3.tf terraform, ansible: drop the Immich buckets 2026-11-12 22:46:44 +04:00
servers.tf terraform: XMPP and Xray ports on services, no Hetzner backups 2026-11-12 20:57:52 +04:00
terraform.tfvars.example terraform 2025-11-01 13:43:31 +02:00
variables.tf terraform 2025-11-01 13:43:31 +02:00
vpc.tf terraform 2025-11-01 13:43:31 +02:00

ASXP.IO Infrastructure

Hetzner Cloud infrastructure managed via Terraform.

Servers

  • asxpio-ops (CX23: 2 vCPU, 4GB) - Traefik, Forgejo, app stacks
  • asxpio-services (CX43: 8 vCPU, 16GB) - CI runner, game servers, SillyTavern

Both have delete and rebuild protection. Hetzner's server backups are off; restic backs up the data instead (see ../README.md).

Both servers carry prevent_destroy and ignore changes to user_data, image and ssh_keys: cloud-init only runs at first boot, and hcloud would otherwise replace a running server when those change. Edits to cloud-init/*.yaml therefore describe a rebuild, not the live machine; day-to-day configuration is in ../ansible.

Storage (S3-compatible)

Terraform State:

  • asxpio-terraform-main (hel1)
  • asxpio-terraform-repl (fsn1)

Backups:

  • asxpio-backups-main (hel1)
  • asxpio-backups-repl (fsn1)

DNS

dns.tf holds the zones asxp.io and czsk.it (Hetzner DNS, this project) and every record set except SOA and NS. Our servers' records reference hcloud_server, so moving a service is one edit.

All record sets are change-protected except the test XMPP servers' (prosody, ejabberd, anotherim); both zones are delete-protected. To change a protected record set, apply twice: first with the set excluded from protection, then with the new value. Hand edits in the console show up as drift in terraform plan.

Network

  • VPC: 10.10.0.0/20
  • WireGuard VPN: 10.20.0.0/24
  • IPv4 + IPv6 enabled

Cost

Item Monthly
ops CX23 €3.99
services CX43 €11.99
IPv4 (2x) €1.00
S3 (1TB) €6.49
S3 1TB Traffic €1.00
Total €24.47

Prices are net, as billed in November 2026 (0% VAT).

Both servers are billed below the current list price (€5.49 and €15.99). A deleted and recreated server gets the list price, so reinstall or rebuild in place and never recreate. prevent_destroy and Hetzner's delete protection guard against it.

Setup

1. Initialize

terraform init

2. Configure secrets

Edit terraform.tfvars:

hcloud_token = "your-token"
s3_access_key = "your-key"
s3_secret_key = "your-secret"
s3_endpoint_hel1 = "hel1.your-objectstorage.com"
s3_endpoint_fsn1 = "fsn1.your-objectstorage.com"

3. Deploy

terraform plan
terraform apply

4. Enable S3 backend (optional)

After buckets are created:

# Set credentials
export AWS_ACCESS_KEY_ID="your-key"
export AWS_SECRET_ACCESS_KEY="your-secret"

# Or use ~/.aws/credentials file
mkdir -p ~/.aws
cat > ~/.aws/credentials <<EOF
[default]
aws_access_key_id = your-key
aws_secret_access_key = your-secret
EOF

# Uncomment backend in providers.tf
# Then migrate state
terraform init -migrate-state

DNS Management

Current: Managed manually via hcloud CLI

Zones: asxp.io (61604), czsk.it (61606), childfr.ee (61605)

TODO: Migrate to Terraform-managed DNS when provider v1.54.0+ is available:

# Upgrade provider in providers.tf
hcloud = {
  source  = "hetznercloud/hcloud"
  version = "~> 1.54"
}

# Import existing zones
terraform import hcloud_zone.asxp_io 61604
terraform import hcloud_zone.czsk_it 61606
terraform import hcloud_zone.childfr_ee 61605

# Example DNS resource
resource "hcloud_zone" "asxp_io" {
  name = "asxp.io"
  mode = "primary"
  labels = {
    managed_by = "terraform"
  }
}

resource "hcloud_zone_rrset" "apex_a" {
  zone = hcloud_zone.asxp_io.name
  name = "@"
  type = "A"
  records = [
    { value = "95.217.19.240", comment = "ops-server" },
  ]
}

See: https://github.com/hetznercloud/terraform-provider-hcloud/releases/tag/v1.54.0

Files

  • servers.tf - Server definitions + cloud-init
  • s3.tf - S3 buckets (6 total)
  • vpc.tf - Private network
  • firewall.tf - Security rules
  • dns.tf - DNS docs (manual management)
  • keys.tf - SSH keys
  • cloud-init/*.yaml - Server provisioning