mirror of
https://github.com/tiennm99/coolify.git
synced 2026-04-18 13:20:41 +00:00
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>
465 lines
11 KiB
Markdown
465 lines
11 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
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:**
|
|
```json
|
|
{
|
|
"uuid": "abc123def456"
|
|
}
|
|
```
|
|
|
|
### 2. List All Cloud Provider Tokens
|
|
|
|
```bash
|
|
curl -X GET "${COOLIFY_URL}/api/v1/cloud-tokens" \
|
|
-H "Authorization: Bearer ${API_TOKEN}" \
|
|
-H "Content-Type: application/json"
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
[
|
|
{
|
|
"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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
curl -X POST "${COOLIFY_URL}/api/v1/cloud-tokens/abc123def456/validate" \
|
|
-H "Authorization: Bearer ${API_TOKEN}" \
|
|
-H "Content-Type: application/json"
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"valid": true,
|
|
"message": "Token is valid."
|
|
}
|
|
```
|
|
|
|
### 6. Delete a Cloud Provider Token
|
|
|
|
```bash
|
|
curl -X DELETE "${COOLIFY_URL}/api/v1/cloud-tokens/abc123def456" \
|
|
-H "Authorization: Bearer ${API_TOKEN}" \
|
|
-H "Content-Type: application/json"
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"message": "Cloud provider token deleted."
|
|
}
|
|
```
|
|
|
|
## Hetzner Resource Discovery
|
|
|
|
### 7. Get Available Hetzner Locations
|
|
|
|
```bash
|
|
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:**
|
|
```json
|
|
[
|
|
{
|
|
"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
|
|
|
|
```bash
|
|
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):**
|
|
```json
|
|
[
|
|
{
|
|
"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)
|
|
|
|
```bash
|
|
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):**
|
|
```json
|
|
[
|
|
{
|
|
"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
|
|
|
|
```bash
|
|
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:**
|
|
```json
|
|
[
|
|
{
|
|
"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:
|
|
|
|
```bash
|
|
curl -X GET "${COOLIFY_URL}/api/v1/security/keys" \
|
|
-H "Authorization: Bearer ${API_TOKEN}" \
|
|
-H "Content-Type: application/json"
|
|
```
|
|
|
|
Then create the server:
|
|
|
|
```bash
|
|
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:**
|
|
```json
|
|
{
|
|
"uuid": "server-uuid-123",
|
|
"hetzner_server_id": 12345678,
|
|
"ip": "1.2.3.4"
|
|
}
|
|
```
|
|
|
|
### 12. Create a Hetzner Server (Full Example with All Options)
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
#!/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:**
|
|
```json
|
|
{
|
|
"message": "Unauthenticated."
|
|
}
|
|
```
|
|
Solution: Check your API token.
|
|
|
|
**404 Not Found:**
|
|
```json
|
|
{
|
|
"message": "Cloud provider token not found."
|
|
}
|
|
```
|
|
Solution: Verify the UUID exists and belongs to your team.
|
|
|
|
**422 Validation Error:**
|
|
```json
|
|
{
|
|
"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:**
|
|
```json
|
|
{
|
|
"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:
|
|
```json
|
|
{
|
|
"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)
|