The zones were edited by hand in the console, and it showed: dozens of records for servers that no longer existed, a CNAME and a TXT record missing their trailing dot, and a 24 h default TTL that made today's XMPP move linger in resolver caches. Now both zones live in the asxpio project, so describe them here, adopted with import blocks (the first apply imported 87 and changed nothing but protection). Our servers' records reference hcloud_server instead of literal IPs. Every record set is change-protected except those of the test XMPP servers, and both zones are delete-protected. Assisted-by: Claude:opus-5-5
158 lines
3.8 KiB
Markdown
158 lines
3.8 KiB
Markdown
# 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
|
|
```bash
|
|
terraform init
|
|
```
|
|
|
|
### 2. Configure secrets
|
|
Edit `terraform.tfvars`:
|
|
```hcl
|
|
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
|
|
```bash
|
|
terraform plan
|
|
terraform apply
|
|
```
|
|
|
|
### 4. Enable S3 backend (optional)
|
|
After buckets are created:
|
|
|
|
```bash
|
|
# 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:
|
|
```hcl
|
|
# 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
|