#!/bin/bash # Coolify Infrastructure Setup via Hetzner Cloud API set -euo pipefail # Load API key from services.env CRED_FILE="${HOME}/.openclaw/workspace/.credentials/services.env" HETZNER_TOKEN=$(grep '^COOLIFY_HETZNER_API_KEY=' "$CRED_FILE" | cut -d= -f2- || true) if [ -z "$HETZNER_TOKEN" ]; then echo "ERROR: COOLIFY_HETZNER_API_KEY not found in services.env" exit 1 fi API="https://api.hetzner.cloud/v1" AUTH="Authorization: Bearer $HETZNER_TOKEN" hcloud() { curl -s -H "$AUTH" -H "Content-Type: application/json" "$@" } echo "=== Coolify Infrastructure Setup ===" # Step 1: Upload SSH keys echo "" echo "--- Step 1: SSH Keys ---" # User's key USER_KEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFshMhXwS0FQFPlITipshvNKrV8sA52ZFlnaoHd1thKg dominik@nb-01" # OpenClaw key OPENCLAW_KEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL+i4Nn0Nc1ovqHXmbyekxCigT2Qn6RD1cdbKkW727Yl openclaw@openclaw-vm" # Check if keys already exist EXISTING_KEYS=$(hcloud "$API/ssh_keys" | jq -r '.ssh_keys[].name' 2>/dev/null || true) upload_key() { local name="$1" key="$2" if echo "$EXISTING_KEYS" | grep -q "^${name}$"; then hcloud "$API/ssh_keys" | jq -r ".ssh_keys[] | select(.name==\"$name\") | .id" else echo " Uploading key '$name'..." >&2 RESP=$(hcloud -X POST "$API/ssh_keys" -d "{\"name\":\"$name\",\"public_key\":\"$key\"}") local kid=$(echo "$RESP" | jq -r '.ssh_key.id') if [ "$kid" = "null" ] || [ -z "$kid" ]; then echo " ERROR: $(echo "$RESP" | jq -r '.error.message // "Unknown error"')" >&2 exit 1 fi echo " Uploaded: ID $kid" >&2 echo "$kid" fi } USER_KEY_ID=$(upload_key "dominik-nb01" "$USER_KEY") OPENCLAW_KEY_ID=$(upload_key "openclaw-vm" "$OPENCLAW_KEY") echo " User key ID: $USER_KEY_ID" echo " OpenClaw key ID: $OPENCLAW_KEY_ID" # Step 2: Create Network echo "" echo "--- Step 2: Private Network ---" EXISTING_NET=$(hcloud "$API/networks" | jq -r '.networks[] | select(.name=="coolify-net") | .id' 2>/dev/null || echo "") if [ -n "$EXISTING_NET" ]; then echo " Network 'coolify-net' already exists: ID $EXISTING_NET" NET_ID="$EXISTING_NET" else echo " Creating network 'coolify-net' (10.0.0.0/16)..." RESP=$(hcloud -X POST "$API/networks" -d '{ "name": "coolify-net", "ip_range": "10.0.0.0/16", "subnets": [{"type": "cloud", "network_zone": "eu-central", "ip_range": "10.0.1.0/24"}] }') NET_ID=$(echo "$RESP" | jq '.network.id') if [ "$NET_ID" = "null" ] || [ -z "$NET_ID" ]; then echo " ERROR: $(echo "$RESP" | jq -r '.error.message // "Unknown error"')" exit 1 fi echo " Created: ID $NET_ID" fi # Step 3: Create Servers echo "" echo "--- Step 3: Servers ---" create_server() { local name="$1" EXISTING=$(hcloud "$API/servers" | jq -r ".servers[] | select(.name==\"$name\") | .id" 2>/dev/null || echo "") if [ -n "$EXISTING" ]; then echo " Server '$name' already exists: ID $EXISTING" IP=$(hcloud "$API/servers/$EXISTING" | jq -r '.server.public_net.ipv4.ip') echo " IP: $IP" return fi echo " Creating server '$name' (CAX11, ARM64, Ubuntu 24.04, fsn1)..." RESP=$(hcloud -X POST "$API/servers" -d "{ \"name\": \"$name\", \"server_type\": \"cax11\", \"image\": \"ubuntu-24.04\", \"location\": \"fsn1\", \"ssh_keys\": [$USER_KEY_ID, $OPENCLAW_KEY_ID], \"networks\": [$NET_ID], \"public_net\": {\"enable_ipv4\": true, \"enable_ipv6\": true}, \"labels\": {\"project\": \"coolify\", \"role\": \"$(echo $name | sed 's/coolify-//')\"} }") SERVER_ID=$(echo "$RESP" | jq '.server.id') IP=$(echo "$RESP" | jq -r '.server.public_net.ipv4.ip') if [ "$SERVER_ID" = "null" ] || [ -z "$SERVER_ID" ]; then echo " ERROR: $(echo "$RESP" | jq -r '.error.message // "Unknown error"')" exit 1 fi echo " Created: ID $SERVER_ID, IP $IP" } create_server "coolify-1" create_server "coolify-2" # Step 4: Create Firewall echo "" echo "--- Step 4: Firewall ---" EXISTING_FW=$(hcloud "$API/firewalls" | jq -r '.firewalls[] | select(.name=="coolify-fw") | .id' 2>/dev/null || echo "") if [ -n "$EXISTING_FW" ]; then echo " Firewall 'coolify-fw' already exists: ID $EXISTING_FW" else echo " Creating firewall 'coolify-fw'..." RESP=$(hcloud -X POST "$API/firewalls" -d '{ "name": "coolify-fw", "rules": [ {"direction": "in", "protocol": "tcp", "port": "22", "source_ips": ["0.0.0.0/0", "::/0"], "description": "SSH"}, {"direction": "in", "protocol": "tcp", "port": "80", "source_ips": ["0.0.0.0/0", "::/0"], "description": "HTTP"}, {"direction": "in", "protocol": "tcp", "port": "443", "source_ips": ["0.0.0.0/0", "::/0"], "description": "HTTPS"}, {"direction": "in", "protocol": "tcp", "port": "8000", "source_ips": ["0.0.0.0/0", "::/0"], "description": "Coolify UI"} ] }') FW_ID=$(echo "$RESP" | jq '.firewall.id') echo " Created: ID $FW_ID" # Apply to both servers echo " Applying firewall to servers..." SERVER_IDS=$(hcloud "$API/servers" | jq '[.servers[] | select(.name | startswith("coolify-")) | .id]') for SID in $(echo "$SERVER_IDS" | jq -r '.[]'); do hcloud -X POST "$API/firewalls/$FW_ID/actions/apply_to_resources" \ -d "{\"apply_to\": [{\"type\": \"server\", \"server\": {\"id\": $SID}}]}" > /dev/null echo " Applied to server $SID" done fi # Summary echo "" echo "=== DONE ===" echo "" echo "Servers:" for name in coolify-1 coolify-2; do IP=$(hcloud "$API/servers" | jq -r ".servers[] | select(.name==\"$name\") | .public_net.ipv4.ip") PRIV_IP=$(hcloud "$API/servers" | jq -r ".servers[] | select(.name==\"$name\") | .private_net[0].ip // \"pending\"") echo " $name: public=$IP private=$PRIV_IP" done echo "" echo "Next: Wait ~60s for servers to boot, then install Coolify on coolify-1"