Files
cc-ci/terraform
autonomic-bot 4c7150d502 terraform: provision cc-ci on Hetzner Cloud via nixos-infect
Adds terraform/ (hcloud provider, cpx32/nbg1/debian-12) and a new
nix/hosts/cc-ci-hetzner/ flake host to provision the cc-ci server on
Hetzner Cloud as an alternative to the Incus cc-nix-test VM.

Stage 1 (Terraform): creates a cpx32 server (4 vCPU / 8 GB / x86 AMD,
Nuremberg), runs nixos-infect (pinned rev 40f62a6, 2026-03-22) to convert
Debian 12 → NixOS 24.11, and reboots into bare NixOS.

Stage 2 (manual, per terraform/README.md): clone cc-ci --recursive,
provision the bootstrap age key, then `nixos-rebuild switch --flake
.#cc-ci-hetzner`.

Verified (throwaway run 2026-05-31, server 134464512, 168.119.126.100):
- terraform apply: cpx32 in nbg1 created in 17 s
- nixos-infect: NixOS 24.11.719113.50ab793786d9 (same nixpkgs pin as flake)
- nixos-rebuild build --flake .#cc-ci-hetzner: exit 0 on server
  (131 derivations; all cc-ci modules: tailscale, drone, drone-runner,
  bridge, dashboard, harness, swarm, abra, proxy, secrets)
- terraform plan: no changes (idempotent)
- terraform destroy: server + SSH key removed

Age key step (plan §4 Stage 2): operator-pending. Full switch/convergence
requires bootstrap age key at /var/lib/sops-nix/key.txt. Flake builds
without it; activation needs it.

No secrets committed: HCLOUD_TOKEN via env, tfstate gitignored,
networking.nix contains throwaway IP (update per README for production).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 01:11:56 +00:00
..

cc-ci Hetzner Cloud Terraform

Provisions the cc-ci NixOS server on Hetzner Cloud (cpx32, 4 vCPU / 8 GB, x86 AMD, nbg1). Stage 1 (Terraform): creates the server, runs nixos-infect to convert Debian 12 → NixOS. Stage 2 (manual): clone the flake + apply the cc-ci config.

Prerequisites (Class-A1 inputs — provide at apply time, NEVER commit)

Input How to provide
HCLOUD_TOKEN export HCLOUD_TOKEN=<token> in shell before tofu apply
SSH key pair Generate once: ssh-keygen -t ed25519 -f ~/.ssh/cc-ci-hetzner; pass pubkey via TF_VAR_ssh_public_key="$(cat ~/.ssh/cc-ci-hetzner.pub)"
Bootstrap age key Provision to /var/lib/sops-nix/key.txt on the server (Stage 2; see docs/install.md)

Stage 1 — Provision server + nixos-infect

cd terraform/

# Provide secrets via environment
export HCLOUD_TOKEN=<your-token>
export TF_VAR_ssh_public_key="$(cat ~/.ssh/cc-ci-hetzner.pub)"

# Download providers (uses .terraform.lock.hcl — pinned, reproducible)
tofu init   # or: terraform init

# Preview
tofu plan

# Apply — creates cpx31 server in nbg1, runs nixos-infect on first boot
tofu apply

# Note the output IP:
#   server_ipv4 = "x.x.x.x"
#   ssh_connect = "ssh root@x.x.x.x"

nixos-infect runs on first boot and reboots the server into NixOS (~5 min total). Wait for the reboot to complete, then verify:

# Check NixOS is up:
ssh root@<ip> 'nixos-version'

# Inspect infect log if needed:
ssh root@<ip> 'cat /var/log/nixos-infect.log'

After the reboot the server runs bare NixOS (infect-generated config). Proceed to Stage 2.

Stage 2 — Apply the cc-ci flake config

Follows the D8 install flow documented in docs/install.md exactly:

# On the Hetzner server (ssh root@<ip>):

# 1. Clone the flake (--recursive brings cc-ci-secrets submodule)
git clone --recursive https://git.autonomic.zone/recipe-maintainers/cc-ci.git /etc/cc-ci
cd /etc/cc-ci

# 2. Provision the bootstrap age key (the one irreducible out-of-band secret)
mkdir -p /var/lib/sops-nix
install -m 0600 /dev/stdin /var/lib/sops-nix/key.txt <<'EOF'
<paste bootstrap age private key here — see docs/install.md>
EOF

# 3. Apply the cc-ci Hetzner host config
nixos-rebuild switch --flake .#cc-ci-hetzner

# 4. Verify (all units green, reconcile oneshots converged)
systemctl --failed

Variables

Variable Default Description
server_type cpx31 x86 only. cpx31=AMD 4vCPU/8GB, cx33=Intel 4vCPU/8GB. Never cax* (ARM).
location nbg1 Hetzner datacenter.
image debian-12 Base image; nixos-infect converts it to NixOS. debian-12 preferred.
server_name cc-ci Hetzner server name.
ssh_public_key (required) Public key registered for root access.

Override via env: TF_VAR_location=hel1 tofu apply.

Teardown (throwaway verification run)

tofu destroy   # removes server + SSH key; billing stops immediately

Notes

  • .terraform.lock.hcl is committed (pins provider SHAs — analogous to flake.lock).
  • *.tfstate, *.tfvars, .terraform/ are gitignored — never commit state or secrets.
  • cpx31 is retired in some Hetzner DCs; cpx32 (equivalent AMD, 4 vCPU / 8 GB) is the default. cx33 (Intel, same spec) is also available. Both are x86_64 — compatible with the x86_64-linux flake.
  • The Hetzner server has a public IPv4 — future: point *.ci.commoninternet.net A record directly at it and drop the gateway/MagicDNS path (see plan §6 + DECISIONS.md).