-
Notifications
You must be signed in to change notification settings - Fork 72
feat: add Hetzner Cloud server template #426
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
melmathari
wants to merge
21
commits into
coder:main
Choose a base branch
from
melmathari:209
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+957
−0
Open
Changes from 5 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
ff686e3
Add Hetzner Cloud
melmathari 0cbe283
prettifier
melmathari 356221d
typos
melmathari a0adec3
jq syntax
melmathari 0798d81
server config updated
melmathari e12e792
Update registry/melmathari/templates/hetzner-cloud/README.md
melmathari 9ab8f47
Update registry/melmathari/templates/hetzner-cloud/README.md
melmathari 8cd33d2
Update registry/melmathari/templates/hetzner-cloud/README.md
melmathari 6b520c5
Update registry/melmathari/templates/hetzner-cloud/README.md
melmathari 43a0718
Update registry/melmathari/templates/hetzner-cloud/README.md
melmathari de9c9c0
README prettier formatting
melmathari 035b8bf
Hetzner Config prettier
melmathari f394d7f
terraform fmt on main.tf
melmathari 86e4448
extend typo hel = hel # Hetzner Helsinki short code
melmathari 46d0744
registry/melmathari/.images/
melmathari 722d6be
Remove unused image
melmathari 8a051bb
fix: remove defaults from hcloud_token and ssh_key variables to allow…
DevelopmentCats 210ee09
feat(hetzner-cloud): add config and format module
melmathari c287a81
ReadMe
melmathari 43611c2
ReadMe
melmathari 52371aa
ReadMe
melmathari File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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/<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 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) | ||
melmathari marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
- **CX31**: 2 vCPU, 8 GB RAM | ||
- **CX32**: 2 vCPU, 8 GB RAM (AMD) | ||
melmathari marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
- **CX41**: 4 vCPU, 16 GB RAM | ||
- **CX42**: 4 vCPU, 16 GB RAM (AMD) | ||
melmathari marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
- **CX51**: 8 vCPU, 32 GB RAM | ||
- **CX52**: 8 vCPU, 32 GB RAM (AMD) | ||
melmathari marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
### 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 | ||
melmathari marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
- **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. |
57 changes: 57 additions & 0 deletions
57
registry/melmathari/templates/hetzner-cloud/cloud-config.yaml.tftpl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.