Skip to content

feat: add Hetzner Cloud server template and configuration files #254

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
wants to merge 1 commit into
base: main
Choose a base branch
from
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
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/brymut/.images/avatar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions registry/brymut/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
display_name: brymut (Bryan Mutai)
bio: Independent software developer based in Kenya, passionate about DevOps and Cloud Native solutions. Combining expert automation and scalability with a strong commitment to the community. Explore collaboration and drive success together.
avatar: ./.images/avatar.png
github: brymut
support_email: mutaiwork@gmail.com
status: community
---
28 changes: 28 additions & 0 deletions registry/brymut/templates/hetzner-linux/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
display_name: Hetzner Cloud Server
description: Provision Hetzner Cloud servers as Coder workspaces
icon: ../../../../.icons/hetzner.svg
maintainer_github: brymut
verified: false
tags: [vm, linux, hetzner]
---

# Remote Development on Hetzner Cloud (Linux)

Provision Hetzner Cloud servers as [Coder workspaces](https://coder.com/docs/workspaces) with this example template.

## Prerequisites

To deploy workspaces as Hetzner Cloud servers, you'll need:

- Hetzner Cloud [API token](https://console.hetzner.cloud/projects) (create under Security > API Tokens)

### Authentication

This template assumes that the Coder Provisioner is run in an environment that is authenticated with Hetzner Cloud.

Obtain a Hetzner Cloud API token from your [Hetzner Cloud Console](https://console.hetzner.cloud/projects) and provide it as the `hcloud_token` variable when creating a workspace.
For more authentication options, see the [Terraform provider documentation](https://registry.terraform.io/providers/hetznercloud/hcloud/latest/docs#authentication).

> **Note**
> This template is designed to be a starting point! Edit the Terraform to extend the template to support your use case.
46 changes: 46 additions & 0 deletions registry/brymut/templates/hetzner-linux/cloud-config.yaml.tftpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#cloud-config
users:
- name: ${username}
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
groups: sudo
shell: /bin/bash
packages:
- git
mounts:
- [
"LABEL=${home_volume_label}",
"/home/${username}",
auto,
"defaults,uid=1000,gid=1000",
]
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:
- chown ${username}:${username} /home/${username}
- systemctl enable coder-agent
- systemctl start coder-agent
191 changes: 191 additions & 0 deletions registry/brymut/templates/hetzner-linux/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
terraform {
required_providers {
hcloud = {
source = "hetznercloud/hcloud"
}
coder = {
source = "coder/coder"
}
}
}

variable "hcloud_token" {
sensitive = true
}

provider "hcloud" {
token = var.hcloud_token
}

data "coder_parameter" "hcloud_location" {
name = "hcloud_location"
display_name = "Hetzner Location"
description = "Select the Hetzner Cloud location for your workspace."
type = "string"
default = "fsn1"
option {
name = "DE Falkenstein"
value = "fsn1"
}
option {
name = "US Ashburn, VA"
value = "ash"
}
option {
name = "US Hillsboro, OR"
value = "hil"
}
option {
name = "SG Singapore"
value = "sin"
}
option {
name = "DE Nuremberg"
value = "nbg1"
}
option {
name = "FI Helsinki"
value = "hel1"
}
}

data "coder_parameter" "hcloud_server_type" {
name = "hcloud_server_type"
display_name = "Hetzner Server Type"
description = "Select the Hetzner Cloud server type for your workspace."
type = "string"
default = "cx22"
option {
name = "CX22 (2 vCPU, 4GB RAM, 40GB, $3.99/mo)"
value = "cx22"
}
option {
name = "CPX11 (2 vCPU, 2GB RAM, 40GB, $4.49/mo)"
value = "cpx11"
}
option {
name = "CX32 (4 vCPU, 8GB RAM, 80GB, $6.99/mo)"
value = "cx32"
}
option {
name = "CPX21 (3 vCPU, 4GB RAM, 80GB, $7.99/mo)"
value = "cpx21"
}
option {
name = "CPX31 (4 vCPU, 8GB RAM, 160GB, $14.99/mo)"
value = "cpx31"
}
option {
name = "CX42 (8 vCPU, 16GB RAM, 160GB, $17.99/mo)"
value = "cx42"
}
option {
name = "CPX41 (8 vCPU, 16GB RAM, 240GB, $27.49/mo)"
value = "cpx41"
}
option {
name = "CX52 (16 vCPU, 32GB RAM, 320GB, $35.49/mo)"
value = "cx52"
}
option {
name = "CPX51 (16 vCPU, 32GB RAM, 360GB, $60.49/mo)"
value = "cpx51"
}
}

resource "hcloud_server" "dev" {
name = "dev"
image = "ubuntu-24.04"
server_type = data.coder_parameter.hcloud_server_type.value
location = data.coder_parameter.hcloud_location.value
public_net {
ipv4_enabled = true
ipv6_enabled = true
}
user_data = templatefile("cloud-config.yaml.tftpl", {
username = lower(data.coder_workspace_owner.me.name)
home_volume_label = hcloud_volume.home_volume.name
init_script = base64encode(coder_agent.main.init_script)
coder_agent_token = coder_agent.main.token
})
}

resource "hcloud_volume" "home_volume" {
name = "coder-${data.coder_workspace.me.id}-home"
size = data.coder_parameter.home_volume_size.value
location = data.coder_parameter.hcloud_location.value
format = "ext4"
delete_protection = true
}

resource "hcloud_volume_attachment" "home_volume_attachment" {
volume_id = hcloud_volume.home_volume.id
server_id = hcloud_server.dev.id
}

locals {
username = data.coder_workspace_owner.me.name
}

data "coder_provisioner" "me" {
}

provider "coder" {
}

data "coder_workspace" "me" {
}

data "coder_workspace_owner" "me" {
}

data "coder_parameter" "home_volume_size" {
name = "home_volume_size"
display_name = "Home volume size"
description = "How large would you like your home volume to be (in GB)?"
type = "number"
default = "20"
mutable = false
validation {
min = 1
max = 100 # Adjust the max size as needed
}
}

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)}"
}
}

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
}