Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ 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
hel = "hel" # Hetzner Helsinki short code
hcloud = "hcloud" # Hetzner Cloud CLI/API
vcpus = "vcpus" # Virtual CPUs

[files]
extend-exclude = ["registry/coder/templates/aws-devcontainer/architecture.svg"] #False positive
5 changes: 5 additions & 0 deletions .icons/hetzner.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added registry/melmathari/.images/melmathari.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions registry/melmathari/README.md
Original file line number Diff line number Diff line change
@@ -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: "[email protected]"
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.
263 changes: 263 additions & 0 deletions registry/melmathari/templates/hetzner-cloud/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
---
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
- **Location-Aware Filtering**: Available server types automatically filter based on selected location
- **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**: Region-based availability in JSON 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/<username>`, 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 multiple Hetzner Cloud server types across four categories:

- **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

Server types are automatically filtered based on your selected location. The specific availability is managed in `hetzner-config.json`.

## Locations

Available locations:

- **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

- 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 (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:

```json
"cx62": { "name": "CX62 (16 vCPU, 64 GB RAM, AMD)", "vcpus": 16, "memory": 64 }
```

The `availability_by_location` section maps which server types are available in each region:

```json
"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
}
```

**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

### Server Type Options Change When Selecting Location

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

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

## Local Testing

To test this template locally, create a `terraform.tfvars` file with:

```hcl
hcloud_token = "your-hetzner-cloud-api-token"
```

Then run:

```bash
terraform init
terraform validate
terraform plan
```

## 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 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]
> 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.
Original file line number Diff line number Diff line change
@@ -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
Loading