4.0 KiB
🚀 Provisioning Servers with RailianceHosts
This guide explains where you declare servers, how Terraform uses that declaration, and how to provision (and later destroy) machines on Hetzner.
🚀 Fast Path: Using the Helper Script
Instead of manually editing inventory/servers.yaml and running make tf-apply, you can use the convenience script scripts/hcloud_new_server.sh.
This script will:
- Add the new host entry to
inventory/servers.yaml - Decrypt your Hetzner API token with SOPS
- Run Terraform (
init/plan/apply) to provision the server - Print the IPv4 address and a ready-to-use SSH command
Example:
scripts/hcloud_new_server.sh core-01 --type cpx11 --region nbg1 --role core
This will create a small cpx11 instance in the Nuremberg (nbg1) region, tagged with the role core. You can then connect directly:
ssh admin@<printed-ip>
👉 The script is optional. You can always manage servers by editing inventory/servers.yaml and running make tf-apply instead.
1) Where you define servers
All desired hosts live in inventory/servers.yaml. Each entry is a simple YAML object with the required attributes:
servers:
- name: core-01
labels: [core, wireguard, git]
role: "core"
region: "nbg1" # Hetzner location (e.g., nbg1, fsn1, hel1)
type: "cpx21" # Hetzner server type/flavor
image: "ubuntu-24.04" # OS image slug
ssh_user: "admin" # bootstrap user (cloud-init creates this)
Tip: Keep names stable. Renaming a server in this file makes Terraform think the old one was destroyed and a new one should be created.
2) Two ways to add a server
A) Edit YAML by hand (simple)
Open inventory/servers.yaml, add a new entry, save, commit.
B) Use the helper script (safe & quick)
# requires scripts/new_host.py
make new-host NAME=web-01 TYPE=cpx21 REGION=nbg1 ROLE=web
# or directly:
python3 scripts/new_host.py --name web-01 --type cpx21 --region nbg1 --role web
You can also do add + provision in one step:
scripts/hcloud_new_server.sh web-01 --type cpx21 --region nbg1 --role web
3) How Terraform uses your declaration
The module at terraform/hetzner/:
- Reads
inventory/servers.yaml(for_eachoverservers) - Registers your SSH key from
keys/admin_ssh.pub - Injects cloud-init that sets up the
adminuser and basic hardening - Creates/updates/destroys servers to match the YAML
Outputs include a map of server names → IPv4 addresses.
4) Provision (create/update)
Make sure your Hetzner API token is present and SOPS-decryptable in inventory/group_vars/secrets.sops.yaml under ops.hcloud_token.
Then run either:
# plan and apply in separate steps
make tf-plan
make tf-apply
or the end-to-end convenience:
make apply # terraform apply + ansible bootstrap
If you used the one-shot script:
scripts/hcloud_new_server.sh web-01 --type cpx21 --region nbg1 --role web
Terraform will print the new servers’ IPv4 addresses at the end.
5) Connect & converge
Connect via SSH:
ssh admin@<server-ip>
Run Ansible base bootstrap (if not using make apply):
make ansible-bootstrap
6) Destroy (tear down)
To remove all servers managed by this repo:
make tf-destroy
To remove just one server, delete its entry from inventory/servers.yaml, commit, then:
make tf-apply
Terraform will destroy the missing server and leave others intact.
7) Notes & conventions
- Idempotent: You can run
make applyrepeatedly; Terraform converges infra, Ansible converges config. - SSH keys: Ensure
keys/admin_ssh.pubexists before provisioning. - Secret token: The Hetzner API token must be in
inventory/group_vars/secrets.sops.yaml(encrypted with SOPS). - Cloud-init delay: Allow ~30–60s after creation for first-boot tasks before first SSH.
- Labels & role:
labelsare freeform tags;rolecan drive Ansible plays as you grow.