feat(coder): add desktop workspace template #9

Merged
k.renner merged 1 commits from coder-migration into master 2026-04-24 20:57:11 +00:00
@@ -0,0 +1,243 @@
terraform {
required_providers {
coder = {
source = "coder/coder"
}
kubernetes = {
source = "hashicorp/kubernetes"
}
}
}
provider "coder" {}
provider "kubernetes" {
config_path = null
}
variable "namespace" {
description = "Kubernetes namespace in which to spawn workspaces."
type = string
default = "coder"
}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
data "coder_parameter" "cpu" {
name = "cpu"
display_name = "CPU cores"
description = "Number of CPU cores for the workspace."
default = "2"
mutable = true
option {
name = "1"
value = "1"
}
option {
name = "2"
value = "2"
}
option {
name = "4"
value = "4"
}
option {
name = "8"
value = "8"
}
}
data "coder_parameter" "memory" {
name = "memory"
display_name = "Memory (GiB)"
description = "Memory allocation for the workspace."
default = "4"
mutable = true
option {
name = "2"
value = "2"
}
option {
name = "4"
value = "4"
}
option {
name = "8"
value = "8"
}
option {
name = "16"
value = "16"
}
}
data "coder_parameter" "home_size" {
name = "home_size"
display_name = "Home volume size (GiB)"
description = "Persistent storage attached at /home/coder. Survives stop/start; deleted with the workspace."
default = "20"
mutable = false
validation {
min = 5
max = 200
}
type = "number"
}
locals {
workspace_name = lower("${data.coder_workspace_owner.me.name}-${data.coder_workspace.me.name}")
}
resource "coder_agent" "main" {
os = "linux"
arch = "amd64"
startup_script = <<-EOT
set -e
# First-boot: seed /home/coder from the image if the PVC is fresh.
if [ ! -f /home/coder/.seeded ]; then
cp -rT /etc/skel /home/coder || true
touch /home/coder/.seeded
fi
EOT
metadata {
display_name = "CPU"
key = "cpu"
script = "coder stat cpu"
interval = 10
timeout = 1
}
metadata {
display_name = "Memory"
key = "memory"
script = "coder stat mem"
interval = 10
timeout = 1
}
metadata {
display_name = "Home"
key = "disk"
script = "coder stat disk --path /home/coder"
interval = 60
timeout = 1
}
}
# Browser-accessible XFCE desktop via KasmVNC. Exposes a web app
# icon on the Coder dashboard that opens the session in a tab.
module "kasmvnc" {
source = "registry.coder.com/modules/kasmvnc/coder"
version = "~> 1.0"
agent_id = coder_agent.main.id
desktop_environment = "xfce"
}
resource "kubernetes_persistent_volume_claim" "home" {
metadata {
name = "coder-${local.workspace_name}-home"
namespace = var.namespace
labels = {
"app.kubernetes.io/part-of" = "coder"
"app.kubernetes.io/name" = "coder-workspace-home"
"app.kubernetes.io/instance" = local.workspace_name
}
}
spec {
access_modes = ["ReadWriteOnce"]
storage_class_name = "ceph-block"
resources {
requests = {
storage = "${data.coder_parameter.home_size.value}Gi"
}
}
}
# The PVC survives stop/start; Coder deletes it only when the
# workspace is destroyed (owner references wired via the
# deployment below are NOT used so the PVC is not GC'd on stop).
lifecycle {
ignore_changes = [metadata[0].annotations]
}
}
resource "kubernetes_deployment" "workspace" {
count = data.coder_workspace.me.start_count
metadata {
name = "coder-${local.workspace_name}"
namespace = var.namespace
labels = {
"app.kubernetes.io/part-of" = "coder"
"app.kubernetes.io/name" = "coder-workspace"
"app.kubernetes.io/instance" = local.workspace_name
}
}
spec {
replicas = 1
selector {
match_labels = {
"app.kubernetes.io/name" = "coder-workspace"
"app.kubernetes.io/instance" = local.workspace_name
}
}
strategy {
type = "Recreate"
}
template {
metadata {
labels = {
"app.kubernetes.io/part-of" = "coder"
"app.kubernetes.io/name" = "coder-workspace"
"app.kubernetes.io/instance" = local.workspace_name
}
}
spec {
security_context {
run_as_non_root = true
run_as_user = 1000
fs_group = 1000
seccomp_profile {
type = "RuntimeDefault"
}
}
container {
name = "dev"
image = "codercom/enterprise-base:ubuntu"
image_pull_policy = "IfNotPresent"
command = ["sh", "-c", coder_agent.main.init_script]
security_context {
run_as_non_root = true
run_as_user = 1000
allow_privilege_escalation = false
capabilities {
drop = ["ALL"]
}
}
env {
name = "CODER_AGENT_TOKEN"
value = coder_agent.main.token
}
resources {
requests = {
cpu = "500m"
memory = "1Gi"
}
limits = {
cpu = "${data.coder_parameter.cpu.value}"
memory = "${data.coder_parameter.memory.value}Gi"
}
}
volume_mount {
name = "home"
mount_path = "/home/coder"
}
}
volume {
name = "home"
persistent_volume_claim {
claim_name = kubernetes_persistent_volume_claim.home.metadata[0].name
}
}
}
}
}
}