TemplatesModules
Back to Templates
Devcontainers (Docker) Icon

Devcontainers (Docker)

By:
Provision envbuilder containers as Coder workspaces
Source
README
Resources (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    docker = {
7      source = "kreuzwerker/docker"
8    }
9  }
10}
11
12data "coder_provisioner" "me" {
13}
14
15provider "docker" {
16}
17
18data "coder_workspace" "me" {
19}
20data "coder_workspace_owner" "me" {}
21
22resource "coder_agent" "main" {
23  arch           = data.coder_provisioner.me.arch
24  os             = "linux"
25  startup_script = <<-EOT
26    set -e
27
28    # install and start code-server
29    curl -fsSL https://code-server.dev/install.sh | sh -s -- --method=standalone --prefix=/tmp/code-server --version 4.11.0
30    /tmp/code-server/bin/code-server --auth none --port 13337 >/tmp/code-server.log 2>&1 &
31  EOT
32  dir            = "/workspaces"
33
34  # These environment variables allow you to make Git commits right away after creating a
35  # workspace. Note that they take precedence over configuration defined in ~/.gitconfig!
36  # You can remove this block if you'd prefer to configure Git manually or using
37  # dotfiles. (see docs/dotfiles.md)
38  env = {
39    GIT_AUTHOR_NAME     = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
40    GIT_AUTHOR_EMAIL    = "${data.coder_workspace_owner.me.email}"
41    GIT_COMMITTER_NAME  = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
42    GIT_COMMITTER_EMAIL = "${data.coder_workspace_owner.me.email}"
43  }
44
45  # The following metadata blocks are optional. They are used to display
46  # information about your workspace in the dashboard. You can remove them
47  # if you don't want to display any information.
48  # For basic resources, you can use the `coder stat` command.
49  # If you need more control, you can write your own script.
50  metadata {
51    display_name = "CPU Usage"
52    key          = "0_cpu_usage"
53    script       = "coder stat cpu"
54    interval     = 10
55    timeout      = 1
56  }
57
58  metadata {
59    display_name = "RAM Usage"
60    key          = "1_ram_usage"
61    script       = "coder stat mem"
62    interval     = 10
63    timeout      = 1
64  }
65
66  metadata {
67    display_name = "Home Disk"
68    key          = "3_home_disk"
69    script       = "coder stat disk --path $HOME"
70    interval     = 60
71    timeout      = 1
72  }
73
74  metadata {
75    display_name = "CPU Usage (Host)"
76    key          = "4_cpu_usage_host"
77    script       = "coder stat cpu --host"
78    interval     = 10
79    timeout      = 1
80  }
81
82  metadata {
83    display_name = "Memory Usage (Host)"
84    key          = "5_mem_usage_host"
85    script       = "coder stat mem --host"
86    interval     = 10
87    timeout      = 1
88  }
89
90  metadata {
91    display_name = "Load Average (Host)"
92    key          = "6_load_host"
93    # get load avg scaled by number of cores
94    script   = <<EOT
95      echo "`cat /proc/loadavg | awk '{ print $1 }'` `nproc`" | awk '{ printf "%0.2f", $1/$2 }'
96    EOT
97    interval = 60
98    timeout  = 1
99  }
100
101  metadata {
102    display_name = "Swap Usage (Host)"
103    key          = "7_swap_host"
104    script       = <<EOT
105      free -b | awk '/^Swap/ { printf("%.1f/%.1f", $3/1024.0/1024.0/1024.0, $2/1024.0/1024.0/1024.0) }'
106    EOT
107    interval     = 10
108    timeout      = 1
109  }
110}
111
112resource "coder_app" "code-server" {
113  agent_id     = coder_agent.main.id
114  slug         = "code-server"
115  display_name = "code-server"
116  url          = "http://localhost:13337/?folder=/workspaces"
117  icon         = "/icon/code.svg"
118  subdomain    = false
119  share        = "owner"
120
121  healthcheck {
122    url       = "http://localhost:13337/healthz"
123    interval  = 5
124    threshold = 6
125  }
126}
127
128
129resource "docker_volume" "workspaces" {
130  name = "coder-${data.coder_workspace.me.id}"
131  # Protect the volume from being deleted due to changes in attributes.
132  lifecycle {
133    ignore_changes = all
134  }
135  # Add labels in Docker to keep track of orphan resources.
136  labels {
137    label = "coder.owner"
138    value = data.coder_workspace_owner.me.name
139  }
140  labels {
141    label = "coder.owner_id"
142    value = data.coder_workspace_owner.me.id
143  }
144  labels {
145    label = "coder.workspace_id"
146    value = data.coder_workspace.me.id
147  }
148  # This field becomes outdated if the workspace is renamed but can
149  # be useful for debugging or cleaning out dangling volumes.
150  labels {
151    label = "coder.workspace_name_at_creation"
152    value = data.coder_workspace.me.name
153  }
154}
155
156data "coder_parameter" "repo" {
157  name         = "repo"
158  display_name = "Repository (auto)"
159  order        = 1
160  description  = "Select a repository to automatically clone and start working with a devcontainer."
161  mutable      = true
162  option {
163    name        = "vercel/next.js"
164    description = "The React Framework"
165    value       = "https://github.com/vercel/next.js"
166  }
167  option {
168    name        = "home-assistant/core"
169    description = "🏡 Open source home automation that puts local control and privacy first."
170    value       = "https://github.com/home-assistant/core"
171  }
172  option {
173    name        = "discourse/discourse"
174    description = "A platform for community discussion. Free, open, simple."
175    value       = "https://github.com/discourse/discourse"
176  }
177  option {
178    name        = "denoland/deno"
179    description = "A modern runtime for JavaScript and TypeScript."
180    value       = "https://github.com/denoland/deno"
181  }
182  option {
183    name        = "microsoft/vscode"
184    icon        = "/icon/code.svg"
185    description = "Code editing. Redefined."
186    value       = "https://github.com/microsoft/vscode"
187  }
188  option {
189    name        = "Custom"
190    icon        = "/emojis/1f5c3.png"
191    description = "Specify a custom repo URL below"
192    value       = "custom"
193  }
194}
195
196data "coder_parameter" "custom_repo_url" {
197  name         = "custom_repo"
198  display_name = "Repository URL (custom)"
199  order        = 2
200  default      = ""
201  description  = "Optionally enter a custom repository URL, see [awesome-devcontainers](https://github.com/manekinekko/awesome-devcontainers)."
202  mutable      = true
203}
204
205resource "docker_container" "workspace" {
206  count = data.coder_workspace.me.start_count
207  # Find the latest version here:
208  # https://github.com/coder/envbuilder/tags
209  image = "ghcr.io/coder/envbuilder:0.2.1"
210  # Uses lower() to avoid Docker restriction on container names.
211  name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
212  # Hostname makes the shell more user friendly: coder@my-workspace:~$
213  hostname = data.coder_workspace.me.name
214  # Use the docker gateway if the access URL is 127.0.0.1
215  env = [
216    "CODER_AGENT_TOKEN=${coder_agent.main.token}",
217    "CODER_AGENT_URL=${replace(data.coder_workspace.me.access_url, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")}",
218    "GIT_URL=${data.coder_parameter.repo.value == "custom" ? data.coder_parameter.custom_repo_url.value : data.coder_parameter.repo.value}",
219    "INIT_SCRIPT=${replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")}",
220    "FALLBACK_IMAGE=codercom/enterprise-base:ubuntu" # This image runs if builds fail
221  ]
222  host {
223    host = "host.docker.internal"
224    ip   = "host-gateway"
225  }
226  volumes {
227    container_path = "/workspaces"
228    volume_name    = docker_volume.workspaces.name
229    read_only      = false
230  }
231  # Add labels in Docker to keep track of orphan resources.
232  labels {
233    label = "coder.owner"
234    value = data.coder_workspace_owner.me.name
235  }
236  labels {
237    label = "coder.owner_id"
238    value = data.coder_workspace_owner.me.id
239  }
240  labels {
241    label = "coder.workspace_id"
242    value = data.coder_workspace.me.id
243  }
244  labels {
245    label = "coder.workspace_name"
246    value = data.coder_workspace.me.name
247  }
248}
249