coolify/docs/api/hetzner-provisioning-examples.md
Andras Bacsai 62c394d3a1 feat: add Hetzner server provisioning API endpoints
Add complete API support for Hetzner server provisioning, matching UI functionality:

Cloud Provider Token Management:
- POST /api/v1/cloud-tokens - Create and validate tokens
- GET /api/v1/cloud-tokens - List all tokens
- GET /api/v1/cloud-tokens/{uuid} - Get specific token
- PATCH /api/v1/cloud-tokens/{uuid} - Update token name
- DELETE /api/v1/cloud-tokens/{uuid} - Delete token
- POST /api/v1/cloud-tokens/{uuid}/validate - Validate token

Hetzner Resource Discovery:
- GET /api/v1/hetzner/locations - List datacenters
- GET /api/v1/hetzner/server-types - List server types
- GET /api/v1/hetzner/images - List OS images
- GET /api/v1/hetzner/ssh-keys - List SSH keys

Server Provisioning:
- POST /api/v1/servers/hetzner - Create server with full options

Features:
- Token validation against provider APIs before storage
- Smart SSH key management with MD5 fingerprint deduplication
- IPv4/IPv6 network configuration with preference logic
- Cloud-init script support with YAML validation
- Team-based isolation and security
- Comprehensive test coverage (40+ test cases)
- Complete documentation with curl examples and Yaak collection

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-10 08:38:09 +01:00

11 KiB

Hetzner Server Provisioning API Examples

This document contains ready-to-use curl examples for the Hetzner server provisioning API endpoints. These examples use the root API token for development and can be easily imported into Yaak or any other API client.

Prerequisites

# Set these environment variables
export COOLIFY_URL="http://localhost"
export API_TOKEN="root"  # Your Coolify API token

Cloud Provider Tokens

1. Create a Hetzner Cloud Provider Token

curl -X POST "${COOLIFY_URL}/api/v1/cloud-tokens" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "hetzner",
    "token": "YOUR_HETZNER_API_TOKEN_HERE",
    "name": "My Hetzner Token"
  }'

Response:

{
  "uuid": "abc123def456"
}

2. List All Cloud Provider Tokens

curl -X GET "${COOLIFY_URL}/api/v1/cloud-tokens" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json"

Response:

[
  {
    "uuid": "abc123def456",
    "name": "My Hetzner Token",
    "provider": "hetzner",
    "team_id": 0,
    "servers_count": 0,
    "created_at": "2025-11-19T12:00:00.000000Z",
    "updated_at": "2025-11-19T12:00:00.000000Z"
  }
]

3. Get a Specific Cloud Provider Token

curl -X GET "${COOLIFY_URL}/api/v1/cloud-tokens/abc123def456" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json"

4. Update Cloud Provider Token Name

curl -X PATCH "${COOLIFY_URL}/api/v1/cloud-tokens/abc123def456" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production Hetzner Token"
  }'

5. Validate a Cloud Provider Token

curl -X POST "${COOLIFY_URL}/api/v1/cloud-tokens/abc123def456/validate" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json"

Response:

{
  "valid": true,
  "message": "Token is valid."
}

6. Delete a Cloud Provider Token

curl -X DELETE "${COOLIFY_URL}/api/v1/cloud-tokens/abc123def456" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json"

Response:

{
  "message": "Cloud provider token deleted."
}

Hetzner Resource Discovery

7. Get Available Hetzner Locations

curl -X GET "${COOLIFY_URL}/api/v1/hetzner/locations?cloud_provider_token_id=abc123def456" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json"

Response:

[
  {
    "id": 1,
    "name": "fsn1",
    "description": "Falkenstein DC Park 1",
    "country": "DE",
    "city": "Falkenstein",
    "latitude": 50.47612,
    "longitude": 12.370071
  },
  {
    "id": 2,
    "name": "nbg1",
    "description": "Nuremberg DC Park 1",
    "country": "DE",
    "city": "Nuremberg",
    "latitude": 49.452102,
    "longitude": 11.076665
  },
  {
    "id": 3,
    "name": "hel1",
    "description": "Helsinki DC Park 1",
    "country": "FI",
    "city": "Helsinki",
    "latitude": 60.169857,
    "longitude": 24.938379
  }
]

8. Get Available Hetzner Server Types

curl -X GET "${COOLIFY_URL}/api/v1/hetzner/server-types?cloud_provider_token_id=abc123def456" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json"

Response (truncated):

[
  {
    "id": 1,
    "name": "cx11",
    "description": "CX11",
    "cores": 1,
    "memory": 2.0,
    "disk": 20,
    "prices": [
      {
        "location": "fsn1",
        "price_hourly": {
          "net": "0.0052000000",
          "gross": "0.0061880000"
        },
        "price_monthly": {
          "net": "3.2900000000",
          "gross": "3.9151000000"
        }
      }
    ],
    "storage_type": "local",
    "cpu_type": "shared",
    "architecture": "x86",
    "deprecated": false
  }
]

9. Get Available Hetzner Images (Operating Systems)

curl -X GET "${COOLIFY_URL}/api/v1/hetzner/images?cloud_provider_token_id=abc123def456" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json"

Response (truncated):

[
  {
    "id": 15512617,
    "name": "ubuntu-20.04",
    "description": "Ubuntu 20.04",
    "type": "system",
    "os_flavor": "ubuntu",
    "os_version": "20.04",
    "architecture": "x86",
    "deprecated": false
  },
  {
    "id": 67794396,
    "name": "ubuntu-22.04",
    "description": "Ubuntu 22.04",
    "type": "system",
    "os_flavor": "ubuntu",
    "os_version": "22.04",
    "architecture": "x86",
    "deprecated": false
  }
]

10. Get Hetzner SSH Keys

curl -X GET "${COOLIFY_URL}/api/v1/hetzner/ssh-keys?cloud_provider_token_id=abc123def456" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json"

Response:

[
  {
    "id": 123456,
    "name": "my-ssh-key",
    "fingerprint": "aa:bb:cc:dd:ee:ff:11:22:33:44:55:66:77:88:99:00",
    "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDe..."
  }
]

Hetzner Server Provisioning

11. Create a Hetzner Server (Minimal Example)

First, you need to get your private key UUID:

curl -X GET "${COOLIFY_URL}/api/v1/security/keys" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json"

Then create the server:

curl -X POST "${COOLIFY_URL}/api/v1/servers/hetzner" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "cloud_provider_token_id": "abc123def456",
    "location": "nbg1",
    "server_type": "cx11",
    "image": 67794396,
    "private_key_uuid": "your-private-key-uuid"
  }'

Response:

{
  "uuid": "server-uuid-123",
  "hetzner_server_id": 12345678,
  "ip": "1.2.3.4"
}

12. Create a Hetzner Server (Full Example with All Options)

curl -X POST "${COOLIFY_URL}/api/v1/servers/hetzner" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "cloud_provider_token_id": "abc123def456",
    "location": "nbg1",
    "server_type": "cx11",
    "image": 67794396,
    "name": "production-server",
    "private_key_uuid": "your-private-key-uuid",
    "enable_ipv4": true,
    "enable_ipv6": true,
    "hetzner_ssh_key_ids": [123456, 789012],
    "cloud_init_script": "#cloud-config\npackages:\n  - docker.io\n  - git",
    "instant_validate": true
  }'

Parameters:

  • cloud_provider_token_id (required): UUID of your Hetzner cloud provider token
  • location (required): Hetzner location name (e.g., "nbg1", "fsn1", "hel1")
  • server_type (required): Hetzner server type (e.g., "cx11", "cx21", "ccx13")
  • image (required): Hetzner image ID (get from images endpoint)
  • name (optional): Server name (auto-generated if not provided)
  • private_key_uuid (required): UUID of the private key to use for SSH
  • enable_ipv4 (optional): Enable IPv4 (default: true)
  • enable_ipv6 (optional): Enable IPv6 (default: true)
  • hetzner_ssh_key_ids (optional): Array of additional Hetzner SSH key IDs
  • cloud_init_script (optional): Cloud-init YAML script for initial setup
  • instant_validate (optional): Validate server connection immediately (default: false)

Complete Workflow Example

Here's a complete example of creating a Hetzner server from start to finish:

#!/bin/bash

# Configuration
export COOLIFY_URL="http://localhost"
export API_TOKEN="root"
export HETZNER_API_TOKEN="your-hetzner-api-token"

# Step 1: Create cloud provider token
echo "Creating cloud provider token..."
TOKEN_RESPONSE=$(curl -s -X POST "${COOLIFY_URL}/api/v1/cloud-tokens" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d "{
    \"provider\": \"hetzner\",
    \"token\": \"${HETZNER_API_TOKEN}\",
    \"name\": \"My Hetzner Token\"
  }")

CLOUD_TOKEN_ID=$(echo $TOKEN_RESPONSE | jq -r '.uuid')
echo "Cloud token created: $CLOUD_TOKEN_ID"

# Step 2: Get available locations
echo "Fetching locations..."
curl -s -X GET "${COOLIFY_URL}/api/v1/hetzner/locations?cloud_provider_token_id=${CLOUD_TOKEN_ID}" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json" | jq '.[] | {name, description, country}'

# Step 3: Get available server types
echo "Fetching server types..."
curl -s -X GET "${COOLIFY_URL}/api/v1/hetzner/server-types?cloud_provider_token_id=${CLOUD_TOKEN_ID}" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json" | jq '.[] | {name, cores, memory, disk}'

# Step 4: Get available images
echo "Fetching images..."
curl -s -X GET "${COOLIFY_URL}/api/v1/hetzner/images?cloud_provider_token_id=${CLOUD_TOKEN_ID}" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json" | jq '.[] | {id, name, description}'

# Step 5: Get private keys
echo "Fetching private keys..."
KEYS_RESPONSE=$(curl -s -X GET "${COOLIFY_URL}/api/v1/security/keys" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json")

PRIVATE_KEY_UUID=$(echo $KEYS_RESPONSE | jq -r '.[0].uuid')
echo "Using private key: $PRIVATE_KEY_UUID"

# Step 6: Create the server
echo "Creating server..."
SERVER_RESPONSE=$(curl -s -X POST "${COOLIFY_URL}/api/v1/servers/hetzner" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d "{
    \"cloud_provider_token_id\": \"${CLOUD_TOKEN_ID}\",
    \"location\": \"nbg1\",
    \"server_type\": \"cx11\",
    \"image\": 67794396,
    \"name\": \"my-production-server\",
    \"private_key_uuid\": \"${PRIVATE_KEY_UUID}\",
    \"enable_ipv4\": true,
    \"enable_ipv6\": false,
    \"instant_validate\": true
  }")

echo "Server created:"
echo $SERVER_RESPONSE | jq '.'

SERVER_UUID=$(echo $SERVER_RESPONSE | jq -r '.uuid')
SERVER_IP=$(echo $SERVER_RESPONSE | jq -r '.ip')

echo "Server UUID: $SERVER_UUID"
echo "Server IP: $SERVER_IP"
echo "You can now SSH to: root@$SERVER_IP"

Error Handling

Common Errors

401 Unauthorized:

{
  "message": "Unauthenticated."
}

Solution: Check your API token.

404 Not Found:

{
  "message": "Cloud provider token not found."
}

Solution: Verify the UUID exists and belongs to your team.

422 Validation Error:

{
  "message": "Validation failed.",
  "errors": {
    "provider": ["The provider field is required."],
    "token": ["The token field is required."]
  }
}

Solution: Check the request body for missing or invalid fields.

400 Bad Request:

{
  "message": "Invalid Hetzner token. Please check your API token."
}

Solution: Verify your Hetzner API token is correct.

Testing with Yaak

To import these examples into Yaak:

  1. Copy any curl command from this document
  2. In Yaak, click "Import" → "From cURL"
  3. Paste the curl command
  4. Update the environment variables (COOLIFY_URL, API_TOKEN) in Yaak's environment settings

Or create a Yaak environment with these variables:

{
  "COOLIFY_URL": "http://localhost",
  "API_TOKEN": "root"
}

Then you can use {{COOLIFY_URL}} and {{API_TOKEN}} in your requests.

Rate Limiting

The Hetzner API has rate limits. If you receive a 429 error, the HetznerService will automatically retry with exponential backoff. The API token validation endpoints are also rate-limited on the Coolify side.

Security Notes

  • Never commit your Hetzner API token to version control
  • Store API tokens securely in environment variables or secrets management
  • Use the validation endpoint to test tokens before creating resources
  • Cloud provider tokens are encrypted at rest in the database
  • The actual token value is never returned by the API (only the UUID)