#cloud-config package_update: true package_upgrade: false groups: - docker users: - default - name: ${deploy_user} gecos: Watermaps deployment user shell: /bin/bash lock_passwd: true sudo: "ALL=(ALL) NOPASSWD:ALL" groups: - docker - sudo ssh_authorized_keys: - ${ssh_public_key} ssh_pwauth: false packages: - ca-certificates - curl - docker.io - docker-compose-v2 - jq - rsync write_files: - path: /etc/docker/daemon.json owner: root:root permissions: "0644" content: | { "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } } - path: /usr/local/sbin/watermaps-prepare-volume owner: root:root permissions: "0755" content: | #!/usr/bin/env bash set -euo pipefail mount_path="/srv/watermaps-data" device="/dev/disk/by-id/scsi-0HC_Volume_${routing_volume_id}" allow_initial_format="${initialize_empty_routing_volume}" install -d -m 0755 "$mount_path" for attempt in $(seq 1 120); do if [ -b "$device" ]; then break fi sleep 5 done if [ ! -b "$device" ]; then echo "Hetzner Cloud Volume ${routing_volume_id} wurde nicht unter $device gefunden." >&2 exit 1 fi filesystem="$(blkid -o value -s TYPE "$device" || true)" if [ -z "$filesystem" ]; then signatures="$(wipefs --no-act --output TYPE --noheadings "$device" | tr -d '[:space:]')" if [ -n "$signatures" ]; then echo "Volume enthält unbekannte Signaturen und wird nicht verändert." >&2 exit 1 fi if [ "$allow_initial_format" = "true" ]; then mkfs.ext4 -F "$device" filesystem="ext4" else echo "Volume hat kein lesbares Dateisystem; automatische Formatierung ist deaktiviert." >&2 exit 1 fi fi if [ "$filesystem" != "ext4" ]; then echo "Unerwartetes Dateisystem auf $device: $filesystem" >&2 exit 1 fi uuid="$(blkid -o value -s UUID "$device")" if ! mountpoint -q "$mount_path"; then if ! grep -q "^UUID=$uuid " /etc/fstab; then printf '%s\n' \ "UUID=$uuid $mount_path ext4 defaults,nofail,x-systemd.device-timeout=30 0 2" \ >> /etc/fstab fi mount "$mount_path" fi mounted_source="$(findmnt --noheadings --output SOURCE --target "$mount_path")" mounted_uuid="$(blkid -o value -s UUID "$mounted_source" || true)" if [ "$mounted_uuid" != "$uuid" ]; then echo "Unter $mount_path ist nicht das erwartete Volume ${routing_volume_id} eingehängt." >&2 exit 1 fi install -d -m 0755 -o ${deploy_user} -g ${deploy_user} \ "$mount_path/geofabrik" \ "$mount_path/local" \ "$mount_path/certbot" \ "$mount_path/certbot/www" \ "$mount_path/certbot/letsencrypt" chown ${deploy_user}:${deploy_user} "$mount_path" - path: /etc/systemd/system/watermaps-volume-setup.service owner: root:root permissions: "0644" content: | [Unit] Description=Prepare persistent Watermaps routing and event data volume Wants=network-online.target After=network-online.target local-fs.target [Service] Type=oneshot ExecStart=/usr/local/sbin/watermaps-prepare-volume RemainAfterExit=yes Restart=on-failure RestartSec=15 TimeoutStartSec=0 [Install] WantedBy=multi-user.target - path: /etc/systemd/system/docker.service.d/watermaps-volume.conf owner: root:root permissions: "0644" content: | [Unit] Requires=watermaps-volume-setup.service After=watermaps-volume-setup.service runcmd: - [install, -d, -m, "0755", -o, ${deploy_user}, -g, ${deploy_user}, /opt/watermaps] - [install, -d, -m, "0755", -o, ${deploy_user}, -g, ${deploy_user}, /opt/watermaps/deploy] - [install, -d, -m, "0755", -o, ${deploy_user}, -g, ${deploy_user}, /var/www/certbot] - [systemctl, daemon-reload] - [systemctl, enable, --now, watermaps-volume-setup.service] - [systemctl, enable, docker.service] - [systemctl, restart, docker.service] final_message: "Watermaps host bootstrap completed after $UPTIME seconds."