# Watermaps auf Hetzner Cloud Diese OpenTofu-Konfiguration erstellt einen einzelnen Ubuntu-24.04-Server mit fester IPv4-Adresse, vorgeschalteter Hetzner-Firewall und einem persistenten ext4-Volume für Routingdaten, Geofabrik-Rohdaten und die PostGIS-Datenbank mit Häfen, Schleusen, Brücken und Kontaktdaten. Kartenkacheln, DNS-Einträge und konkrete Anwendungsversionen sind bewusst nicht Teil dieses Infrastrukturmoduls. Die Anwendung wird anschließend als commitgenaues Container-Image aus der Gitea Registry deployt. ## Konfiguration Die lokale Datei `terraform.tfvars` wird aus der versionierten Vorlage angelegt und durch die lokale `.gitignore` ausgeschlossen: ```sh cp terraform.tfvars.example terraform.tfvars ``` Vor dem ersten Plan müssen dort mindestens folgende Werte ersetzt werden: - `hcloud_token`: Read/Write-API-Token des richtigen Hetzner-Cloud-Projekts - `ssh_public_key`: vollständiger Inhalt des öffentlichen SSH-Schlüssels - `admin_cidrs`: öffentliche Administrator-IP mit `/32` beziehungsweise ein bewusst gewähltes Netz `terraform.tfvars.example` bleibt als geheimnisfreie Vorlage versioniert. Da `terraform.tfvars` den Token im Klartext enthält, sollte sie nur für den lokalen Benutzer lesbar sein: ```sh chmod 600 terraform.tfvars ``` Auch `terraform.tfstate` bleibt lokal und wird nicht nach Git oder auf den Anwendungsserver übertragen. Nach jedem Import oder Apply muss die State-Datei mit Dateimodus `0600` verschlüsselt gesichert werden, weil OpenTofu sie für spätere Änderungen an denselben Ressourcen benötigt. Ist ein älterer State verloren, werden vorhandene Ressourcen vor jedem Apply anhand ihrer Hetzner-IDs importiert; ein paralleles Neuerzeugen gleichnamiger Ressourcen ist nicht zulässig. ## Infrastruktur erzeugen ```sh cd infra/opentofu tofu init tofu fmt -check tofu validate umask 077 tofu plan -out=watermaps.tfplan tofu apply watermaps.tfplan ``` Gespeicherte Plan-Dateien enthalten trotz als sensibel markierter Variablen unter Umständen den Hetzner-Token. Sie müssen deshalb wie `terraform.tfvars` und der State immer Modus `0600` haben und dürfen nicht weitergegeben werden. Die feste IPv4-Adresse und den erforderlichen manuellen DNS-Eintrag zeigt OpenTofu anschließend an: ```sh tofu output server_ipv4 tofu output dns_a_record tofu output ssh_command ``` Der A-Record `watermaps.incoso.eu` kann direkt nach dem Apply auf `server_ipv4` gesetzt werden. Das eigentliche SSL-Livegehen erfolgt erst durch das separate Deployment-Skript, nachdem DNS propagiert ist und die Routingdaten bereitstehen. ## Auf dem Server Cloud-init installiert Docker einschließlich Compose v2, `curl`, `jq` und `rsync`. Der Benutzer `deploy` erhält Zugriff per SSH und auf Docker. Es wird kein Git-Repository auf den Server geklont. Das persistente Volume wird nach dem Anhängen unter `/srv/watermaps-data` gemountet und enthält die vom Produktions-Stack verwendeten Verzeichnisse `geofabrik`, `local` und `certbot`. Ein systemd-Drop-in lässt Docker bei jedem Start auf den erfolgreichen Volume-Mount warten, bevor bestehende Container neu gestartet werden. Der Status des ersten Starts lässt sich so prüfen: ```sh ssh deploy@"$(tofu output -raw server_ipv4)" \ "cloud-init status --wait && systemctl status watermaps-volume-setup --no-pager" ``` Cloud-init bindet ausschließlich das von OpenTofu erzeugte Volume über den stabilen Hetzner-Gerätepfad `/dev/disk/by-id/scsi-0HC_Volume_` ein. Es wird nicht heuristisch das erste Blockgerät mit dem Modellnamen `Volume` ausgewählt. Nach dem Mount wird zusätzlich die UUID des tatsächlich eingehängten Geräts geprüft. Ein Volume ohne lesbares Dateisystem wird standardmäßig nicht formatiert; nur bei einem garantiert neuen, leeren Volume darf `initialize_empty_routing_volume = true` für die einmalige Initialisierung gesetzt werden. Bei Importen und Serverwechseln bleibt der Wert `false`. Der Löschschutz ist getrennt konfiguriert: - `enable_server_protection` für den austauschbaren Host, - `enable_primary_ip_protection` für die feste öffentliche Adresse, - `enable_volume_protection` für Routing-, Zertifikats- und PostGIS-Daten. Bei einem kontrollierten Servertausch bleiben IP- und Volume-Schutz immer aktiv. Ein Plan, der `hcloud_primary_ip.main` oder `hcloud_volume.routing_data` ersetzen beziehungsweise löschen will, darf nicht angewendet werden. Zusätzlich verhindert OpenTofu für beide Ressourcen mit `prevent_destroy` eine versehentliche Löschung durch den IaC-Plan.