From ff686e373b02c1f27b96dd0a85b8fec3b262704c Mon Sep 17 00:00:00 2001 From: Mido Date: Thu, 18 Sep 2025 20:03:05 +0200 Subject: [PATCH 01/21] Add Hetzner Cloud --- .icons/hetzner.svg | 5 + registry/melmathari/README.md | 16 + registry/melmathari/images/melmathari.jpeg | Bin 0 -> 41247 bytes .../templates/hetzner-cloud/README.md | 242 +++++++++++ .../hetzner-cloud/cloud-config.yaml.tftpl | 57 +++ .../hetzner-cloud/hetzner-config.json | 49 +++ .../templates/hetzner-cloud/main.tf | 391 ++++++++++++++++++ 7 files changed, 760 insertions(+) create mode 100644 .icons/hetzner.svg create mode 100644 registry/melmathari/README.md create mode 100644 registry/melmathari/images/melmathari.jpeg create mode 100644 registry/melmathari/templates/hetzner-cloud/README.md create mode 100644 registry/melmathari/templates/hetzner-cloud/cloud-config.yaml.tftpl create mode 100644 registry/melmathari/templates/hetzner-cloud/hetzner-config.json create mode 100644 registry/melmathari/templates/hetzner-cloud/main.tf diff --git a/.icons/hetzner.svg b/.icons/hetzner.svg new file mode 100644 index 000000000..74bb87c1a --- /dev/null +++ b/.icons/hetzner.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/registry/melmathari/README.md b/registry/melmathari/README.md new file mode 100644 index 000000000..92faa46de --- /dev/null +++ b/registry/melmathari/README.md @@ -0,0 +1,16 @@ +--- +display_name: "Mohamed El Mathari" +bio: "Software engineer, no-code nerd, teacher, and always ready to try something new" +avatar: "./.images/melmathari.jpeg" +github: "melmathari" +linkedin: "https://www.linkedin.com/in/melmathari/" +website: "https://melmathari.dev" +support_email: "info@nocodeventure.com" +status: community +--- + +# About Me + +👨‍💻 Learning by contributing in Open Source. + +Software engineer, no-code nerd, teacher, and always ready to try something new. Based in the Netherlands, I'm a no-code advocate who loves building tools and launching products that make a difference. Whether I'm coding, mentoring, or experimenting, I aim to keep things simple and impactful. I'm a big fan of breaking down complex problems into straightforward solutions—one idea at a time. diff --git a/registry/melmathari/images/melmathari.jpeg b/registry/melmathari/images/melmathari.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..b50b558d4deb41adfd0f2b12ded3d6f21de4da0b GIT binary patch literal 41247 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 GKmXbAUl|+# literal 0 HcmV?d00001 diff --git a/registry/melmathari/templates/hetzner-cloud/README.md b/registry/melmathari/templates/hetzner-cloud/README.md new file mode 100644 index 000000000..b8fff5f0b --- /dev/null +++ b/registry/melmathari/templates/hetzner-cloud/README.md @@ -0,0 +1,242 @@ +--- +display_name: Hetzner Cloud Server (Linux) +description: Provision Hetzner Cloud servers as Coder workspaces with networking and volumes +icon: ../../../../.icons/hetzner.svg +verified: false +tags: [vm, linux, hetzner, cloud, germany] +--- + +# Remote Development on Hetzner Cloud + +Provision Hetzner Cloud servers as [Coder workspaces](https://coder.com/docs/workspaces) with this template. + +This template provides a comprehensive Hetzner Cloud setup with: +- **Dynamic Configuration**: Server types, locations, and images loaded from JSON +- **Smart Validation**: Prevents invalid server type/location combinations +- **Multiple Server Types**: Shared, dedicated, and CPU-optimized instances +- **Global Locations**: Germany, Finland, and USA datacenters +- **Persistent Storage**: Home volumes that survive workspace restarts +- **Secure Networking**: Private networks with firewall rules +- **Clean Architecture**: Minimal JSON configuration for easy maintenance + +## Prerequisites + +To deploy workspaces as Hetzner Cloud servers, you'll need: + +- Hetzner Cloud [API token](https://docs.hetzner.cloud/#authentication) +- Hetzner Cloud project (create one in the [Hetzner Cloud Console](https://console.hetzner.cloud/)) +- **SSH Keys**: Upload your SSH public keys to your Hetzner Cloud account (the template will use all available keys) + +### Authentication + +This template assumes that the Coder Provisioner is run in an environment that is authenticated with Hetzner Cloud. + +Set the `HCLOUD_TOKEN` environment variable to your Hetzner Cloud API token, or provide it via the `hcloud_token` variable in your `terraform.tfvars` file. + +For other authentication methods, consult the [Hetzner Cloud Terraform provider documentation](https://registry.terraform.io/providers/hetznercloud/hcloud/latest/docs). + +### Image Name Verification + +The template uses Hetzner's official image names. To verify current available images: + +```bash +# Set your API token +export HCLOUD_TOKEN="your-hetzner-cloud-api-token" + +# List all available images +curl -s -H "Authorization: Bearer $HCLOUD_TOKEN" \ + "https://api.hetzner.cloud/v1/images" | \ + jq '.images[] | select(.type=="system") | .name' +``` + +If you encounter image-related errors, check that the image names in `hetzner-config.json` match the official names exactly (some may include architecture suffixes like `-amd64`). + +## Architecture + +This template provisions the following resources: + +- **Hetzner Cloud server** (ephemeral, deleted on workspace stop) +- **Persistent volume** (mounted to `/home/`, survives workspace restarts) +- **Private network** with subnet for secure communication +- **Firewall** with rules for SSH, HTTP, HTTPS, and development ports +- **SSH keys** automatically loaded from your Hetzner Cloud account + +### Lifecycle Management + +- **Workspace start**: Server and volume are created, volume is attached +- **Workspace stop**: Server is destroyed, but volume persists +- **Workspace restart**: New server is created and existing volume is reattached + +This means that when the workspace restarts, any tools or files outside of the home directory are not persisted. To pre-bake tools into the workspace, modify the server image or use a [startup script](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/script). + +## Server Types + +The template supports all major Hetzner Cloud server types: + +### Shared vCPU (Cost-effective) +- **CX11**: 1 vCPU, 4 GB RAM +- **CX21**: 2 vCPU, 8 GB RAM +- **CX22**: 2 vCPU, 8 GB RAM (AMD) +- **CX31**: 2 vCPU, 8 GB RAM +- **CX32**: 2 vCPU, 8 GB RAM (AMD) +- **CX41**: 4 vCPU, 16 GB RAM +- **CX42**: 4 vCPU, 16 GB RAM (AMD) +- **CX51**: 8 vCPU, 32 GB RAM +- **CX52**: 8 vCPU, 32 GB RAM (AMD) + +### Dedicated vCPU (High Performance) +- **CCX13**: 2 vCPU, 8 GB RAM +- **CCX23**: 4 vCPU, 16 GB RAM +- **CCX33**: 8 vCPU, 32 GB RAM +- **CCX43**: 16 vCPU, 64 GB RAM +- **CCX53**: 32 vCPU, 128 GB RAM +- **CCX63**: 48 vCPU, 192 GB RAM + +### CPU-Optimized +- **CPX11**: 2 vCPU, 4 GB RAM +- **CPX21**: 3 vCPU, 8 GB RAM +- **CPX31**: 4 vCPU, 16 GB RAM +- **CPX41**: 8 vCPU, 32 GB RAM +- **CPX51**: 16 vCPU, 64 GB RAM + +## Locations + +Available locations: +- **Falkenstein, Germany** (fsn1) - Primary location +- **Nuremberg, Germany** (nbg1) - Secondary location +- **Helsinki, Finland** (hel1) - EU Nordic +- **Ashburn, Virginia, USA** (ash) - US East Coast +- **Hillsboro, Oregon, USA** (hil) - US West Coast + +## Supported Operating Systems + +- Ubuntu 24.04 LTS +- Ubuntu 22.04 LTS (default) +- Ubuntu 20.04 LTS +- Debian 12 +- Debian 11 +- CentOS Stream 9 +- Fedora 39 +- Rocky Linux 9 +- AlmaLinux 9 + +## Configuration + +### Required Variables + +```hcl +# terraform.tfvars +hcloud_token = "your-hetzner-cloud-api-token" +``` + +### Maintaining Configuration + +The template uses `hetzner-config.json` for dynamic configuration: + +- **Server Types**: Add new server types with their specifications +- **Locations**: Add new Hetzner datacenters as they become available +- **Images**: Update with current Hetzner image names (verify with API) +- **Availability**: Map server type restrictions per location + +**Example**: Adding a new server type: +```json +"cx62": { "name": "CX62 (16 vCPU, 64 GB RAM, AMD)", "vcpus": 16, "memory": 64 } +``` + +**Important**: Always verify image names match Hetzner's official names exactly to avoid provisioning errors. + +### Optional Variables + +All other parameters can be configured through the Coder workspace creation interface: + +- **Location**: Choose the datacenter location +- **Server Type**: Select from available server configurations +- **Operating System**: Choose your preferred Linux distribution from the curated list +- **Custom Image Override**: Optionally specify a custom Hetzner Cloud image name (overrides the OS selection) +- **Home Volume Size**: Set the size of persistent storage (10-1000 GB) + +### Custom Images + +You can use custom images in two ways: + +1. **Override Field**: Leave the "Custom Image Override" field empty to use the selected OS, or enter a custom image name to override it +2. **Examples**: + - `my-custom-snapshot` - Your own Hetzner Cloud snapshot + - `debian-12-amd64` - Specific architecture variant + - `ubuntu-24.04` - Newer image not yet in the dropdown list + +The custom override takes precedence over the dropdown selection, allowing you to use any valid Hetzner Cloud image name. + +## Security + +The template includes: + +- Private networking for secure inter-service communication +- Firewall rules allowing only necessary ports (22, 80, 443, 8080) +- SSH key authentication +- User isolation through cloud-init configuration + +## Cost Optimization + +- Servers are destroyed when workspaces stop, minimizing compute costs +- Volumes persist but are only charged for storage when servers are stopped +- Choose appropriate server types based on workload requirements +- Consider using shared vCPU instances for development workloads + +## Troubleshooting + +### Invalid Server Type/Location Combination + +The template includes validation to prevent selecting server types that aren't available in certain locations. If you encounter this error, choose a different server type or location combination. + +### Image Not Found Errors + +If you get errors like "image not found" or "invalid image name": + +1. **Verify Image Names**: Check current available images using the API: + ```bash + curl -s -H "Authorization: Bearer $HCLOUD_TOKEN" \ + "https://api.hetzner.cloud/v1/images" | \ + jq '.images[] | select(.type=="system") | .name' | sort + ``` + +2. **Update JSON Configuration**: Edit `hetzner-config.json` to match exact image names from Hetzner +3. **Common Issues**: + - Some images may have architecture suffixes (e.g., `debian-12` vs `debian-12-amd64`) + - Image names may change over time as new versions are released + - Deprecated images are removed from the available list + +4. **Test Locally**: Before using in Coder, test image names with basic Terraform: + ```hcl + resource "hcloud_server" "test" { + name = "test" + server_type = "cx11" + image = "ubuntu-22.04" # Test this image name + location = "fsn1" + } + ``` + +### Volume Mount Issues + +If the home directory doesn't mount properly: +1. Check that the volume is attached to the server +2. Verify the cloud-init configuration is applied correctly +3. Ensure the filesystem is formatted as ext4 + +### Network Connectivity Issues + +If you can't connect to development servers: +1. Verify firewall rules allow the required ports +2. Check that the private network is configured correctly +3. Ensure the server has a public IP address + +## Notes + +> [!NOTE] +> This template is designed to be a starting point! Edit the Terraform configuration to extend the template to support your specific use case. + +> [!IMPORTANT] +> The SSH key in this template is a placeholder. In a production environment, you should replace it with your actual SSH public key or remove the SSH key resource entirely if not needed. + +> [!WARNING] +> Some server types may not be available in all locations. The template includes validation to prevent invalid combinations, but availability can change over time. diff --git a/registry/melmathari/templates/hetzner-cloud/cloud-config.yaml.tftpl b/registry/melmathari/templates/hetzner-cloud/cloud-config.yaml.tftpl new file mode 100644 index 000000000..053eedd15 --- /dev/null +++ b/registry/melmathari/templates/hetzner-cloud/cloud-config.yaml.tftpl @@ -0,0 +1,57 @@ +#cloud-config +hostname: ${hostname} +users: + - name: ${username} + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + groups: sudo + shell: /bin/bash +packages: + - git + - curl + - wget + - unzip + - htop +disk_setup: + ${volume_device}: + table_type: 'gpt' + layout: true + overwrite: false +fs_setup: + - label: coder-home + filesystem: ext4 + device: ${volume_device} + partition: auto +mounts: + - ["${volume_device}", "/home/${username}", "ext4", "defaults", "0", "2"] +write_files: + - path: /opt/coder/init + permissions: "0755" + encoding: b64 + content: ${init_script} + - path: /etc/systemd/system/coder-agent.service + permissions: "0644" + content: | + [Unit] + Description=Coder Agent + After=network-online.target + Wants=network-online.target + + [Service] + User=${username} + ExecStart=/opt/coder/init + Environment=CODER_AGENT_TOKEN=${coder_agent_token} + Restart=always + RestartSec=10 + TimeoutStopSec=90 + KillMode=process + + OOMScoreAdjust=-1000 + SyslogIdentifier=coder-agent + + [Install] + WantedBy=multi-user.target +runcmd: + - mkdir -p /home/${username} + - chown ${username}:${username} /home/${username} + - systemctl enable coder-agent + - systemctl start coder-agent diff --git a/registry/melmathari/templates/hetzner-cloud/hetzner-config.json b/registry/melmathari/templates/hetzner-cloud/hetzner-config.json new file mode 100644 index 000000000..e2cb374cf --- /dev/null +++ b/registry/melmathari/templates/hetzner-cloud/hetzner-config.json @@ -0,0 +1,49 @@ +{ + "_comment": "Image names should match Hetzner's official Terraform provider names exactly. Verify with: curl -H 'Authorization: Bearer $HCLOUD_TOKEN' 'https://api.hetzner.cloud/v1/images' | jq '.images[].name'", + "type_meta": { + "locations": { + "fsn1": { "name": "Falkenstein, Germany", "zone": "eu-central" }, + "nbg1": { "name": "Nuremberg, Germany", "zone": "eu-central" }, + "hel1": { "name": "Helsinki, Finland", "zone": "eu-central" }, + "ash": { "name": "Ashburn, VA, USA", "zone": "us-east" }, + "hil": { "name": "Hillsboro, OR, USA", "zone": "us-west" } + }, + "server_types": { + "cx11": { "name": "CX11 (1 vCPU, 4 GB RAM)", "vcpus": 1, "memory": 4 }, + "cx21": { "name": "CX21 (2 vCPU, 8 GB RAM)", "vcpus": 2, "memory": 8 }, + "cx22": { "name": "CX22 (2 vCPU, 8 GB RAM, AMD)", "vcpus": 2, "memory": 8 }, + "cx31": { "name": "CX31 (2 vCPU, 8 GB RAM)", "vcpus": 2, "memory": 8 }, + "cx32": { "name": "CX32 (2 vCPU, 8 GB RAM, AMD)", "vcpus": 2, "memory": 8 }, + "cx41": { "name": "CX41 (4 vCPU, 16 GB RAM)", "vcpus": 4, "memory": 16 }, + "cx42": { "name": "CX42 (4 vCPU, 16 GB RAM, AMD)", "vcpus": 4, "memory": 16 }, + "cx51": { "name": "CX51 (8 vCPU, 32 GB RAM)", "vcpus": 8, "memory": 32 }, + "cx52": { "name": "CX52 (8 vCPU, 32 GB RAM, AMD)", "vcpus": 8, "memory": 32 }, + "ccx13": { "name": "CCX13 (2 vCPU, 8 GB RAM, Dedicated)", "vcpus": 2, "memory": 8 }, + "ccx23": { "name": "CCX23 (4 vCPU, 16 GB RAM, Dedicated)", "vcpus": 4, "memory": 16 }, + "ccx33": { "name": "CCX33 (8 vCPU, 32 GB RAM, Dedicated)", "vcpus": 8, "memory": 32 }, + "ccx43": { "name": "CCX43 (16 vCPU, 64 GB RAM, Dedicated)", "vcpus": 16, "memory": 64 }, + "ccx53": { "name": "CCX53 (32 vCPU, 128 GB RAM, Dedicated)", "vcpus": 32, "memory": 128 }, + "ccx63": { "name": "CCX63 (48 vCPU, 192 GB RAM, Dedicated)", "vcpus": 48, "memory": 192 }, + "cpx11": { "name": "CPX11 (2 vCPU, 4 GB RAM, CPU-optimized)", "vcpus": 2, "memory": 4 }, + "cpx21": { "name": "CPX21 (3 vCPU, 8 GB RAM, CPU-optimized)", "vcpus": 3, "memory": 8 }, + "cpx31": { "name": "CPX31 (4 vCPU, 16 GB RAM, CPU-optimized)", "vcpus": 4, "memory": 16 }, + "cpx41": { "name": "CPX41 (8 vCPU, 32 GB RAM, CPU-optimized)", "vcpus": 8, "memory": 32 }, + "cpx51": { "name": "CPX51 (16 vCPU, 64 GB RAM, CPU-optimized)", "vcpus": 16, "memory": 64 } + }, + "images": { + "ubuntu-24.04": { "name": "Ubuntu 24.04 LTS", "icon": "/icon/ubuntu.svg" }, + "ubuntu-22.04": { "name": "Ubuntu 22.04 LTS", "icon": "/icon/ubuntu.svg" }, + "ubuntu-20.04": { "name": "Ubuntu 20.04 LTS", "icon": "/icon/ubuntu.svg" }, + "debian-12": { "name": "Debian 12", "icon": "/icon/debian.svg" }, + "debian-11": { "name": "Debian 11", "icon": "/icon/debian.svg" }, + "centos-stream-9": { "name": "CentOS Stream 9", "icon": "/icon/centos.svg" }, + "fedora-39": { "name": "Fedora 39", "icon": "/icon/fedora.svg" }, + "rocky-9": { "name": "Rocky Linux 9", "icon": "/icon/rockylinux.svg" }, + "alma-9": { "name": "AlmaLinux 9", "icon": "/icon/almalinux.svg" } + } + }, + "availability": { + "ccx63": ["fsn1", "nbg1"], + "*": ["fsn1", "nbg1", "hel1", "ash", "hil"] + } +} diff --git a/registry/melmathari/templates/hetzner-cloud/main.tf b/registry/melmathari/templates/hetzner-cloud/main.tf new file mode 100644 index 000000000..d93c0997b --- /dev/null +++ b/registry/melmathari/templates/hetzner-cloud/main.tf @@ -0,0 +1,391 @@ +terraform { + required_providers { + coder = { + source = "coder/coder" + } + hcloud = { + source = "hetznercloud/hcloud" + } + } +} + +provider "coder" {} + +# Variable for Hetzner Cloud API token +variable "hcloud_token" { + description = "Hetzner Cloud API token for authentication" + type = string + sensitive = true + default = "" +} + +# Configure the Hetzner Cloud Provider +provider "hcloud" { + token = var.hcloud_token != "" ? var.hcloud_token : null +} + +data "coder_workspace" "me" {} +data "coder_workspace_owner" "me" {} + +# Load Hetzner Cloud configuration from JSON +locals { + hetzner_config = jsondecode(file("${path.module}/hetzner-config.json")) +} + +# Hetzner Cloud locations parameter (dynamically generated from JSON) +data "coder_parameter" "location" { + name = "location" + display_name = "Location" + description = "This is the location where your workspace will be created." + icon = "/emojis/1f30e.png" + type = "string" + default = "fsn1" + mutable = false + + dynamic "option" { + for_each = local.hetzner_config.type_meta.locations + content { + name = option.value.name + value = option.key + icon = "/emojis/1f30e.png" + } + } +} + + +# Hetzner Cloud server types parameter (dynamically generated from JSON) +data "coder_parameter" "server_type" { + name = "server_type" + display_name = "Server Type" + description = "Which Hetzner Cloud server type would you like to use?" + default = "cx22" + type = "string" + icon = "/icon/memory.svg" + mutable = false + + dynamic "option" { + for_each = local.hetzner_config.type_meta.server_types + content { + name = option.value.name + value = option.key + } + } +} + +# Server image parameter (dynamically generated from JSON) +data "coder_parameter" "server_image" { + name = "server_image" + display_name = "Server Image" + description = "Which operating system image would you like to use?" + default = "ubuntu-22.04" + type = "string" + mutable = false + + dynamic "option" { + for_each = local.hetzner_config.type_meta.images + content { + name = option.value.name + value = option.key + icon = option.value.icon + } + } + +} + +# Optional custom image override +data "coder_parameter" "custom_image_override" { + name = "custom_image_override" + display_name = "Custom Image Override (optional)" + description = "Leave empty to use the selected image above, or enter a custom Hetzner Cloud image name to override (e.g., 'my-custom-snapshot', 'debian-12-amd64')" + type = "string" + default = "" + mutable = false +} + +# Determine which image to use - custom override takes precedence +locals { + final_image = data.coder_parameter.custom_image_override.value != "" ? data.coder_parameter.custom_image_override.value : data.coder_parameter.server_image.value +} + +# Home volume size parameter +data "coder_parameter" "volume_size" { + name = "volume_size" + display_name = "Home Volume Size (GB)" + description = "How large would you like your home volume to be (in GB)?" + type = "number" + default = 20 + mutable = true + + validation { + min = 10 + max = 1000 + monotonic = "increasing" + } +} + +locals { + # Ensure unique names by including workspace ID + server_name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}-${substr(data.coder_workspace.me.id, 0, 8)}" + volume_name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}-${substr(data.coder_workspace.me.id, 0, 8)}-home" + network_name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}-${substr(data.coder_workspace.me.id, 0, 8)}-net" + firewall_name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}-${substr(data.coder_workspace.me.id, 0, 8)}-fw" + + # Get selected server type and location configuration + selected_server_type = local.hetzner_config.type_meta.server_types[data.coder_parameter.server_type.value] + selected_location = local.hetzner_config.type_meta.locations[data.coder_parameter.location.value] + network_zone = local.selected_location.zone + + # Get availability for selected server type (use specific or wildcard) + server_availability = lookup(local.hetzner_config.availability, data.coder_parameter.server_type.value, local.hetzner_config.availability["*"]) + + # Validate server type is available in selected location + is_valid_combination = contains(local.server_availability, data.coder_parameter.location.value) +} + +# Validation check for server type and location compatibility +resource "null_resource" "validate_server_location" { + count = local.is_valid_combination ? 0 : 1 + + provisioner "local-exec" { + command = "echo 'ERROR: Server type ${data.coder_parameter.server_type.value} is not available in location ${data.coder_parameter.location.value}' && exit 1" + } +} + +resource "coder_agent" "main" { + os = "linux" + arch = "amd64" + + metadata { + key = "cpu" + display_name = "CPU Usage" + interval = 5 + timeout = 5 + script = "coder stat cpu" + } + metadata { + key = "memory" + display_name = "Memory Usage" + interval = 5 + timeout = 5 + script = "coder stat mem" + } + metadata { + key = "home" + display_name = "Home Usage" + interval = 600 # every 10 minutes + timeout = 30 # df can take a while on large filesystems + script = "coder stat disk --path /home/${lower(data.coder_workspace_owner.me.name)}" + } +} + +# See https://registry.coder.com/modules/coder/code-server +module "code-server" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/code-server/coder" + + # This ensures that the latest non-breaking version of the module gets downloaded, you can also pin the module version to prevent breaking changes in production. + version = "~> 1.0" + + agent_id = coder_agent.main.id + order = 1 +} + +# See https://registry.coder.com/modules/coder/jetbrains +module "jetbrains" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/jetbrains/coder" + version = "~> 1.0" + agent_id = coder_agent.main.id + folder = "/home/coder" +} + +variable "ssh_key_id" { + type = number + description = <<-EOF + Hetzner Cloud SSH key ID (obtain via the Hetzner Cloud Console or CLI): + + Can be set to "0" for no SSH key. + + $ hcloud ssh-key list + EOF + sensitive = true + default = 0 + + validation { + condition = var.ssh_key_id >= 0 + error_message = "Invalid Hetzner Cloud SSH key ID, a number is required." + } +} + +# Create private network +resource "hcloud_network" "workspace" { + name = local.network_name + ip_range = "10.0.0.0/16" + + labels = { + "coder.workspace" = data.coder_workspace.me.name + "coder.owner" = data.coder_workspace_owner.me.name + "coder.resource" = "network" + } +} + +# Create network subnet +resource "hcloud_network_subnet" "workspace" { + network_id = hcloud_network.workspace.id + type = "cloud" + network_zone = local.network_zone + ip_range = "10.0.1.0/24" +} + +# Create firewall +resource "hcloud_firewall" "workspace" { + name = local.firewall_name + + labels = { + "coder.workspace" = data.coder_workspace.me.name + "coder.owner" = data.coder_workspace_owner.me.name + "coder.resource" = "firewall" + } + + rule { + direction = "in" + port = "22" + protocol = "tcp" + source_ips = ["0.0.0.0/0", "::/0"] + } + + rule { + direction = "in" + port = "80" + protocol = "tcp" + source_ips = ["0.0.0.0/0", "::/0"] + } + + rule { + direction = "in" + port = "443" + protocol = "tcp" + source_ips = ["0.0.0.0/0", "::/0"] + } + + rule { + direction = "in" + port = "8080" + protocol = "tcp" + source_ips = ["0.0.0.0/0", "::/0"] + } +} + +# Create volume for home directory +resource "hcloud_volume" "home_volume" { + name = local.volume_name + size = data.coder_parameter.volume_size.value + location = data.coder_parameter.location.value + format = "ext4" + + labels = { + "coder.workspace" = data.coder_workspace.me.name + "coder.owner" = data.coder_workspace_owner.me.name + "coder.resource" = "home-volume" + } + + # Protect the volume from being deleted due to changes in attributes + lifecycle { + ignore_changes = all + } +} + +# Create the server +resource "hcloud_server" "workspace" { + count = data.coder_workspace.me.start_count + name = local.server_name + server_type = data.coder_parameter.server_type.value + image = local.final_image + location = data.coder_parameter.location.value + ssh_keys = var.ssh_key_id > 0 ? [var.ssh_key_id] : [] + firewall_ids = [hcloud_firewall.workspace.id] + + labels = { + "coder.workspace" = data.coder_workspace.me.name + "coder.owner" = data.coder_workspace_owner.me.name + "coder.resource" = "workspace-server" + } + + public_net { + ipv4_enabled = true + ipv6_enabled = true + } + + network { + network_id = hcloud_network.workspace.id + ip = "10.0.1.5" + } + + user_data = templatefile("${path.module}/cloud-config.yaml.tftpl", { + hostname = local.server_name + username = lower(data.coder_workspace_owner.me.name) + volume_device = "/dev/sdb" + init_script = base64encode(coder_agent.main.init_script) + coder_agent_token = coder_agent.main.token + }) + + depends_on = [ + hcloud_network_subnet.workspace + ] + + # Proper lifecycle: server is destroyed when workspace stops, but volume persists + lifecycle { + ignore_changes = [ssh_keys, user_data] + } +} + +# Attach volume to server +resource "hcloud_volume_attachment" "home_volume" { + count = data.coder_workspace.me.start_count + volume_id = hcloud_volume.home_volume.id + server_id = hcloud_server.workspace[0].id + automount = true +} + +resource "coder_metadata" "workspace_info" { + count = data.coder_workspace.me.start_count + resource_id = hcloud_server.workspace[0].id + + item { + key = "location" + value = "${local.selected_location.name} (${hcloud_server.workspace[0].location})" + } + item { + key = "server_type" + value = "${local.selected_server_type.name} (${hcloud_server.workspace[0].server_type})" + } + item { + key = "vcpus" + value = "${local.selected_server_type.vcpus}" + } + item { + key = "memory" + value = "${local.selected_server_type.memory} GB" + } + item { + key = "image" + value = data.coder_parameter.custom_image_override.value != "" ? data.coder_parameter.custom_image_override.value : local.hetzner_config.type_meta.images[data.coder_parameter.server_image.value].name + } + item { + key = "public_ipv4" + value = hcloud_server.workspace[0].ipv4_address + } +} + +resource "coder_metadata" "volume_info" { + resource_id = hcloud_volume.home_volume.id + + item { + key = "size" + value = "${hcloud_volume.home_volume.size} GB" + } + item { + key = "location" + value = hcloud_volume.home_volume.location + } +} From 0cbe2839d35d35a6ec15c21f86e971b69cb05b26 Mon Sep 17 00:00:00 2001 From: Mido Date: Thu, 18 Sep 2025 20:23:46 +0200 Subject: [PATCH 02/21] prettifier --- .../melmathari/templates/hetzner-cloud/cloud-config.yaml.tftpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/melmathari/templates/hetzner-cloud/cloud-config.yaml.tftpl b/registry/melmathari/templates/hetzner-cloud/cloud-config.yaml.tftpl index 053eedd15..eb14bb537 100644 --- a/registry/melmathari/templates/hetzner-cloud/cloud-config.yaml.tftpl +++ b/registry/melmathari/templates/hetzner-cloud/cloud-config.yaml.tftpl @@ -13,7 +13,7 @@ packages: - htop disk_setup: ${volume_device}: - table_type: 'gpt' + table_type: "gpt" layout: true overwrite: false fs_setup: From 356221d22f63be468a0de3dd043610e99466cfaf Mon Sep 17 00:00:00 2001 From: Me Date: Thu, 18 Sep 2025 22:12:41 +0200 Subject: [PATCH 03/21] typos --- .github/typos.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/typos.toml b/.github/typos.toml index fdb747483..7a21014ad 100644 --- a/.github/typos.toml +++ b/.github/typos.toml @@ -5,6 +5,12 @@ Hashi = "Hashi" HashiCorp = "HashiCorp" mavrickrishi = "mavrickrishi" # Username mavrick = "mavrick" # Username +melmathari = "melmathari" # Username +fsn1 = "fsn1" # Hetzner Falkenstein datacenter code +nbg1 = "nbg1" # Hetzner Nuremberg datacenter code +hel1 = "hel1" # Hetzner Helsinki datacenter code +hcloud = "hcloud" # Hetzner Cloud CLI/API +vcpus = "vcpus" # Virtual CPUs [files] extend-exclude = ["registry/coder/templates/aws-devcontainer/architecture.svg"] #False positive \ No newline at end of file From a0adec3b29d3d167e8b80d817eb7f48111d13d26 Mon Sep 17 00:00:00 2001 From: Me Date: Thu, 18 Sep 2025 22:22:01 +0200 Subject: [PATCH 04/21] jq syntax --- registry/melmathari/templates/hetzner-cloud/hetzner-config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/melmathari/templates/hetzner-cloud/hetzner-config.json b/registry/melmathari/templates/hetzner-cloud/hetzner-config.json index e2cb374cf..224382667 100644 --- a/registry/melmathari/templates/hetzner-cloud/hetzner-config.json +++ b/registry/melmathari/templates/hetzner-cloud/hetzner-config.json @@ -1,5 +1,5 @@ { - "_comment": "Image names should match Hetzner's official Terraform provider names exactly. Verify with: curl -H 'Authorization: Bearer $HCLOUD_TOKEN' 'https://api.hetzner.cloud/v1/images' | jq '.images[].name'", + "_comment": "Image names should match Hetzner's official Terraform provider names exactly. Verify with: curl -H 'Authorization: Bearer $HCLOUD_TOKEN' 'https://api.hetzner.cloud/v1/images' | jq '.images[] | .name'", "type_meta": { "locations": { "fsn1": { "name": "Falkenstein, Germany", "zone": "eu-central" }, From 0798d81534c59ad05b2c760f935bb331bf5ada07 Mon Sep 17 00:00:00 2001 From: Me Date: Thu, 18 Sep 2025 23:26:43 +0200 Subject: [PATCH 05/21] server config updated --- .../templates/hetzner-cloud/hetzner-config.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/registry/melmathari/templates/hetzner-cloud/hetzner-config.json b/registry/melmathari/templates/hetzner-cloud/hetzner-config.json index 224382667..f17d05f8b 100644 --- a/registry/melmathari/templates/hetzner-cloud/hetzner-config.json +++ b/registry/melmathari/templates/hetzner-cloud/hetzner-config.json @@ -11,23 +11,23 @@ "server_types": { "cx11": { "name": "CX11 (1 vCPU, 4 GB RAM)", "vcpus": 1, "memory": 4 }, "cx21": { "name": "CX21 (2 vCPU, 8 GB RAM)", "vcpus": 2, "memory": 8 }, - "cx22": { "name": "CX22 (2 vCPU, 8 GB RAM, AMD)", "vcpus": 2, "memory": 8 }, + "cx22": { "name": "CX22 (2 vCPU, 4 GB RAM, AMD)", "vcpus": 2, "memory": 4 }, "cx31": { "name": "CX31 (2 vCPU, 8 GB RAM)", "vcpus": 2, "memory": 8 }, - "cx32": { "name": "CX32 (2 vCPU, 8 GB RAM, AMD)", "vcpus": 2, "memory": 8 }, + "cx32": { "name": "CX32 (4 vCPU, 8 GB RAM, AMD)", "vcpus": 4, "memory": 8 }, "cx41": { "name": "CX41 (4 vCPU, 16 GB RAM)", "vcpus": 4, "memory": 16 }, - "cx42": { "name": "CX42 (4 vCPU, 16 GB RAM, AMD)", "vcpus": 4, "memory": 16 }, + "cx42": { "name": "CX42 (8 vCPU, 16 GB RAM, AMD)", "vcpus": 8, "memory": 16 }, "cx51": { "name": "CX51 (8 vCPU, 32 GB RAM)", "vcpus": 8, "memory": 32 }, - "cx52": { "name": "CX52 (8 vCPU, 32 GB RAM, AMD)", "vcpus": 8, "memory": 32 }, + "cx52": { "name": "CX52 (16 vCPU, 32 GB RAM, AMD)", "vcpus": 16, "memory": 32 }, "ccx13": { "name": "CCX13 (2 vCPU, 8 GB RAM, Dedicated)", "vcpus": 2, "memory": 8 }, "ccx23": { "name": "CCX23 (4 vCPU, 16 GB RAM, Dedicated)", "vcpus": 4, "memory": 16 }, "ccx33": { "name": "CCX33 (8 vCPU, 32 GB RAM, Dedicated)", "vcpus": 8, "memory": 32 }, "ccx43": { "name": "CCX43 (16 vCPU, 64 GB RAM, Dedicated)", "vcpus": 16, "memory": 64 }, "ccx53": { "name": "CCX53 (32 vCPU, 128 GB RAM, Dedicated)", "vcpus": 32, "memory": 128 }, "ccx63": { "name": "CCX63 (48 vCPU, 192 GB RAM, Dedicated)", "vcpus": 48, "memory": 192 }, - "cpx11": { "name": "CPX11 (2 vCPU, 4 GB RAM, CPU-optimized)", "vcpus": 2, "memory": 4 }, - "cpx21": { "name": "CPX21 (3 vCPU, 8 GB RAM, CPU-optimized)", "vcpus": 3, "memory": 8 }, - "cpx31": { "name": "CPX31 (4 vCPU, 16 GB RAM, CPU-optimized)", "vcpus": 4, "memory": 16 }, - "cpx41": { "name": "CPX41 (8 vCPU, 32 GB RAM, CPU-optimized)", "vcpus": 8, "memory": 32 }, + "cpx11": { "name": "CPX11 (2 vCPU, 2 GB RAM, CPU-optimized)", "vcpus": 2, "memory": 2 }, + "cpx21": { "name": "CPX21 (3 vCPU, 4 GB RAM, CPU-optimized)", "vcpus": 3, "memory": 4 }, + "cpx31": { "name": "CPX31 (4 vCPU, 8 GB RAM, CPU-optimized)", "vcpus": 4, "memory": 8 }, + "cpx41": { "name": "CPX41 (8 vCPU, 16 GB RAM, CPU-optimized)", "vcpus": 8, "memory": 16 }, "cpx51": { "name": "CPX51 (16 vCPU, 64 GB RAM, CPU-optimized)", "vcpus": 16, "memory": 64 } }, "images": { From e12e79205b79af1cf0007d7bc26f2b0161d3acb4 Mon Sep 17 00:00:00 2001 From: Mohamed Mathari <155896313+melmathari@users.noreply.github.com> Date: Fri, 19 Sep 2025 08:49:26 +0200 Subject: [PATCH 06/21] Update registry/melmathari/templates/hetzner-cloud/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- registry/melmathari/templates/hetzner-cloud/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/melmathari/templates/hetzner-cloud/README.md b/registry/melmathari/templates/hetzner-cloud/README.md index b8fff5f0b..38379794a 100644 --- a/registry/melmathari/templates/hetzner-cloud/README.md +++ b/registry/melmathari/templates/hetzner-cloud/README.md @@ -76,7 +76,7 @@ The template supports all major Hetzner Cloud server types: ### Shared vCPU (Cost-effective) - **CX11**: 1 vCPU, 4 GB RAM - **CX21**: 2 vCPU, 8 GB RAM -- **CX22**: 2 vCPU, 8 GB RAM (AMD) +- **CX22**: 2 vCPU, 4 GB RAM (AMD) - **CX31**: 2 vCPU, 8 GB RAM - **CX32**: 2 vCPU, 8 GB RAM (AMD) - **CX41**: 4 vCPU, 16 GB RAM From 9ab8f47648707efd417324f055fb3dae6ab72e9f Mon Sep 17 00:00:00 2001 From: Mohamed Mathari <155896313+melmathari@users.noreply.github.com> Date: Fri, 19 Sep 2025 08:50:02 +0200 Subject: [PATCH 07/21] Update registry/melmathari/templates/hetzner-cloud/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- registry/melmathari/templates/hetzner-cloud/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/melmathari/templates/hetzner-cloud/README.md b/registry/melmathari/templates/hetzner-cloud/README.md index 38379794a..c6a2dc21c 100644 --- a/registry/melmathari/templates/hetzner-cloud/README.md +++ b/registry/melmathari/templates/hetzner-cloud/README.md @@ -78,7 +78,7 @@ The template supports all major Hetzner Cloud server types: - **CX21**: 2 vCPU, 8 GB RAM - **CX22**: 2 vCPU, 4 GB RAM (AMD) - **CX31**: 2 vCPU, 8 GB RAM -- **CX32**: 2 vCPU, 8 GB RAM (AMD) +- **CX32**: 4 vCPU, 8 GB RAM (AMD) - **CX41**: 4 vCPU, 16 GB RAM - **CX42**: 4 vCPU, 16 GB RAM (AMD) - **CX51**: 8 vCPU, 32 GB RAM From 8cd33d25cebe6be67e18168544025176286b46b3 Mon Sep 17 00:00:00 2001 From: Mohamed Mathari <155896313+melmathari@users.noreply.github.com> Date: Fri, 19 Sep 2025 08:50:12 +0200 Subject: [PATCH 08/21] Update registry/melmathari/templates/hetzner-cloud/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- registry/melmathari/templates/hetzner-cloud/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/melmathari/templates/hetzner-cloud/README.md b/registry/melmathari/templates/hetzner-cloud/README.md index c6a2dc21c..3e22c02d2 100644 --- a/registry/melmathari/templates/hetzner-cloud/README.md +++ b/registry/melmathari/templates/hetzner-cloud/README.md @@ -80,7 +80,7 @@ The template supports all major Hetzner Cloud server types: - **CX31**: 2 vCPU, 8 GB RAM - **CX32**: 4 vCPU, 8 GB RAM (AMD) - **CX41**: 4 vCPU, 16 GB RAM -- **CX42**: 4 vCPU, 16 GB RAM (AMD) +- **CX42**: 8 vCPU, 16 GB RAM (AMD) - **CX51**: 8 vCPU, 32 GB RAM - **CX52**: 8 vCPU, 32 GB RAM (AMD) From 6b520c5a558b2a575eed5bdc9310bf98e18267e7 Mon Sep 17 00:00:00 2001 From: Mohamed Mathari <155896313+melmathari@users.noreply.github.com> Date: Fri, 19 Sep 2025 08:50:20 +0200 Subject: [PATCH 09/21] Update registry/melmathari/templates/hetzner-cloud/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- registry/melmathari/templates/hetzner-cloud/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/melmathari/templates/hetzner-cloud/README.md b/registry/melmathari/templates/hetzner-cloud/README.md index 3e22c02d2..97238fbb7 100644 --- a/registry/melmathari/templates/hetzner-cloud/README.md +++ b/registry/melmathari/templates/hetzner-cloud/README.md @@ -82,7 +82,7 @@ The template supports all major Hetzner Cloud server types: - **CX41**: 4 vCPU, 16 GB RAM - **CX42**: 8 vCPU, 16 GB RAM (AMD) - **CX51**: 8 vCPU, 32 GB RAM -- **CX52**: 8 vCPU, 32 GB RAM (AMD) +- **CX52**: 16 vCPU, 32 GB RAM (AMD) ### Dedicated vCPU (High Performance) - **CCX13**: 2 vCPU, 8 GB RAM From 43a071837c70271bdd80bd8b7fcde16db244fac6 Mon Sep 17 00:00:00 2001 From: Mohamed Mathari <155896313+melmathari@users.noreply.github.com> Date: Fri, 19 Sep 2025 08:50:35 +0200 Subject: [PATCH 10/21] Update registry/melmathari/templates/hetzner-cloud/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- registry/melmathari/templates/hetzner-cloud/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/registry/melmathari/templates/hetzner-cloud/README.md b/registry/melmathari/templates/hetzner-cloud/README.md index 97238fbb7..194459cb3 100644 --- a/registry/melmathari/templates/hetzner-cloud/README.md +++ b/registry/melmathari/templates/hetzner-cloud/README.md @@ -93,10 +93,10 @@ The template supports all major Hetzner Cloud server types: - **CCX63**: 48 vCPU, 192 GB RAM ### CPU-Optimized -- **CPX11**: 2 vCPU, 4 GB RAM -- **CPX21**: 3 vCPU, 8 GB RAM -- **CPX31**: 4 vCPU, 16 GB RAM -- **CPX41**: 8 vCPU, 32 GB RAM +- **CPX11**: 2 vCPU, 2 GB RAM +- **CPX21**: 3 vCPU, 4 GB RAM +- **CPX31**: 4 vCPU, 8 GB RAM +- **CPX41**: 8 vCPU, 16 GB RAM - **CPX51**: 16 vCPU, 64 GB RAM ## Locations From de9c9c00d19580638ed698a7d8442cabf301ed5c Mon Sep 17 00:00:00 2001 From: Mido Date: Fri, 19 Sep 2025 09:00:11 +0200 Subject: [PATCH 11/21] README prettier formatting --- .../templates/hetzner-cloud/README.md | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/registry/melmathari/templates/hetzner-cloud/README.md b/registry/melmathari/templates/hetzner-cloud/README.md index 194459cb3..8cf6902e5 100644 --- a/registry/melmathari/templates/hetzner-cloud/README.md +++ b/registry/melmathari/templates/hetzner-cloud/README.md @@ -11,8 +11,9 @@ tags: [vm, linux, hetzner, cloud, germany] Provision Hetzner Cloud servers as [Coder workspaces](https://coder.com/docs/workspaces) with this template. This template provides a comprehensive Hetzner Cloud setup with: + - **Dynamic Configuration**: Server types, locations, and images loaded from JSON -- **Smart Validation**: Prevents invalid server type/location combinations +- **Smart Validation**: Prevents invalid server type/location combinations - **Multiple Server Types**: Shared, dedicated, and CPU-optimized instances - **Global Locations**: Germany, Finland, and USA datacenters - **Persistent Storage**: Home volumes that survive workspace restarts @@ -45,8 +46,8 @@ export HCLOUD_TOKEN="your-hetzner-cloud-api-token" # List all available images curl -s -H "Authorization: Bearer $HCLOUD_TOKEN" \ - "https://api.hetzner.cloud/v1/images" | \ - jq '.images[] | select(.type=="system") | .name' + "https://api.hetzner.cloud/v1/images" \ + | jq '.images[] | select(.type=="system") | .name' ``` If you encounter image-related errors, check that the image names in `hetzner-config.json` match the official names exactly (some may include architecture suffixes like `-amd64`). @@ -74,8 +75,9 @@ This means that when the workspace restarts, any tools or files outside of the h The template supports all major Hetzner Cloud server types: ### Shared vCPU (Cost-effective) + - **CX11**: 1 vCPU, 4 GB RAM -- **CX21**: 2 vCPU, 8 GB RAM +- **CX21**: 2 vCPU, 8 GB RAM - **CX22**: 2 vCPU, 4 GB RAM (AMD) - **CX31**: 2 vCPU, 8 GB RAM - **CX32**: 4 vCPU, 8 GB RAM (AMD) @@ -85,6 +87,7 @@ The template supports all major Hetzner Cloud server types: - **CX52**: 16 vCPU, 32 GB RAM (AMD) ### Dedicated vCPU (High Performance) + - **CCX13**: 2 vCPU, 8 GB RAM - **CCX23**: 4 vCPU, 16 GB RAM - **CCX33**: 8 vCPU, 32 GB RAM @@ -93,6 +96,7 @@ The template supports all major Hetzner Cloud server types: - **CCX63**: 48 vCPU, 192 GB RAM ### CPU-Optimized + - **CPX11**: 2 vCPU, 2 GB RAM - **CPX21**: 3 vCPU, 4 GB RAM - **CPX31**: 4 vCPU, 8 GB RAM @@ -102,6 +106,7 @@ The template supports all major Hetzner Cloud server types: ## Locations Available locations: + - **Falkenstein, Germany** (fsn1) - Primary location - **Nuremberg, Germany** (nbg1) - Secondary location - **Helsinki, Finland** (hel1) - EU Nordic @@ -134,11 +139,12 @@ hcloud_token = "your-hetzner-cloud-api-token" The template uses `hetzner-config.json` for dynamic configuration: - **Server Types**: Add new server types with their specifications -- **Locations**: Add new Hetzner datacenters as they become available +- **Locations**: Add new Hetzner datacenters as they become available - **Images**: Update with current Hetzner image names (verify with API) - **Availability**: Map server type restrictions per location **Example**: Adding a new server type: + ```json "cx62": { "name": "CX62 (16 vCPU, 64 GB RAM, AMD)", "vcpus": 16, "memory": 64 } ``` @@ -160,7 +166,7 @@ All other parameters can be configured through the Coder workspace creation inte You can use custom images in two ways: 1. **Override Field**: Leave the "Custom Image Override" field empty to use the selected OS, or enter a custom image name to override it -2. **Examples**: +2. **Examples**: - `my-custom-snapshot` - Your own Hetzner Cloud snapshot - `debian-12-amd64` - Specific architecture variant - `ubuntu-24.04` - Newer image not yet in the dropdown list @@ -194,14 +200,15 @@ The template includes validation to prevent selecting server types that aren't a If you get errors like "image not found" or "invalid image name": 1. **Verify Image Names**: Check current available images using the API: + ```bash curl -s -H "Authorization: Bearer $HCLOUD_TOKEN" \ - "https://api.hetzner.cloud/v1/images" | \ - jq '.images[] | select(.type=="system") | .name' | sort + "https://api.hetzner.cloud/v1/images" \ + | jq '.images[] | select(.type=="system") | .name' | sort ``` 2. **Update JSON Configuration**: Edit `hetzner-config.json` to match exact image names from Hetzner -3. **Common Issues**: +3. **Common Issues**: - Some images may have architecture suffixes (e.g., `debian-12` vs `debian-12-amd64`) - Image names may change over time as new versions are released - Deprecated images are removed from the available list @@ -219,6 +226,7 @@ If you get errors like "image not found" or "invalid image name": ### Volume Mount Issues If the home directory doesn't mount properly: + 1. Check that the volume is attached to the server 2. Verify the cloud-init configuration is applied correctly 3. Ensure the filesystem is formatted as ext4 @@ -226,6 +234,7 @@ If the home directory doesn't mount properly: ### Network Connectivity Issues If you can't connect to development servers: + 1. Verify firewall rules allow the required ports 2. Check that the private network is configured correctly 3. Ensure the server has a public IP address From 035b8bff643744b2fad1653462933598615059d2 Mon Sep 17 00:00:00 2001 From: Mido Date: Fri, 19 Sep 2025 09:10:01 +0200 Subject: [PATCH 12/21] Hetzner Config prettier --- .../hetzner-cloud/hetzner-config.json | 110 +++++++++++++++--- 1 file changed, 91 insertions(+), 19 deletions(-) diff --git a/registry/melmathari/templates/hetzner-cloud/hetzner-config.json b/registry/melmathari/templates/hetzner-cloud/hetzner-config.json index f17d05f8b..71f1e5e32 100644 --- a/registry/melmathari/templates/hetzner-cloud/hetzner-config.json +++ b/registry/melmathari/templates/hetzner-cloud/hetzner-config.json @@ -11,32 +11,104 @@ "server_types": { "cx11": { "name": "CX11 (1 vCPU, 4 GB RAM)", "vcpus": 1, "memory": 4 }, "cx21": { "name": "CX21 (2 vCPU, 8 GB RAM)", "vcpus": 2, "memory": 8 }, - "cx22": { "name": "CX22 (2 vCPU, 4 GB RAM, AMD)", "vcpus": 2, "memory": 4 }, + "cx22": { + "name": "CX22 (2 vCPU, 4 GB RAM, AMD)", + "vcpus": 2, + "memory": 4 + }, "cx31": { "name": "CX31 (2 vCPU, 8 GB RAM)", "vcpus": 2, "memory": 8 }, - "cx32": { "name": "CX32 (4 vCPU, 8 GB RAM, AMD)", "vcpus": 4, "memory": 8 }, + "cx32": { + "name": "CX32 (4 vCPU, 8 GB RAM, AMD)", + "vcpus": 4, + "memory": 8 + }, "cx41": { "name": "CX41 (4 vCPU, 16 GB RAM)", "vcpus": 4, "memory": 16 }, - "cx42": { "name": "CX42 (8 vCPU, 16 GB RAM, AMD)", "vcpus": 8, "memory": 16 }, + "cx42": { + "name": "CX42 (8 vCPU, 16 GB RAM, AMD)", + "vcpus": 8, + "memory": 16 + }, "cx51": { "name": "CX51 (8 vCPU, 32 GB RAM)", "vcpus": 8, "memory": 32 }, - "cx52": { "name": "CX52 (16 vCPU, 32 GB RAM, AMD)", "vcpus": 16, "memory": 32 }, - "ccx13": { "name": "CCX13 (2 vCPU, 8 GB RAM, Dedicated)", "vcpus": 2, "memory": 8 }, - "ccx23": { "name": "CCX23 (4 vCPU, 16 GB RAM, Dedicated)", "vcpus": 4, "memory": 16 }, - "ccx33": { "name": "CCX33 (8 vCPU, 32 GB RAM, Dedicated)", "vcpus": 8, "memory": 32 }, - "ccx43": { "name": "CCX43 (16 vCPU, 64 GB RAM, Dedicated)", "vcpus": 16, "memory": 64 }, - "ccx53": { "name": "CCX53 (32 vCPU, 128 GB RAM, Dedicated)", "vcpus": 32, "memory": 128 }, - "ccx63": { "name": "CCX63 (48 vCPU, 192 GB RAM, Dedicated)", "vcpus": 48, "memory": 192 }, - "cpx11": { "name": "CPX11 (2 vCPU, 2 GB RAM, CPU-optimized)", "vcpus": 2, "memory": 2 }, - "cpx21": { "name": "CPX21 (3 vCPU, 4 GB RAM, CPU-optimized)", "vcpus": 3, "memory": 4 }, - "cpx31": { "name": "CPX31 (4 vCPU, 8 GB RAM, CPU-optimized)", "vcpus": 4, "memory": 8 }, - "cpx41": { "name": "CPX41 (8 vCPU, 16 GB RAM, CPU-optimized)", "vcpus": 8, "memory": 16 }, - "cpx51": { "name": "CPX51 (16 vCPU, 64 GB RAM, CPU-optimized)", "vcpus": 16, "memory": 64 } + "cx52": { + "name": "CX52 (16 vCPU, 32 GB RAM, AMD)", + "vcpus": 16, + "memory": 32 + }, + "ccx13": { + "name": "CCX13 (2 vCPU, 8 GB RAM, Dedicated)", + "vcpus": 2, + "memory": 8 + }, + "ccx23": { + "name": "CCX23 (4 vCPU, 16 GB RAM, Dedicated)", + "vcpus": 4, + "memory": 16 + }, + "ccx33": { + "name": "CCX33 (8 vCPU, 32 GB RAM, Dedicated)", + "vcpus": 8, + "memory": 32 + }, + "ccx43": { + "name": "CCX43 (16 vCPU, 64 GB RAM, Dedicated)", + "vcpus": 16, + "memory": 64 + }, + "ccx53": { + "name": "CCX53 (32 vCPU, 128 GB RAM, Dedicated)", + "vcpus": 32, + "memory": 128 + }, + "ccx63": { + "name": "CCX63 (48 vCPU, 192 GB RAM, Dedicated)", + "vcpus": 48, + "memory": 192 + }, + "cpx11": { + "name": "CPX11 (2 vCPU, 2 GB RAM, CPU-optimized)", + "vcpus": 2, + "memory": 2 + }, + "cpx21": { + "name": "CPX21 (3 vCPU, 4 GB RAM, CPU-optimized)", + "vcpus": 3, + "memory": 4 + }, + "cpx31": { + "name": "CPX31 (4 vCPU, 8 GB RAM, CPU-optimized)", + "vcpus": 4, + "memory": 8 + }, + "cpx41": { + "name": "CPX41 (8 vCPU, 16 GB RAM, CPU-optimized)", + "vcpus": 8, + "memory": 16 + }, + "cpx51": { + "name": "CPX51 (16 vCPU, 64 GB RAM, CPU-optimized)", + "vcpus": 16, + "memory": 64 + } }, "images": { - "ubuntu-24.04": { "name": "Ubuntu 24.04 LTS", "icon": "/icon/ubuntu.svg" }, - "ubuntu-22.04": { "name": "Ubuntu 22.04 LTS", "icon": "/icon/ubuntu.svg" }, - "ubuntu-20.04": { "name": "Ubuntu 20.04 LTS", "icon": "/icon/ubuntu.svg" }, + "ubuntu-24.04": { + "name": "Ubuntu 24.04 LTS", + "icon": "/icon/ubuntu.svg" + }, + "ubuntu-22.04": { + "name": "Ubuntu 22.04 LTS", + "icon": "/icon/ubuntu.svg" + }, + "ubuntu-20.04": { + "name": "Ubuntu 20.04 LTS", + "icon": "/icon/ubuntu.svg" + }, "debian-12": { "name": "Debian 12", "icon": "/icon/debian.svg" }, "debian-11": { "name": "Debian 11", "icon": "/icon/debian.svg" }, - "centos-stream-9": { "name": "CentOS Stream 9", "icon": "/icon/centos.svg" }, + "centos-stream-9": { + "name": "CentOS Stream 9", + "icon": "/icon/centos.svg" + }, "fedora-39": { "name": "Fedora 39", "icon": "/icon/fedora.svg" }, "rocky-9": { "name": "Rocky Linux 9", "icon": "/icon/rockylinux.svg" }, "alma-9": { "name": "AlmaLinux 9", "icon": "/icon/almalinux.svg" } From f394d7fc1afaf453053edef568183f3c640b3617 Mon Sep 17 00:00:00 2001 From: Mido Date: Fri, 19 Sep 2025 09:11:33 +0200 Subject: [PATCH 13/21] terraform fmt on main.tf --- .../templates/hetzner-cloud/main.tf | 110 +++++++++--------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/registry/melmathari/templates/hetzner-cloud/main.tf b/registry/melmathari/templates/hetzner-cloud/main.tf index d93c0997b..a5b64f601 100644 --- a/registry/melmathari/templates/hetzner-cloud/main.tf +++ b/registry/melmathari/templates/hetzner-cloud/main.tf @@ -41,7 +41,7 @@ data "coder_parameter" "location" { type = "string" default = "fsn1" mutable = false - + dynamic "option" { for_each = local.hetzner_config.type_meta.locations content { @@ -80,7 +80,7 @@ data "coder_parameter" "server_image" { default = "ubuntu-22.04" type = "string" mutable = false - + dynamic "option" { for_each = local.hetzner_config.type_meta.images content { @@ -89,7 +89,7 @@ data "coder_parameter" "server_image" { icon = option.value.icon } } - + } # Optional custom image override @@ -125,19 +125,19 @@ data "coder_parameter" "volume_size" { locals { # Ensure unique names by including workspace ID - server_name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}-${substr(data.coder_workspace.me.id, 0, 8)}" - volume_name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}-${substr(data.coder_workspace.me.id, 0, 8)}-home" - network_name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}-${substr(data.coder_workspace.me.id, 0, 8)}-net" - firewall_name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}-${substr(data.coder_workspace.me.id, 0, 8)}-fw" - + server_name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}-${substr(data.coder_workspace.me.id, 0, 8)}" + volume_name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}-${substr(data.coder_workspace.me.id, 0, 8)}-home" + network_name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}-${substr(data.coder_workspace.me.id, 0, 8)}-net" + firewall_name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}-${substr(data.coder_workspace.me.id, 0, 8)}-fw" + # Get selected server type and location configuration selected_server_type = local.hetzner_config.type_meta.server_types[data.coder_parameter.server_type.value] - selected_location = local.hetzner_config.type_meta.locations[data.coder_parameter.location.value] - network_zone = local.selected_location.zone - + selected_location = local.hetzner_config.type_meta.locations[data.coder_parameter.location.value] + network_zone = local.selected_location.zone + # Get availability for selected server type (use specific or wildcard) server_availability = lookup(local.hetzner_config.availability, data.coder_parameter.server_type.value, local.hetzner_config.availability["*"]) - + # Validate server type is available in selected location is_valid_combination = contains(local.server_availability, data.coder_parameter.location.value) } @@ -145,7 +145,7 @@ locals { # Validation check for server type and location compatibility resource "null_resource" "validate_server_location" { count = local.is_valid_combination ? 0 : 1 - + provisioner "local-exec" { command = "echo 'ERROR: Server type ${data.coder_parameter.server_type.value} is not available in location ${data.coder_parameter.location.value}' && exit 1" } @@ -192,11 +192,11 @@ module "code-server" { # See https://registry.coder.com/modules/coder/jetbrains module "jetbrains" { - count = data.coder_workspace.me.start_count - source = "registry.coder.com/coder/jetbrains/coder" - version = "~> 1.0" - agent_id = coder_agent.main.id - folder = "/home/coder" + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/jetbrains/coder" + version = "~> 1.0" + agent_id = coder_agent.main.id + folder = "/home/coder" } variable "ssh_key_id" { @@ -221,7 +221,7 @@ variable "ssh_key_id" { resource "hcloud_network" "workspace" { name = local.network_name ip_range = "10.0.0.0/16" - + labels = { "coder.workspace" = data.coder_workspace.me.name "coder.owner" = data.coder_workspace_owner.me.name @@ -240,55 +240,55 @@ resource "hcloud_network_subnet" "workspace" { # Create firewall resource "hcloud_firewall" "workspace" { name = local.firewall_name - + labels = { "coder.workspace" = data.coder_workspace.me.name "coder.owner" = data.coder_workspace_owner.me.name "coder.resource" = "firewall" } - + rule { - direction = "in" - port = "22" - protocol = "tcp" + direction = "in" + port = "22" + protocol = "tcp" source_ips = ["0.0.0.0/0", "::/0"] } - + rule { - direction = "in" - port = "80" - protocol = "tcp" + direction = "in" + port = "80" + protocol = "tcp" source_ips = ["0.0.0.0/0", "::/0"] } - + rule { - direction = "in" - port = "443" - protocol = "tcp" + direction = "in" + port = "443" + protocol = "tcp" source_ips = ["0.0.0.0/0", "::/0"] } - + rule { - direction = "in" - port = "8080" - protocol = "tcp" + direction = "in" + port = "8080" + protocol = "tcp" source_ips = ["0.0.0.0/0", "::/0"] } } # Create volume for home directory resource "hcloud_volume" "home_volume" { - name = local.volume_name - size = data.coder_parameter.volume_size.value - location = data.coder_parameter.location.value - format = "ext4" - + name = local.volume_name + size = data.coder_parameter.volume_size.value + location = data.coder_parameter.location.value + format = "ext4" + labels = { "coder.workspace" = data.coder_workspace.me.name "coder.owner" = data.coder_workspace_owner.me.name "coder.resource" = "home-volume" } - + # Protect the volume from being deleted due to changes in attributes lifecycle { ignore_changes = all @@ -297,30 +297,30 @@ resource "hcloud_volume" "home_volume" { # Create the server resource "hcloud_server" "workspace" { - count = data.coder_workspace.me.start_count - name = local.server_name - server_type = data.coder_parameter.server_type.value - image = local.final_image - location = data.coder_parameter.location.value - ssh_keys = var.ssh_key_id > 0 ? [var.ssh_key_id] : [] + count = data.coder_workspace.me.start_count + name = local.server_name + server_type = data.coder_parameter.server_type.value + image = local.final_image + location = data.coder_parameter.location.value + ssh_keys = var.ssh_key_id > 0 ? [var.ssh_key_id] : [] firewall_ids = [hcloud_firewall.workspace.id] - + labels = { "coder.workspace" = data.coder_workspace.me.name "coder.owner" = data.coder_workspace_owner.me.name "coder.resource" = "workspace-server" } - + public_net { ipv4_enabled = true ipv6_enabled = true } - + network { network_id = hcloud_network.workspace.id ip = "10.0.1.5" } - + user_data = templatefile("${path.module}/cloud-config.yaml.tftpl", { hostname = local.server_name username = lower(data.coder_workspace_owner.me.name) @@ -328,11 +328,11 @@ resource "hcloud_server" "workspace" { init_script = base64encode(coder_agent.main.init_script) coder_agent_token = coder_agent.main.token }) - + depends_on = [ hcloud_network_subnet.workspace ] - + # Proper lifecycle: server is destroyed when workspace stops, but volume persists lifecycle { ignore_changes = [ssh_keys, user_data] @@ -361,7 +361,7 @@ resource "coder_metadata" "workspace_info" { } item { key = "vcpus" - value = "${local.selected_server_type.vcpus}" + value = local.selected_server_type.vcpus } item { key = "memory" From 86e4448796bffd6900c4b36d39f742c1e0cbb12b Mon Sep 17 00:00:00 2001 From: Mido Date: Fri, 19 Sep 2025 09:22:59 +0200 Subject: [PATCH 14/21] extend typo hel = hel # Hetzner Helsinki short code --- .github/typos.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/typos.toml b/.github/typos.toml index 7a21014ad..aefbbfd43 100644 --- a/.github/typos.toml +++ b/.github/typos.toml @@ -9,6 +9,7 @@ melmathari = "melmathari" # Username fsn1 = "fsn1" # Hetzner Falkenstein datacenter code nbg1 = "nbg1" # Hetzner Nuremberg datacenter code hel1 = "hel1" # Hetzner Helsinki datacenter code +hel = "hel" # Hetzner Helsinki short code hcloud = "hcloud" # Hetzner Cloud CLI/API vcpus = "vcpus" # Virtual CPUs From 46d0744a8cb22fa50ccd0c8e9f0389d8bc606c5b Mon Sep 17 00:00:00 2001 From: Mido Date: Fri, 19 Sep 2025 12:36:19 +0200 Subject: [PATCH 15/21] registry/melmathari/.images/ --- registry/melmathari/.images/melmathari.jpeg | Bin 0 -> 41247 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 registry/melmathari/.images/melmathari.jpeg diff --git a/registry/melmathari/.images/melmathari.jpeg b/registry/melmathari/.images/melmathari.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..b50b558d4deb41adfd0f2b12ded3d6f21de4da0b GIT binary patch literal 41247 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 GKmXbAUl|+# literal 0 HcmV?d00001 From 722d6be7ad444459fcd8f8da1e0c424a34309cbf Mon Sep 17 00:00:00 2001 From: Mido Date: Mon, 22 Sep 2025 13:33:40 +0200 Subject: [PATCH 16/21] Remove unused image --- registry/melmathari/images/melmathari.jpeg | Bin 41247 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 registry/melmathari/images/melmathari.jpeg diff --git a/registry/melmathari/images/melmathari.jpeg b/registry/melmathari/images/melmathari.jpeg deleted file mode 100644 index b50b558d4deb41adfd0f2b12ded3d6f21de4da0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41247 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 GKmXbAUl|+# From 8a051bb68ddddfb67aab088c148cf3aaf3cba4d9 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 1 Oct 2025 18:16:48 -0500 Subject: [PATCH 17/21] fix: remove defaults from hcloud_token and ssh_key variables to allow default template to build --- registry/melmathari/templates/hetzner-cloud/main.tf | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/registry/melmathari/templates/hetzner-cloud/main.tf b/registry/melmathari/templates/hetzner-cloud/main.tf index a5b64f601..fa965c35c 100644 --- a/registry/melmathari/templates/hetzner-cloud/main.tf +++ b/registry/melmathari/templates/hetzner-cloud/main.tf @@ -16,12 +16,11 @@ variable "hcloud_token" { description = "Hetzner Cloud API token for authentication" type = string sensitive = true - default = "" } # Configure the Hetzner Cloud Provider provider "hcloud" { - token = var.hcloud_token != "" ? var.hcloud_token : null + token = var.hcloud_token } data "coder_workspace" "me" {} @@ -209,7 +208,6 @@ variable "ssh_key_id" { $ hcloud ssh-key list EOF sensitive = true - default = 0 validation { condition = var.ssh_key_id >= 0 From 210ee09cd8d61aba7d20437ff9ec3691f9ca0ae1 Mon Sep 17 00:00:00 2001 From: Mido Date: Thu, 2 Oct 2025 11:29:30 +0200 Subject: [PATCH 18/21] feat(hetzner-cloud): add config and format module --- .../hetzner-cloud/hetzner-config.json | 173 ++++++++++++++---- .../templates/hetzner-cloud/main.tf | 30 ++- 2 files changed, 151 insertions(+), 52 deletions(-) diff --git a/registry/melmathari/templates/hetzner-cloud/hetzner-config.json b/registry/melmathari/templates/hetzner-cloud/hetzner-config.json index 71f1e5e32..5bbd38624 100644 --- a/registry/melmathari/templates/hetzner-cloud/hetzner-config.json +++ b/registry/melmathari/templates/hetzner-cloud/hetzner-config.json @@ -6,29 +6,58 @@ "nbg1": { "name": "Nuremberg, Germany", "zone": "eu-central" }, "hel1": { "name": "Helsinki, Finland", "zone": "eu-central" }, "ash": { "name": "Ashburn, VA, USA", "zone": "us-east" }, - "hil": { "name": "Hillsboro, OR, USA", "zone": "us-west" } + "hil": { "name": "Hillsboro, OR, USA", "zone": "us-west" }, + "sin": { "name": "Singapore", "zone": "ap-southeast" } }, "server_types": { - "cx11": { "name": "CX11 (1 vCPU, 4 GB RAM)", "vcpus": 1, "memory": 4 }, - "cx21": { "name": "CX21 (2 vCPU, 8 GB RAM)", "vcpus": 2, "memory": 8 }, + "cax11": { + "name": "CAX11 (2 vCPU, 4 GB RAM, ARM)", + "vcpus": 2, + "memory": 4 + }, + "cax21": { + "name": "CAX21 (4 vCPU, 8 GB RAM, ARM)", + "vcpus": 4, + "memory": 8 + }, + "cax31": { + "name": "CAX31 (8 vCPU, 16 GB RAM, ARM)", + "vcpus": 8, + "memory": 16 + }, + "cax41": { + "name": "CAX41 (16 vCPU, 32 GB RAM, ARM)", + "vcpus": 16, + "memory": 32 + }, + "cpx11": { "name": "CPX11 (2 vCPU, 2 GB RAM)", "vcpus": 2, "memory": 2 }, + "cpx21": { "name": "CPX21 (3 vCPU, 4 GB RAM)", "vcpus": 3, "memory": 4 }, + "cpx31": { "name": "CPX31 (4 vCPU, 8 GB RAM)", "vcpus": 4, "memory": 8 }, + "cpx41": { + "name": "CPX41 (8 vCPU, 16 GB RAM)", + "vcpus": 8, + "memory": 16 + }, + "cpx51": { + "name": "CPX51 (16 vCPU, 32 GB RAM)", + "vcpus": 16, + "memory": 32 + }, "cx22": { "name": "CX22 (2 vCPU, 4 GB RAM, AMD)", "vcpus": 2, "memory": 4 }, - "cx31": { "name": "CX31 (2 vCPU, 8 GB RAM)", "vcpus": 2, "memory": 8 }, "cx32": { "name": "CX32 (4 vCPU, 8 GB RAM, AMD)", "vcpus": 4, "memory": 8 }, - "cx41": { "name": "CX41 (4 vCPU, 16 GB RAM)", "vcpus": 4, "memory": 16 }, "cx42": { "name": "CX42 (8 vCPU, 16 GB RAM, AMD)", "vcpus": 8, "memory": 16 }, - "cx51": { "name": "CX51 (8 vCPU, 32 GB RAM)", "vcpus": 8, "memory": 32 }, "cx52": { "name": "CX52 (16 vCPU, 32 GB RAM, AMD)", "vcpus": 16, @@ -63,31 +92,6 @@ "name": "CCX63 (48 vCPU, 192 GB RAM, Dedicated)", "vcpus": 48, "memory": 192 - }, - "cpx11": { - "name": "CPX11 (2 vCPU, 2 GB RAM, CPU-optimized)", - "vcpus": 2, - "memory": 2 - }, - "cpx21": { - "name": "CPX21 (3 vCPU, 4 GB RAM, CPU-optimized)", - "vcpus": 3, - "memory": 4 - }, - "cpx31": { - "name": "CPX31 (4 vCPU, 8 GB RAM, CPU-optimized)", - "vcpus": 4, - "memory": 8 - }, - "cpx41": { - "name": "CPX41 (8 vCPU, 16 GB RAM, CPU-optimized)", - "vcpus": 8, - "memory": 16 - }, - "cpx51": { - "name": "CPX51 (16 vCPU, 64 GB RAM, CPU-optimized)", - "vcpus": 16, - "memory": 64 } }, "images": { @@ -114,8 +118,109 @@ "alma-9": { "name": "AlmaLinux 9", "icon": "/icon/almalinux.svg" } } }, - "availability": { - "ccx63": ["fsn1", "nbg1"], - "*": ["fsn1", "nbg1", "hel1", "ash", "hil"] + "availability_by_location": { + "_comment": "Europe has ARM (CAX) + AMD (CX). USA/Asia only have Intel (CPX) + Dedicated (CCX).", + "fsn1": [ + "cax11", + "cax21", + "cax31", + "cax41", + "cpx11", + "cpx21", + "cpx31", + "cpx41", + "cpx51", + "cx22", + "cx32", + "cx42", + "cx52", + "ccx13", + "ccx23", + "ccx33", + "ccx43", + "ccx53", + "ccx63" + ], + "nbg1": [ + "cax11", + "cax21", + "cax31", + "cax41", + "cpx11", + "cpx21", + "cpx31", + "cpx41", + "cpx51", + "cx22", + "cx32", + "cx42", + "cx52", + "ccx13", + "ccx23", + "ccx33", + "ccx43", + "ccx53", + "ccx63" + ], + "hel1": [ + "cax11", + "cax21", + "cax31", + "cax41", + "cpx11", + "cpx21", + "cpx31", + "cpx41", + "cpx51", + "cx22", + "cx32", + "cx42", + "cx52", + "ccx13", + "ccx23", + "ccx33", + "ccx43", + "ccx53", + "ccx63" + ], + "ash": [ + "cpx11", + "cpx21", + "cpx31", + "cpx41", + "cpx51", + "ccx13", + "ccx23", + "ccx33", + "ccx43", + "ccx53", + "ccx63" + ], + "hil": [ + "cpx11", + "cpx21", + "cpx31", + "cpx41", + "cpx51", + "ccx13", + "ccx23", + "ccx33", + "ccx43", + "ccx53", + "ccx63" + ], + "sin": [ + "cpx11", + "cpx21", + "cpx31", + "cpx41", + "cpx51", + "ccx13", + "ccx23", + "ccx33", + "ccx43", + "ccx53", + "ccx63" + ] } } diff --git a/registry/melmathari/templates/hetzner-cloud/main.tf b/registry/melmathari/templates/hetzner-cloud/main.tf index fa965c35c..a9e5d0390 100644 --- a/registry/melmathari/templates/hetzner-cloud/main.tf +++ b/registry/melmathari/templates/hetzner-cloud/main.tf @@ -29,6 +29,14 @@ data "coder_workspace_owner" "me" {} # Load Hetzner Cloud configuration from JSON locals { hetzner_config = jsondecode(file("${path.module}/hetzner-config.json")) + + # Generate server type options filtered by selected location + server_type_options_for_selected_location = [ + for type_key in lookup(local.hetzner_config.availability_by_location, data.coder_parameter.location.value, []) : { + name = local.hetzner_config.type_meta.server_types[type_key].name + value = type_key + } + ] } # Hetzner Cloud locations parameter (dynamically generated from JSON) @@ -52,7 +60,7 @@ data "coder_parameter" "location" { } -# Hetzner Cloud server types parameter (dynamically generated from JSON) +# Hetzner Cloud server types parameter (dynamically filtered based on selected location) data "coder_parameter" "server_type" { name = "server_type" display_name = "Server Type" @@ -62,11 +70,12 @@ data "coder_parameter" "server_type" { icon = "/icon/memory.svg" mutable = false + # Filter server types based on the selected location dynamic "option" { - for_each = local.hetzner_config.type_meta.server_types + for_each = local.server_type_options_for_selected_location content { name = option.value.name - value = option.key + value = option.value.value } } } @@ -133,21 +142,6 @@ locals { selected_server_type = local.hetzner_config.type_meta.server_types[data.coder_parameter.server_type.value] selected_location = local.hetzner_config.type_meta.locations[data.coder_parameter.location.value] network_zone = local.selected_location.zone - - # Get availability for selected server type (use specific or wildcard) - server_availability = lookup(local.hetzner_config.availability, data.coder_parameter.server_type.value, local.hetzner_config.availability["*"]) - - # Validate server type is available in selected location - is_valid_combination = contains(local.server_availability, data.coder_parameter.location.value) -} - -# Validation check for server type and location compatibility -resource "null_resource" "validate_server_location" { - count = local.is_valid_combination ? 0 : 1 - - provisioner "local-exec" { - command = "echo 'ERROR: Server type ${data.coder_parameter.server_type.value} is not available in location ${data.coder_parameter.location.value}' && exit 1" - } } resource "coder_agent" "main" { From c287a81a54def00b0f566bf325f2b4aeff365dc0 Mon Sep 17 00:00:00 2001 From: Mido Date: Thu, 2 Oct 2025 12:35:17 +0200 Subject: [PATCH 19/21] ReadMe --- .../templates/hetzner-cloud/README.md | 102 +++++++++++++----- 1 file changed, 74 insertions(+), 28 deletions(-) diff --git a/registry/melmathari/templates/hetzner-cloud/README.md b/registry/melmathari/templates/hetzner-cloud/README.md index 8cf6902e5..091a3a544 100644 --- a/registry/melmathari/templates/hetzner-cloud/README.md +++ b/registry/melmathari/templates/hetzner-cloud/README.md @@ -13,7 +13,7 @@ Provision Hetzner Cloud servers as [Coder workspaces](https://coder.com/docs/wor This template provides a comprehensive Hetzner Cloud setup with: - **Dynamic Configuration**: Server types, locations, and images loaded from JSON -- **Smart Validation**: Prevents invalid server type/location combinations +- **Location-Aware Filtering**: Available server types automatically filter based on selected location - **Multiple Server Types**: Shared, dedicated, and CPU-optimized instances - **Global Locations**: Germany, Finland, and USA datacenters - **Persistent Storage**: Home volumes that survive workspace restarts @@ -72,19 +72,29 @@ This means that when the workspace restarts, any tools or files outside of the h ## Server Types -The template supports all major Hetzner Cloud server types: +The template supports current Hetzner Cloud server types: -### Shared vCPU (Cost-effective) +### ARM-based (Energy Efficient) -- **CX11**: 1 vCPU, 4 GB RAM -- **CX21**: 2 vCPU, 8 GB RAM -- **CX22**: 2 vCPU, 4 GB RAM (AMD) -- **CX31**: 2 vCPU, 8 GB RAM -- **CX32**: 4 vCPU, 8 GB RAM (AMD) -- **CX41**: 4 vCPU, 16 GB RAM -- **CX42**: 8 vCPU, 16 GB RAM (AMD) -- **CX51**: 8 vCPU, 32 GB RAM -- **CX52**: 16 vCPU, 32 GB RAM (AMD) +- **CAX11**: 2 vCPU, 4 GB RAM +- **CAX21**: 4 vCPU, 8 GB RAM +- **CAX31**: 8 vCPU, 16 GB RAM +- **CAX41**: 16 vCPU, 32 GB RAM + +### Shared AMD (Cost-effective) + +- **CX22**: 2 vCPU, 4 GB RAM +- **CX32**: 4 vCPU, 8 GB RAM +- **CX42**: 8 vCPU, 16 GB RAM +- **CX52**: 16 vCPU, 32 GB RAM + +### Intel CPU-Optimized + +- **CPX11**: 2 vCPU, 2 GB RAM +- **CPX21**: 3 vCPU, 4 GB RAM +- **CPX31**: 4 vCPU, 8 GB RAM +- **CPX41**: 8 vCPU, 16 GB RAM +- **CPX51**: 16 vCPU, 32 GB RAM ### Dedicated vCPU (High Performance) @@ -95,23 +105,16 @@ The template supports all major Hetzner Cloud server types: - **CCX53**: 32 vCPU, 128 GB RAM - **CCX63**: 48 vCPU, 192 GB RAM -### CPU-Optimized - -- **CPX11**: 2 vCPU, 2 GB RAM -- **CPX21**: 3 vCPU, 4 GB RAM -- **CPX31**: 4 vCPU, 8 GB RAM -- **CPX41**: 8 vCPU, 16 GB RAM -- **CPX51**: 16 vCPU, 64 GB RAM - ## Locations Available locations: -- **Falkenstein, Germany** (fsn1) - Primary location -- **Nuremberg, Germany** (nbg1) - Secondary location -- **Helsinki, Finland** (hel1) - EU Nordic +- **Falkenstein, Germany** (fsn1) - Europe +- **Nuremberg, Germany** (nbg1) - Europe +- **Helsinki, Finland** (hel1) - Europe - **Ashburn, Virginia, USA** (ash) - US East Coast - **Hillsboro, Oregon, USA** (hil) - US West Coast +- **Singapore** (sin) - Asia Pacific ## Supported Operating Systems @@ -141,7 +144,9 @@ The template uses `hetzner-config.json` for dynamic configuration: - **Server Types**: Add new server types with their specifications - **Locations**: Add new Hetzner datacenters as they become available - **Images**: Update with current Hetzner image names (verify with API) -- **Availability**: Map server type restrictions per location +- **Availability**: Map server type restrictions per location (only shown server types that are available) + +**How it works**: When a user selects a location, the template automatically filters the server type dropdown to only show instances available in that location. This prevents configuration errors by design. **Example**: Adding a new server type: @@ -149,6 +154,15 @@ The template uses `hetzner-config.json` for dynamic configuration: "cx62": { "name": "CX62 (16 vCPU, 64 GB RAM, AMD)", "vcpus": 16, "memory": 64 } ``` +If a server type has limited availability, add it to the `availability` section: + +```json +"availability": { + "ccx63": ["fsn1", "nbg1"], // Only available in these locations + "*": ["fsn1", "nbg1", "hel1", "ash", "hil"] // Default for all other types +} +``` + **Important**: Always verify image names match Hetzner's official names exactly to avoid provisioning errors. ### Optional Variables @@ -191,9 +205,9 @@ The template includes: ## Troubleshooting -### Invalid Server Type/Location Combination +### Server Type Options Change When Selecting Location -The template includes validation to prevent selecting server types that aren't available in certain locations. If you encounter this error, choose a different server type or location combination. +The template dynamically filters server types based on the selected location. For example, if you select a location where certain dedicated server types aren't available, those options won't appear in the server type dropdown. This prevents configuration errors before they happen. ### Image Not Found Errors @@ -239,13 +253,45 @@ If you can't connect to development servers: 2. Check that the private network is configured correctly 3. Ensure the server has a public IP address +## Local Testing + +To test this template locally before deployment: + +1. **Create a configuration file**: + + ```bash + cp terraform.tfvars.example terraform.tfvars + ``` + +2. **Add your Hetzner Cloud API token** to `terraform.tfvars`: + + ```hcl + hcloud_token = "your-actual-token-here" + ``` + +3. **Initialize and validate**: + + ```bash + terraform init + terraform validate + terraform plan + ``` + +4. **Test dynamic filtering**: Try planning with different locations to verify server types filter correctly: + ```bash + terraform plan -var="location=fsn1" # Should show CCX63 + terraform plan -var="location=ash" # Should NOT show CCX63 + ``` + +See `TEST_GUIDE.md` for detailed testing instructions. + ## Notes > [!NOTE] > This template is designed to be a starting point! Edit the Terraform configuration to extend the template to support your specific use case. > [!IMPORTANT] -> The SSH key in this template is a placeholder. In a production environment, you should replace it with your actual SSH public key or remove the SSH key resource entirely if not needed. +> The SSH key parameter defaults to 0 (no SSH key). To enable SSH access, set `ssh_key_id` to your actual SSH key ID from Hetzner Cloud. > [!WARNING] -> Some server types may not be available in all locations. The template includes validation to prevent invalid combinations, but availability can change over time. +> Server types are automatically filtered based on location availability. If you don't see a specific server type in the dropdown, it's not available in your selected location. From 43611c25e30c41e8e4a22dd4a28602b7c471fb88 Mon Sep 17 00:00:00 2001 From: Mido Date: Thu, 2 Oct 2025 12:38:28 +0200 Subject: [PATCH 20/21] ReadMe --- .../templates/hetzner-cloud/README.md | 64 +++++++------------ 1 file changed, 22 insertions(+), 42 deletions(-) diff --git a/registry/melmathari/templates/hetzner-cloud/README.md b/registry/melmathari/templates/hetzner-cloud/README.md index 091a3a544..77d60fafe 100644 --- a/registry/melmathari/templates/hetzner-cloud/README.md +++ b/registry/melmathari/templates/hetzner-cloud/README.md @@ -14,11 +14,11 @@ This template provides a comprehensive Hetzner Cloud setup with: - **Dynamic Configuration**: Server types, locations, and images loaded from JSON - **Location-Aware Filtering**: Available server types automatically filter based on selected location -- **Multiple Server Types**: Shared, dedicated, and CPU-optimized instances -- **Global Locations**: Germany, Finland, and USA datacenters +- **Multiple Server Types**: ARM, Intel, AMD shared, and dedicated instances +- **Global Locations**: Europe, USA, and Asia datacenters - **Persistent Storage**: Home volumes that survive workspace restarts - **Secure Networking**: Private networks with firewall rules -- **Clean Architecture**: Minimal JSON configuration for easy maintenance +- **Clean Architecture**: Region-based availability in JSON for easy maintenance ## Prerequisites @@ -72,38 +72,14 @@ This means that when the workspace restarts, any tools or files outside of the h ## Server Types -The template supports current Hetzner Cloud server types: +The template supports multiple Hetzner Cloud server types across four categories: -### ARM-based (Energy Efficient) +- **ARM-based (CAX)**: Energy-efficient ARM architecture instances +- **Intel CPU-Optimized (CPX)**: High-performance Intel processors +- **AMD Shared (CX)**: Cost-effective AMD shared instances +- **Dedicated vCPU (CCX)**: Dedicated CPU resources for consistent performance -- **CAX11**: 2 vCPU, 4 GB RAM -- **CAX21**: 4 vCPU, 8 GB RAM -- **CAX31**: 8 vCPU, 16 GB RAM -- **CAX41**: 16 vCPU, 32 GB RAM - -### Shared AMD (Cost-effective) - -- **CX22**: 2 vCPU, 4 GB RAM -- **CX32**: 4 vCPU, 8 GB RAM -- **CX42**: 8 vCPU, 16 GB RAM -- **CX52**: 16 vCPU, 32 GB RAM - -### Intel CPU-Optimized - -- **CPX11**: 2 vCPU, 2 GB RAM -- **CPX21**: 3 vCPU, 4 GB RAM -- **CPX31**: 4 vCPU, 8 GB RAM -- **CPX41**: 8 vCPU, 16 GB RAM -- **CPX51**: 16 vCPU, 32 GB RAM - -### Dedicated vCPU (High Performance) - -- **CCX13**: 2 vCPU, 8 GB RAM -- **CCX23**: 4 vCPU, 16 GB RAM -- **CCX33**: 8 vCPU, 32 GB RAM -- **CCX43**: 16 vCPU, 64 GB RAM -- **CCX53**: 32 vCPU, 128 GB RAM -- **CCX63**: 48 vCPU, 192 GB RAM +Server types are automatically filtered based on your selected location. The specific availability is managed in `hetzner-config.json`. ## Locations @@ -154,12 +130,13 @@ The template uses `hetzner-config.json` for dynamic configuration: "cx62": { "name": "CX62 (16 vCPU, 64 GB RAM, AMD)", "vcpus": 16, "memory": 64 } ``` -If a server type has limited availability, add it to the `availability` section: +The `availability_by_location` section maps which server types are available in each region: ```json -"availability": { - "ccx63": ["fsn1", "nbg1"], // Only available in these locations - "*": ["fsn1", "nbg1", "hel1", "ash", "hil"] // Default for all other types +"availability_by_location": { + "fsn1": ["cax11", "cpx11", "cx22", "ccx13", ...], // Europe: All types + "ash": ["cpx11", "ccx13", ...], // USA: Intel + Dedicated only + "sin": ["cpx11", "ccx13", ...] // Asia: Intel + Dedicated only } ``` @@ -207,7 +184,12 @@ The template includes: ### Server Type Options Change When Selecting Location -The template dynamically filters server types based on the selected location. For example, if you select a location where certain dedicated server types aren't available, those options won't appear in the server type dropdown. This prevents configuration errors before they happen. +This is expected behavior! The template dynamically filters server types based on regional availability: + +- **Europe (fsn1, nbg1, hel1)**: Shows all server types including ARM (CAX) and AMD (CX) +- **USA/Asia (ash, hil, sin)**: Shows only Intel (CPX) and Dedicated (CCX) servers + +This prevents configuration errors by only showing what's actually available in your selected region. ### Image Not Found Errors @@ -279,12 +261,10 @@ To test this template locally before deployment: 4. **Test dynamic filtering**: Try planning with different locations to verify server types filter correctly: ```bash - terraform plan -var="location=fsn1" # Should show CCX63 - terraform plan -var="location=ash" # Should NOT show CCX63 + terraform plan -var="location=fsn1" # Europe: Shows ARM (CAX), AMD (CX), Intel (CPX), Dedicated (CCX) + terraform plan -var="location=ash" # USA: Shows only Intel (CPX) and Dedicated (CCX) - no ARM/AMD ``` -See `TEST_GUIDE.md` for detailed testing instructions. - ## Notes > [!NOTE] From 52371aaeb963782774d25f8cf8bcbaff57efdc68 Mon Sep 17 00:00:00 2001 From: Mido Date: Thu, 2 Oct 2025 12:41:03 +0200 Subject: [PATCH 21/21] ReadMe --- .../templates/hetzner-cloud/README.md | 34 ++++++------------- 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/registry/melmathari/templates/hetzner-cloud/README.md b/registry/melmathari/templates/hetzner-cloud/README.md index 77d60fafe..b1e7e1fac 100644 --- a/registry/melmathari/templates/hetzner-cloud/README.md +++ b/registry/melmathari/templates/hetzner-cloud/README.md @@ -237,33 +237,19 @@ If you can't connect to development servers: ## Local Testing -To test this template locally before deployment: +To test this template locally, create a `terraform.tfvars` file with: -1. **Create a configuration file**: - - ```bash - cp terraform.tfvars.example terraform.tfvars - ``` - -2. **Add your Hetzner Cloud API token** to `terraform.tfvars`: - - ```hcl - hcloud_token = "your-actual-token-here" - ``` - -3. **Initialize and validate**: +```hcl +hcloud_token = "your-hetzner-cloud-api-token" +``` - ```bash - terraform init - terraform validate - terraform plan - ``` +Then run: -4. **Test dynamic filtering**: Try planning with different locations to verify server types filter correctly: - ```bash - terraform plan -var="location=fsn1" # Europe: Shows ARM (CAX), AMD (CX), Intel (CPX), Dedicated (CCX) - terraform plan -var="location=ash" # USA: Shows only Intel (CPX) and Dedicated (CCX) - no ARM/AMD - ``` +```bash +terraform init +terraform validate +terraform plan +``` ## Notes