railiance-infra/docs/provisioning.md
codex b93af8cc78
Some checks failed
CI Smoke / source-contract (push) Failing after 2s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Implement reproducible S1 handoff contracts
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02994-7685-7940-bf34-3555b8256018
2026-08-23 12:02:23 +02:00

161 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🚀 Provisioning Servers with railiance-infra
This guide explains how adopted hosts and provider-managed Hetzner hosts share
an inventory without sharing lifecycle behavior.
---
## 🚀 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`](../scripts/hcloud_new_server.sh).
This script will:
1. Add the new host entry to `inventory/servers.yaml`
2. Decrypt your Hetzner API token with SOPS
3. Run Terraform init and plan
4. Apply only when the operator supplies the explicit `--apply` flag after
reviewing the plan
**Example:**
```bash
scripts/hcloud_new_server.sh core-01 --type cpx11 --region nbg1 --role core
# after review and the required approval:
scripts/hcloud_new_server.sh core-01 --type cpx11 --region nbg1 --role core --apply
```
This will create a small cpx11 instance in the Nuremberg (nbg1) region, tagged with the role core.
You can then connect directly:
```bash
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 host identities live in **`inventory/servers.yaml`**. Run
`make validate-inventory` before Terraform or Ansible. Adopted hosts have a
provider address but no `provisioning` block. Provider-managed Hetzner hosts
have no committed IP and use this shape:
```yaml
servers:
- name: core-01
provider: hetzner
lifecycle_mode: provider-managed
ssh_user: admin
baseline_profile: ufw-managed
provisioning:
server_type: cpx21
region: nbg1
image: ubuntu-24.04
role: core
labels: [core, wireguard, git]
```
> 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)
```bash
# 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**:
```bash
scripts/hcloud_new_server.sh web-01 --type cpx21 --region nbg1 --role web
```
---
## 3) How Terraform uses your declaration
The module at `terraform/hetzner/`:
- Selects only records with `provider: hetzner` and
`lifecycle_mode: provider-managed`
- Registers your SSH key from `keys/admin_ssh.pub`
- Injects **cloud-init** that sets up the `admin` user and basic hardening
- Creates/updates/destroys servers to match the YAML
Outputs include a map of server names → IPv4 addresses.
---
## 4) Provision (create/update)
The Hetzner API token is **SOPS-decryptable** at
`secrets/hetzner-token.yaml`, field `hetzner.token`. It is decrypted only into
the invoking process environment.
Then run either:
```bash
# plan and apply in separate steps
make tf-plan
APPROVE_TF_APPLY=YES make tf-apply
```
or the end-to-end convenience:
```bash
APPROVE_TF_APPLY=YES make apply # terraform apply + Ansible bootstrap
```
If you used the one-shot script:
```bash
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:
```bash
ssh admin@<server-ip>
```
Run Ansible base bootstrap (if not using `make apply`):
```bash
make ansible-bootstrap
```
---
## 6) Destroy (tear down)
To remove all servers managed by this repo:
```bash
APPROVE_TF_DESTROY=DESTROY-MANAGED-HETZNER make tf-destroy
```
To remove one **provider-managed Hetzner** server, delete its entry from
`inventory/servers.yaml`, review the plan, obtain the required approval, then:
```bash
APPROVE_TF_APPLY=YES make tf-apply
```
Terraform will destroy the missing managed Hetzner server and leave others
intact. Adopted Host Europe records are never Terraform resources.
---
## 7) Notes & conventions
- **Idempotent:** You can run `make apply` repeatedly; Terraform converges infra, Ansible converges config.
- **SSH keys:** Ensure `keys/admin_ssh.pub` exists before provisioning.
- **Secret token:** The Hetzner API token is `secrets/hetzner-token.yaml`, field
`hetzner.token`, and must remain SOPS-encrypted.
- **Cloud-init delay:** Allow ~3060s after creation for first-boot tasks before first SSH.
- **Labels & role:** `labels` are freeform tags; `role` can drive Ansible plays as you grow.