diff --git a/.icons/hetzner-cloud.svg b/.icons/hetzner-cloud.svg
new file mode 100644
index 000000000..8012f6eee
--- /dev/null
+++ b/.icons/hetzner-cloud.svg
@@ -0,0 +1,4 @@
+
diff --git a/registry/coder/templates/hetzner-cloud/README.md b/registry/coder/templates/hetzner-cloud/README.md
new file mode 100644
index 000000000..7737d9cae
--- /dev/null
+++ b/registry/coder/templates/hetzner-cloud/README.md
@@ -0,0 +1,82 @@
+---
+name: Hetzner Cloud Linux Workspace
+description: Provision a Hetzner Cloud server with private networking and a persistent home volume for Coder workspaces.
+tags: [hetzner, terraform, linux, coder]
+icon: /icon/hetzner-cloud.svg
+---
+
+# Hetzner Cloud Linux Workspace
+
+This template provisions a single Hetzner Cloud server optimised for Coder. It creates a dedicated private network, attaches
+a persistent volume for the workspace home directory, and boots the machine with a cloud-init configuration that installs and
+starts the Coder agent automatically.
+
+## Features
+
+- Choice of popular Ubuntu, Debian, Fedora, and Rocky Linux images
+- Selectable CPX and CAX instance sizes, including x86 and ARM options
+- Private network and firewall pre-configured for secure access
+- Persistent ext4 home volume mounted at `/home/`
+- Optional code-server and JetBrains module integrations
+
+## Requirements
+
+- Hetzner Cloud project with API access enabled
+- Hetzner Cloud API token (`HCLOUD_TOKEN`) with permission to create servers, networks, firewalls, and volumes
+- Coder v2.9+ (tested with Terraform >= 1.4)
+
+## Usage
+
+1. Export your Hetzner Cloud token before starting `coderd`:
+ ```bash
+ export HCLOUD_TOKEN=""
+ ```
+2. Import this template into your Coder workspace namespace (see [Coder template docs](https://coder.com/docs/templates/overview)).
+3. When creating a workspace pick the desired location, server type, and image from the parameters sidebar.
+4. Launch the workspace – the agent will come online automatically and the persistent volume will mount to the home directory.
+
+## Variables
+
+| Name | Description | Type | Default | Required |
+|------------------|---------------------------------------------------|----------|----------------|----------|
+| `hcloud_token` | Overrides the HCLOUD_TOKEN environment variable | `string` | `""` | no |
+
+### Workspace Parameters
+
+| Parameter | Description |
+|-------------------------|------------------------------------------------|
+| `Hetzner location` | Target data centre (nbg1, fsn1, hel1, ash, hil) |
+| `Server type` | CPX/CAX instance family |
+| `Server image` | Linux distribution image |
+| `Home volume size` | Persistent volume size in GiB (10 – 1024) |
+| `Private network CIDR` | CIDR for the created private network |
+| `Subnet CIDR` | CIDR for the workspace subnet |
+
+## Resources Created
+
+- `hcloud_network` and `hcloud_network_subnet` for workspace isolation
+- `hcloud_firewall` allowing SSH/HTTP/HTTPS ingress and full egress
+- `hcloud_volume` formatted as ext4 and attached to the workspace server
+- `hcloud_server` with user-data to start the Coder agent
+- Optional `code-server` and `jetbrains` Coder modules for IDE support
+
+## Customisation
+
+- Adjust the default network ranges in the parameter definitions if they conflict with existing infrastructure.
+- Update the `startup_script` in `coder_agent.main` to install language runtimes or tooling specific to your team.
+- Add extra firewall rules or attach additional volumes as needed.
+
+## Troubleshooting
+
+### Workspace fails to start and reports unreachable agent
+- Verify that the HCLOUD_TOKEN exported for `coderd` has `Read & Write` permissions.
+- Check the Hetzner Cloud console for the server logs – ensure the agent service is running (`systemctl status coder-agent`).
+
+### Volume not mounted on first boot
+- Hetzner volumes can take a few seconds to attach. Restarting the instance or re-running `systemctl start coder-agent`
+ after attachment will complete the mount.
+
+## Contributing
+
+Improvements and additional server options are welcome! Please read the [contributing guidelines](../../../../CONTRIBUTING.md)
+before submitting a pull request.
diff --git a/registry/coder/templates/hetzner-cloud/cloud-config.yaml.tftpl b/registry/coder/templates/hetzner-cloud/cloud-config.yaml.tftpl
new file mode 100644
index 000000000..d42e35331
--- /dev/null
+++ b/registry/coder/templates/hetzner-cloud/cloud-config.yaml.tftpl
@@ -0,0 +1,56 @@
+#cloud-config
+users:
+ - name: ${username}
+ groups: sudo
+ sudo: ["ALL=(ALL) NOPASSWD:ALL"]
+ shell: /bin/bash
+packages:
+ - git
+fs_setup:
+ - label: ${home_volume_label}
+ filesystem: ext4
+ device: ${volume_device}
+ partition: auto
+ overwrite: false
+mounts:
+ - [
+ "/dev/disk/by-label/${home_volume_label}",
+ "/home/${username}",
+ ext4,
+ "defaults,nofail,discard",
+ "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=-900
+ SyslogIdentifier=coder-agent
+
+ [Install]
+ WantedBy=multi-user.target
+runcmd:
+ - usermod -aG sudo ${username}
+ - chown ${username}:${username} /home/${username}
+ - systemctl daemon-reload
+ - systemctl enable coder-agent
+ - systemctl start coder-agent
diff --git a/registry/coder/templates/hetzner-cloud/main.tf b/registry/coder/templates/hetzner-cloud/main.tf
new file mode 100644
index 000000000..c7d9a96e9
--- /dev/null
+++ b/registry/coder/templates/hetzner-cloud/main.tf
@@ -0,0 +1,407 @@
+terraform {
+ required_version = ">= 1.4.0"
+
+ required_providers {
+ coder = {
+ source = "coder/coder"
+ }
+ hcloud = {
+ source = "hetznercloud/hcloud"
+ }
+ }
+}
+
+provider "coder" {}
+
+variable "hcloud_token" {
+ type = string
+ default = ""
+ sensitive = true
+ description = <<-EOF
+ Hetzner Cloud API token. It is recommended to supply this via the HCLOUD_TOKEN
+ environment variable when starting coderd instead of setting the variable directly.
+ EOF
+}
+
+provider "hcloud" {
+ token = var.hcloud_token != "" ? var.hcloud_token : null
+}
+
+locals {
+ owner_name = lower(replace(data.coder_workspace_owner.me.name, "[^a-zA-Z0-9-]", "-"))
+ workspace_name = lower(replace(data.coder_workspace.me.name, "[^a-zA-Z0-9-]", "-"))
+ server_name = substr("coder-${local.owner_name}-${local.workspace_name}", 0, 63)
+ username = local.owner_name != "" ? local.owner_name : "coder"
+ home_volume_label = "coder-home"
+ network_zones = {
+ nbg1 = "eu-central"
+ fsn1 = "eu-central"
+ hel1 = "eu-central"
+ ash = "us-east"
+ hil = "us-west"
+ }
+ network_zone = lookup(local.network_zones, data.coder_parameter.location.value, "eu-central")
+}
+
+# Workspace parameters exposed in the Coder UI
+
+data "coder_parameter" "location" {
+ name = "hcloud_location"
+ display_name = "Hetzner location"
+ description = "Region where the server will be created."
+ default = "nbg1"
+ mutable = false
+ option {
+ name = "Germany (Nuremberg)"
+ value = "nbg1"
+ icon = "/emojis/1f1e9-1f1ea.png"
+ }
+ option {
+ name = "Germany (Falkenstein)"
+ value = "fsn1"
+ icon = "/emojis/1f1e9-1f1ea.png"
+ }
+ option {
+ name = "Finland (Helsinki)"
+ value = "hel1"
+ icon = "/emojis/1f1eb-1f1ee.png"
+ }
+ option {
+ name = "United States (Ashburn)"
+ value = "ash"
+ icon = "/emojis/1f1fa-1f1f8.png"
+ }
+ option {
+ name = "United States (Hillsboro)"
+ value = "hil"
+ icon = "/emojis/1f1fa-1f1f8.png"
+ }
+}
+
+data "coder_parameter" "server_type" {
+ name = "hcloud_server_type"
+ display_name = "Server type"
+ description = "Hetzner instance size. Prices are per hour."
+ default = "cpx21"
+ mutable = false
+ option {
+ name = "CPX11 – 2 vCPU, 2 GB RAM"
+ value = "cpx11"
+ }
+ option {
+ name = "CPX21 – 3 vCPU, 4 GB RAM"
+ value = "cpx21"
+ }
+ option {
+ name = "CPX31 – 4 vCPU, 8 GB RAM"
+ value = "cpx31"
+ }
+ option {
+ name = "CPX41 – 8 vCPU, 16 GB RAM"
+ value = "cpx41"
+ }
+ option {
+ name = "CAX11 (ARM) – 2 vCPU, 2 GB RAM"
+ value = "cax11"
+ }
+ option {
+ name = "CAX21 (ARM) – 4 vCPU, 8 GB RAM"
+ value = "cax21"
+ }
+}
+
+data "coder_parameter" "image" {
+ name = "hcloud_image"
+ display_name = "Server image"
+ description = "Operating system image for the workspace."
+ default = "ubuntu-22.04"
+ mutable = false
+ option {
+ name = "Ubuntu 24.04 LTS"
+ value = "ubuntu-24.04"
+ icon = "/icon/ubuntu.svg"
+ }
+ option {
+ name = "Ubuntu 22.04 LTS"
+ value = "ubuntu-22.04"
+ icon = "/icon/ubuntu.svg"
+ }
+ option {
+ name = "Debian 12"
+ value = "debian-12"
+ icon = "/icon/debian.svg"
+ }
+ option {
+ name = "Fedora 40"
+ value = "fedora-40"
+ icon = "/icon/fedora.svg"
+ }
+ option {
+ name = "Rocky Linux 9"
+ value = "rocky-9"
+ icon = "/icon/rockylinux.svg"
+ }
+}
+
+data "coder_parameter" "volume_size" {
+ name = "home_volume_size"
+ display_name = "Home volume size"
+ description = "Size of the persistent home volume (GiB)."
+ type = "number"
+ default = "50"
+ mutable = false
+ validation {
+ min = 10
+ max = 1024
+ }
+}
+
+data "coder_parameter" "network_cidr" {
+ name = "network_cidr"
+ display_name = "Private network CIDR"
+ description = "CIDR block for the workspace private network."
+ default = "10.20.0.0/16"
+ mutable = false
+}
+
+data "coder_parameter" "subnet_cidr" {
+ name = "subnet_cidr"
+ display_name = "Subnet CIDR"
+ description = "Subnet used for the workspace server. Must be within the private network CIDR."
+ default = "10.20.1.0/24"
+ mutable = false
+}
+
+# Coder workspace metadata
+
+data "coder_workspace" "me" {}
+data "coder_workspace_owner" "me" {}
+
+resource "coder_agent" "main" {
+ arch = "amd64"
+ os = "linux"
+
+ startup_script = <<-EOT
+ set -e
+
+ if [ ! -f ~/.init_done ]; then
+ cp -rT /etc/skel ~
+ touch ~/.init_done
+ fi
+
+ # Install basic packages used in most development workflows
+ if command -v apt >/dev/null 2>&1; then
+ sudo apt-get update -y && sudo apt-get install -y build-essential curl git
+ fi
+ EOT
+
+ env = {
+ GIT_AUTHOR_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
+ GIT_AUTHOR_EMAIL = data.coder_workspace_owner.me.email
+ GIT_COMMITTER_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
+ GIT_COMMITTER_EMAIL = data.coder_workspace_owner.me.email
+ }
+
+ 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 Disk"
+ interval = 600
+ timeout = 30
+ script = "coder stat disk --path /home/${local.username}"
+ }
+
+ display_apps {
+ vscode = true
+ vscode_insiders = false
+ web_terminal = true
+ port_forwarding_helper = true
+ }
+}
+
+module "code_server" {
+ count = data.coder_workspace.me.start_count
+ source = "registry.coder.com/modules/coder/code-server/coder"
+ version = "~> 1.0"
+
+ agent_id = coder_agent.main.id
+ agent_name = "main"
+ order = 1
+}
+
+module "jetbrains" {
+ count = data.coder_workspace.me.start_count
+ source = "registry.coder.com/modules/coder/jetbrains/coder"
+ version = "~> 1.0"
+ agent_id = coder_agent.main.id
+ agent_name = "main"
+ folder = "/home/${local.username}"
+}
+
+resource "hcloud_network" "workspace" {
+ name = "coder-${data.coder_workspace.me.id}-network"
+ ip_range = data.coder_parameter.network_cidr.value
+ labels = {
+ "coder.workspace_id" = data.coder_workspace.me.id
+ "coder.workspace_name" = data.coder_workspace.me.name
+ }
+}
+
+resource "hcloud_network_subnet" "workspace" {
+ network_id = hcloud_network.workspace.id
+ type = "cloud"
+ network_zone = local.network_zone
+ ip_range = data.coder_parameter.subnet_cidr.value
+}
+
+resource "hcloud_firewall" "workspace" {
+ name = "coder-${data.coder_workspace.me.id}-firewall"
+
+ rule {
+ direction = "in"
+ description = "Allow SSH"
+ protocol = "tcp"
+ port = "22"
+ source_ips = ["0.0.0.0/0", "::/0"]
+ }
+
+ rule {
+ direction = "in"
+ description = "Allow HTTPS"
+ protocol = "tcp"
+ port = "443"
+ source_ips = ["0.0.0.0/0", "::/0"]
+ }
+
+ rule {
+ direction = "in"
+ description = "Allow HTTP"
+ protocol = "tcp"
+ port = "80"
+ source_ips = ["0.0.0.0/0", "::/0"]
+ }
+
+ rule {
+ direction = "out"
+ description = "Allow all TCP egress"
+ protocol = "tcp"
+ port = "0-65535"
+ destination_ips = ["0.0.0.0/0", "::/0"]
+ }
+
+ rule {
+ direction = "out"
+ description = "Allow all UDP egress"
+ protocol = "udp"
+ port = "0-65535"
+ destination_ips = ["0.0.0.0/0", "::/0"]
+ }
+}
+
+resource "hcloud_volume" "home_volume" {
+ name = "coder-${data.coder_workspace.me.id}-home"
+ size = data.coder_parameter.volume_size.value
+ location = data.coder_parameter.location.value
+ format = "ext4"
+ automount = false
+ labels = {
+ "coder.workspace_id" = data.coder_workspace.me.id
+ "coder.workspace_name" = data.coder_workspace.me.name
+ "coder.owner" = data.coder_workspace_owner.me.name
+ }
+}
+
+resource "hcloud_server" "workspace" {
+ count = data.coder_workspace.me.start_count
+ name = local.server_name
+ image = data.coder_parameter.image.value
+ server_type = data.coder_parameter.server_type.value
+ location = data.coder_parameter.location.value
+ firewall_ids = [hcloud_firewall.workspace.id]
+ volume_ids = [hcloud_volume.home_volume.id]
+
+ public_net {
+ enable_ipv4 = true
+ enable_ipv6 = true
+ }
+
+ network {
+ network_id = hcloud_network.workspace.id
+ ip = cidrhost(data.coder_parameter.subnet_cidr.value, 10)
+ }
+
+ user_data = templatefile("${path.module}/cloud-config.yaml.tftpl", {
+ username = local.username
+ home_volume_label = local.home_volume_label
+ volume_device = hcloud_volume.home_volume.linux_device
+ init_script = base64encode(coder_agent.main.init_script)
+ coder_agent_token = coder_agent.main.token
+ })
+
+ labels = {
+ "coder.workspace_id" = data.coder_workspace.me.id
+ "coder.workspace_name" = data.coder_workspace.me.name
+ "coder.owner" = data.coder_workspace_owner.me.name
+ }
+
+ depends_on = [
+ hcloud_network_subnet.workspace
+ ]
+}
+
+resource "coder_metadata" "server" {
+ count = length(hcloud_server.workspace) > 0 ? 1 : 0
+ resource_id = hcloud_server.workspace[0].id
+
+ item {
+ key = "location"
+ value = data.coder_parameter.location.value
+ }
+
+ item {
+ key = "server_type"
+ value = data.coder_parameter.server_type.value
+ }
+
+ item {
+ key = "image"
+ value = data.coder_parameter.image.value
+ }
+}
+
+resource "coder_metadata" "volume" {
+ resource_id = hcloud_volume.home_volume.id
+
+ item {
+ key = "size"
+ value = "${hcloud_volume.home_volume.size} GiB"
+ }
+}
+
+resource "coder_metadata" "network" {
+ resource_id = hcloud_network.workspace.id
+
+ item {
+ key = "cidr"
+ value = data.coder_parameter.network_cidr.value
+ }
+ item {
+ key = "subnet"
+ value = data.coder_parameter.subnet_cidr.value
+ }
+}