Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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: "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.
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
Loading