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