TemplatesModules
Back to Templates
Devcontainers (Kubernetes) Icon

Devcontainers (Kubernetes)

By:
Provision envbuilder pods as Coder workspaces
Source
README
Resources (2)
Variables (2)

Copy and paste the following into main.tf and run coder template push:

1terraform {
2  required_providers {
3    coder = {
4      source = "coder/coder"
5    }
6    kubernetes = {
7      source = "hashicorp/kubernetes"
8    }
9  }
10}
11
12data "coder_provisioner" "me" {
13}
14
15provider "coder" {
16}
17
18variable "use_kubeconfig" {
19  type        = bool
20  description = <<-EOF
21  Use host kubeconfig? (true/false)
22
23  Set this to false if the Coder host is itself running as a Pod on the same
24  Kubernetes cluster as you are deploying workspaces to.
25
26  Set this to true if the Coder host is running outside the Kubernetes cluster
27  for workspaces.  A valid "~/.kube/config" must be present on the Coder host.
28  EOF
29  default     = false
30}
31
32variable "namespace" {
33  type        = string
34  description = "The Kubernetes namespace to create workspaces in (must exist prior to creating workspaces)"
35}
36
37provider "kubernetes" {
38  # Authenticate via ~/.kube/config or a Coder-specific ServiceAccount, depending on admin preferences
39  config_path = var.use_kubeconfig == true ? "~/.kube/config" : null
40}
41
42
43data "coder_workspace" "me" {
44}
45
46resource "coder_agent" "main" {
47  arch           = data.coder_provisioner.me.arch
48  os             = "linux"
49  startup_script = <<-EOT
50    set -e
51
52    # install and start code-server
53    curl -fsSL https://code-server.dev/install.sh | sh -s -- --method=standalone --prefix=/tmp/code-server --version 4.11.0
54    /tmp/code-server/bin/code-server --auth none --port 13337 >/tmp/code-server.log 2>&1 &
55  EOT
56  dir            = "/workspaces"
57
58  # These environment variables allow you to make Git commits right away after creating a
59  # workspace. Note that they take precedence over configuration defined in ~/.gitconfig!
60  # You can remove this block if you'd prefer to configure Git manually or using
61  # dotfiles. (see docs/dotfiles.md)
62  env = {
63    GIT_AUTHOR_NAME     = coalesce(data.coder_workspace.me.owner_name, data.coder_workspace.me.owner)
64    GIT_AUTHOR_EMAIL    = "${data.coder_workspace.me.owner_email}"
65    GIT_COMMITTER_NAME  = coalesce(data.coder_workspace.me.owner_name, data.coder_workspace.me.owner)
66    GIT_COMMITTER_EMAIL = "${data.coder_workspace.me.owner_email}"
67  }
68
69}
70
71resource "coder_app" "code-server" {
72  agent_id     = coder_agent.main.id
73  slug         = "code-server"
74  display_name = "code-server"
75  url          = "http://localhost:13337/?folder=/workspaces"
76  icon         = "/icon/code.svg"
77  subdomain    = false
78  share        = "owner"
79
80  healthcheck {
81    url       = "http://localhost:13337/healthz"
82    interval  = 5
83    threshold = 6
84  }
85}
86
87resource "kubernetes_persistent_volume_claim" "workspaces" {
88  metadata {
89    name      = "coder-${data.coder_workspace.me.id}"
90    namespace = var.namespace
91    labels = {
92      "coder.owner"                      = data.coder_workspace.me.owner
93      "coder.owner_id"                   = data.coder_workspace.me.owner_id
94      "coder.workspace_id"               = data.coder_workspace.me.id
95      "coder.workspace_name_at_creation" = data.coder_workspace.me.name
96    }
97  }
98  wait_until_bound = false
99  spec {
100    access_modes = ["ReadWriteOnce"]
101    resources {
102      requests = {
103        storage = "10Gi" // adjust as needed
104      }
105    }
106  }
107  lifecycle {
108    ignore_changes = all
109  }
110}
111
112data "coder_parameter" "repo" {
113  name         = "repo"
114  display_name = "Repository (auto)"
115  order        = 1
116  description  = "Select a repository to automatically clone and start working with a devcontainer."
117  mutable      = true
118  option {
119    name        = "vercel/next.js"
120    description = "The React Framework"
121    value       = "https://github.com/vercel/next.js"
122  }
123  option {
124    name        = "home-assistant/core"
125    description = "🏡 Open source home automation that puts local control and privacy first."
126    value       = "https://github.com/home-assistant/core"
127  }
128  option {
129    name        = "discourse/discourse"
130    description = "A platform for community discussion. Free, open, simple."
131    value       = "https://github.com/discourse/discourse"
132  }
133  option {
134    name        = "denoland/deno"
135    description = "A modern runtime for JavaScript and TypeScript."
136    value       = "https://github.com/denoland/deno"
137  }
138  option {
139    name        = "microsoft/vscode"
140    icon        = "/icon/code.svg"
141    description = "Code editing. Redefined."
142    value       = "https://github.com/microsoft/vscode"
143  }
144  option {
145    name        = "Custom"
146    icon        = "/emojis/1f5c3.png"
147    description = "Specify a custom repo URL below"
148    value       = "custom"
149  }
150}
151
152data "coder_parameter" "custom_repo_url" {
153  name         = "custom_repo"
154  display_name = "Repository URL (custom)"
155  order        = 2
156  default      = ""
157  description  = "Optionally enter a custom repository URL, see [awesome-devcontainers](https://github.com/manekinekko/awesome-devcontainers)."
158  mutable      = true
159}
160
161resource "kubernetes_deployment" "workspace" {
162  metadata {
163    name      = "coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}"
164    namespace = var.namespace
165    labels = {
166      "coder.owner"          = data.coder_workspace.me.owner
167      "coder.owner_id"       = data.coder_workspace.me.owner_id
168      "coder.workspace_id"   = data.coder_workspace.me.id
169      "coder.workspace_name" = data.coder_workspace.me.name
170    }
171  }
172  spec {
173    replicas = data.coder_workspace.me.start_count
174    selector {
175      match_labels = {
176        "coder.workspace_id" = data.coder_workspace.me.id
177      }
178    }
179    strategy {
180      type = "Recreate"
181    }
182    template {
183      metadata {
184        labels = {
185          "coder.workspace_id" = data.coder_workspace.me.id
186        }
187      }
188      spec {
189        container {
190          name = "coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}"
191          # Find the latest version here:
192          # https://github.com/coder/envbuilder/tags
193          image = "ghcr.io/coder/envbuilder:0.2.1"
194          env {
195            name  = "CODER_AGENT_TOKEN"
196            value = coder_agent.main.token
197          }
198          env {
199            name  = "CODER_AGENT_URL"
200            value = replace(data.coder_workspace.me.access_url, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")
201          }
202          env {
203            name  = "GIT_URL"
204            value = data.coder_parameter.repo.value == "custom" ? data.coder_parameter.custom_repo_url.value : data.coder_parameter.repo.value
205          }
206          env {
207            name  = "INIT_SCRIPT"
208            value = replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")
209          }
210          env {
211            name  = "FALLBACK_IMAGE"
212            value = "codercom/enterprise-base:ubuntu"
213          }
214          volume_mount {
215            name       = "workspaces"
216            mount_path = "/workspaces"
217          }
218        }
219        volume {
220          name = "workspaces"
221          persistent_volume_claim {
222            claim_name = kubernetes_persistent_volume_claim.workspaces.metadata.0.name
223          }
224        }
225      }
226    }
227  }
228}
229