Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit bf9449e

Browse files
committed
feat: add Hetzner Cloud server template and configuration files
1 parent 08ed594 commit bf9449e

File tree

8 files changed

+307
-0
lines changed

8 files changed

+307
-0
lines changed

‎.github/typos.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
muc = "muc" # For Munich location code
33
Hashi = "Hashi"
44
HashiCorp = "HashiCorp"
5+
hel = "hel" # For Helsinki location code
56

67
[files]
78
extend-exclude = ["registry/coder/templates/aws-devcontainer/architecture.svg"] #False positive

‎.icons/hetzner.svg

Lines changed: 5 additions & 0 deletions
Loading[フレーム]

‎registry/brymut/.images/avatar.png

30.6 KB
Loading[フレーム]

‎registry/brymut/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
display_name: brymut (Bryan Mutai)
3+
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.
4+
avatar: ./.images/avatar.png
5+
github: brymut
6+
support_email: mutaiwork@gmail.com
7+
status: community
8+
---
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
display_name: Hetzner Cloud Server
3+
description: Provision Hetzner Cloud servers as Coder workspaces
4+
icon: ../../../../.icons/hetzner.svg
5+
tags: [vm, linux, hetzner]
6+
---
7+
8+
# Remote Development on Hetzner Cloud (Linux)
9+
10+
Provision Hetzner Cloud servers as [Coder workspaces](https://coder.com/docs/workspaces) with this example template.
11+
12+
## Prerequisites
13+
14+
To deploy workspaces as Hetzner Cloud servers, you'll need:
15+
16+
- Hetzner Cloud [API token](https://console.hetzner.cloud/projects) (create under Security > API Tokens)
17+
18+
### Authentication
19+
20+
This template assumes that the Coder Provisioner is run in an environment that is authenticated with Hetzner Cloud.
21+
22+
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.
23+
For more authentication options, see the [Terraform provider documentation](https://registry.terraform.io/providers/hetznercloud/hcloud/latest/docs#authentication).
24+
25+
> [!NOTE]
26+
> This template is designed to be a starting point. Edit the Terraform to extend the template to support your use case.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#cloud-config
2+
users:
3+
- name: ${username}
4+
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
5+
groups: sudo
6+
shell: /bin/bash
7+
packages:
8+
- git
9+
%{ if home_volume_label != "" ~}
10+
fs_setup:
11+
- device: /dev/disk/by-id/scsi-0HC_Volume_${volume_id}
12+
filesystem: ext4
13+
label: ${home_volume_label}
14+
overwrite: false # This prevents reformatting the disk on every boot
15+
16+
mounts:
17+
- [
18+
"LABEL=${home_volume_label}",
19+
"/home/${username}",
20+
auto,
21+
"defaults,uid=1000,gid=1000",
22+
]
23+
%{ endif ~}
24+
write_files:
25+
- path: /opt/coder/init
26+
permissions: "0755"
27+
encoding: b64
28+
content: ${init_script}
29+
- path: /etc/systemd/system/coder-agent.service
30+
permissions: "0644"
31+
content: |
32+
[Unit]
33+
Description=Coder Agent
34+
After=network-online.target
35+
Wants=network-online.target
36+
37+
[Service]
38+
User=${username}
39+
ExecStart=/opt/coder/init
40+
Environment=CODER_AGENT_TOKEN=${coder_agent_token}
41+
Restart=always
42+
RestartSec=10
43+
TimeoutStopSec=90
44+
KillMode=process
45+
46+
OOMScoreAdjust=-900
47+
SyslogIdentifier=coder-agent
48+
49+
[Install]
50+
WantedBy=multi-user.target
51+
runcmd:
52+
- mount -a
53+
- chown ${username}:${username} /home/${username}
54+
- systemctl enable coder-agent
55+
- systemctl start coder-agent
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"type_meta": {
3+
"cx22": { "cores": 2, "memory_gb": 4, "disk_gb": 40 },
4+
"cx32": { "cores": 4, "memory_gb": 8, "disk_gb": 80 },
5+
"cx42": { "cores": 8, "memory_gb": 16, "disk_gb": 160 },
6+
"cx52": { "cores": 16, "memory_gb": 32, "disk_gb": 320 },
7+
"cpx11": { "cores": 2, "memory_gb": 2, "disk_gb": 40 },
8+
"cpx21": { "cores": 3, "memory_gb": 4, "disk_gb": 80 },
9+
"cpx31": { "cores": 4, "memory_gb": 8, "disk_gb": 160 },
10+
"cpx41": { "cores": 8, "memory_gb": 16, "disk_gb": 240 },
11+
"cpx51": { "cores": 16, "memory_gb": 32, "disk_gb": 360 },
12+
"ccx13": { "cores": 2, "memory_gb": 8, "disk_gb": 80 },
13+
"ccx23": { "cores": 4, "memory_gb": 16, "disk_gb": 160 },
14+
"ccx33": { "cores": 8, "memory_gb": 32, "disk_gb": 240 },
15+
"ccx43": { "cores": 16, "memory_gb": 64, "disk_gb": 360 },
16+
"ccx53": { "cores": 32, "memory_gb": 128, "disk_gb": 600 },
17+
"ccx63": { "cores": 48, "memory_gb": 192, "disk_gb": 960 }
18+
},
19+
"availability": {
20+
"fsn1": ["cpx11", "cpx21", "cpx31", "cpx41", "cpx51", "ccx13", "ccx23", "ccx33"],
21+
"ash": ["cpx11", "cpx21", "cpx31", "cpx41", "cpx51", "ccx13", "ccx23", "ccx33"],
22+
"hel1": ["cx22", "cpx11", "cpx21", "cpx31", "cpx41", "cpx51", "ccx13", "ccx23", "ccx33"],
23+
"hil": ["cpx11", "cpx21", "cpx31", "cpx41", "ccx13", "ccx23", "ccx33"],
24+
"nbg1": ["cx22", "cx32", "cx42", "cx52", "cpx11", "cpx21", "cpx31", "cpx41", "cpx51", "ccx13", "ccx23", "ccx33"],
25+
"sin": ["cpx11", "cpx21", "cpx31", "cpx41", "cpx51", "ccx13", "ccx23", "ccx33"]
26+
}
27+
}
28+
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
terraform {
2+
required_providers {
3+
hcloud = {
4+
source = "hetznercloud/hcloud"
5+
}
6+
coder = {
7+
source = "coder/coder"
8+
}
9+
}
10+
}
11+
12+
variable "hcloud_token" {
13+
sensitive = true
14+
}
15+
16+
provider "hcloud" {
17+
token = var.hcloud_token
18+
}
19+
20+
# Available locations: https://docs.hetzner.com/cloud/general/locations/
21+
data "coder_parameter" "hcloud_location" {
22+
name = "hcloud_location"
23+
display_name = "Hetzner Location"
24+
description = "Select the Hetzner Cloud location for your workspace."
25+
type = "string"
26+
default = "fsn1"
27+
option {
28+
name = "DE Falkenstein"
29+
value = "fsn1"
30+
}
31+
option {
32+
name = "US Ashburn, VA"
33+
value = "ash"
34+
}
35+
option {
36+
name = "US Hillsboro, OR"
37+
value = "hil"
38+
}
39+
option {
40+
name = "SG Singapore"
41+
value = "sin"
42+
}
43+
option {
44+
name = "DE Nuremberg"
45+
value = "nbg1"
46+
}
47+
option {
48+
name = "FI Helsinki"
49+
value = "hel1"
50+
}
51+
}
52+
53+
# Available server types: https://docs.hetzner.com/cloud/servers/overview/
54+
data "coder_parameter" "hcloud_server_type" {
55+
name = "hcloud_server_type"
56+
display_name = "Hetzner Server Type"
57+
description = "Select the Hetzner Cloud server type for your workspace."
58+
type = "string"
59+
60+
dynamic "option" {
61+
for_each = local.hcloud_server_type_options_for_selected_location
62+
content {
63+
name = option.value.name
64+
value = option.value.value
65+
}
66+
}
67+
}
68+
69+
resource "hcloud_server" "dev" {
70+
count = data.coder_workspace.me.start_count
71+
name = "coder-${data.coder_workspace.me.name}-dev"
72+
image = "ubuntu-24.04"
73+
server_type = data.coder_parameter.hcloud_server_type.value
74+
location = data.coder_parameter.hcloud_location.value
75+
public_net {
76+
ipv4_enabled = true
77+
ipv6_enabled = true
78+
}
79+
user_data = templatefile("cloud-config.yaml.tftpl", {
80+
username = lower(data.coder_workspace_owner.me.name)
81+
home_volume_label = "coder-${data.coder_workspace.me.id}-home"
82+
volume_id = hcloud_volume.home_volume[count.index].id
83+
init_script = base64encode(coder_agent.main.init_script)
84+
coder_agent_token = coder_agent.main.token
85+
})
86+
labels = {
87+
"coder_workspace_name" = data.coder_workspace.me.name,
88+
"coder_workspace_owner" = data.coder_workspace_owner.me.name,
89+
}
90+
}
91+
92+
resource "hcloud_volume" "home_volume" {
93+
count = data.coder_workspace.me.start_count
94+
name = "coder-${data.coder_workspace.me.id}-home"
95+
size = data.coder_parameter.home_volume_size.value
96+
location = data.coder_parameter.hcloud_location.value
97+
labels = {
98+
"coder_workspace_name" = data.coder_workspace.me.name,
99+
"coder_workspace_owner" = data.coder_workspace_owner.me.name,
100+
}
101+
}
102+
103+
resource "hcloud_volume_attachment" "home_volume_attachment" {
104+
count = data.coder_workspace.me.start_count
105+
volume_id = hcloud_volume.home_volume[count.index].id
106+
server_id = hcloud_server.dev[count.index].id
107+
automount = false
108+
}
109+
110+
locals {
111+
username = lower(data.coder_workspace_owner.me.name)
112+
113+
# Data source: local JSON file under the module directory
114+
# Check API for latest server types & availability: https://docs.hetzner.cloud/reference/cloud#server-types
115+
hcloud_server_types_data = jsondecode(file("${path.module}/hetzner_server_types.json"))
116+
hcloud_server_type_meta = local.hcloud_server_types_data.type_meta
117+
hcloud_server_types_by_location = local.hcloud_server_types_data.availability
118+
119+
hcloud_server_type_options_for_selected_location = [
120+
for type_name in lookup(local.hcloud_server_types_by_location, data.coder_parameter.hcloud_location.value, []) : {
121+
name = format("%s (%d vCPU, %dGB RAM, %dGB)", upper(type_name), local.hcloud_server_type_meta[type_name].cores, local.hcloud_server_type_meta[type_name].memory_gb, local.hcloud_server_type_meta[type_name].disk_gb)
122+
value = type_name
123+
}
124+
]
125+
}
126+
127+
data "coder_provisioner" "me" {}
128+
129+
provider "coder" {}
130+
131+
data "coder_workspace" "me" {}
132+
133+
data "coder_workspace_owner" "me" {}
134+
135+
data "coder_parameter" "home_volume_size" {
136+
name = "home_volume_size"
137+
display_name = "Home volume size"
138+
description = "How large would you like your home volume to be (in GB)?"
139+
type = "number"
140+
default = "20"
141+
mutable = false
142+
validation {
143+
min = 1
144+
max = 100 # Adjust the max size as needed
145+
}
146+
}
147+
148+
resource "coder_agent" "main" {
149+
os = "linux"
150+
arch = "amd64"
151+
152+
metadata {
153+
key = "cpu"
154+
display_name = "CPU Usage"
155+
interval = 5
156+
timeout = 5
157+
script = "coder stat cpu"
158+
}
159+
metadata {
160+
key = "memory"
161+
display_name = "Memory Usage"
162+
interval = 5
163+
timeout = 5
164+
script = "coder stat mem"
165+
}
166+
metadata {
167+
key = "home"
168+
display_name = "Home Usage"
169+
interval = 600 # every 10 minutes
170+
timeout = 30 # df can take a while on large filesystems
171+
script = "coder stat disk --path /home/${local.username}"
172+
}
173+
}
174+
175+
module "code-server" {
176+
count = data.coder_workspace.me.start_count
177+
source = "registry.coder.com/coder/code-server/coder"
178+
179+
# 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.
180+
version = "~> 1.0"
181+
182+
agent_id = coder_agent.main.id
183+
order = 1
184+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /