TemplatesModules
Back to Templates
Docker Containers Icon

Docker Containers

By:
Provision Docker containers as Coder workspaces
Source
README
Resources (3)

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
12locals {
13  username = data.coder_workspace.me.owner
14}
15
16data "coder_provisioner" "me" {
17}
18
19provider "docker" {
20}
21
22data "coder_workspace" "me" {
23}
24
25resource "coder_agent" "main" {
26  arch           = data.coder_provisioner.me.arch
27  os             = "linux"
28  startup_script = <<-EOT
29    set -e
30
31    # Prepare user home with default files on first start.
32    if [ ! -f ~/.init_done ]; then
33      cp -rT /etc/skel ~
34      touch ~/.init_done
35    fi
36
37    # install and start code-server
38    curl -fsSL https://code-server.dev/install.sh | sh -s -- --method=standalone --prefix=/tmp/code-server --version 4.19.1
39    /tmp/code-server/bin/code-server --auth none --port 13337 >/tmp/code-server.log 2>&1 &
40  EOT
41
42  # These environment variables allow you to make Git commits right away after creating a
43  # workspace. Note that they take precedence over configuration defined in ~/.gitconfig!
44  # You can remove this block if you'd prefer to configure Git manually or using
45  # dotfiles. (see docs/dotfiles.md)
46  env = {
47    GIT_AUTHOR_NAME     = coalesce(data.coder_workspace.me.owner_name, data.coder_workspace.me.owner)
48    GIT_AUTHOR_EMAIL    = "${data.coder_workspace.me.owner_email}"
49    GIT_COMMITTER_NAME  = coalesce(data.coder_workspace.me.owner_name, data.coder_workspace.me.owner)
50    GIT_COMMITTER_EMAIL = "${data.coder_workspace.me.owner_email}"
51  }
52
53  # The following metadata blocks are optional. They are used to display
54  # information about your workspace in the dashboard. You can remove them
55  # if you don't want to display any information.
56  # For basic resources, you can use the `coder stat` command.
57  # If you need more control, you can write your own script.
58  metadata {
59    display_name = "CPU Usage"
60    key          = "0_cpu_usage"
61    script       = "coder stat cpu"
62    interval     = 10
63    timeout      = 1
64  }
65
66  metadata {
67    display_name = "RAM Usage"
68    key          = "1_ram_usage"
69    script       = "coder stat mem"
70    interval     = 10
71    timeout      = 1
72  }
73
74  metadata {
75    display_name = "Home Disk"
76    key          = "3_home_disk"
77    script       = "coder stat disk --path $${HOME}"
78    interval     = 60
79    timeout      = 1
80  }
81
82  metadata {
83    display_name = "CPU Usage (Host)"
84    key          = "4_cpu_usage_host"
85    script       = "coder stat cpu --host"
86    interval     = 10
87    timeout      = 1
88  }
89
90  metadata {
91    display_name = "Memory Usage (Host)"
92    key          = "5_mem_usage_host"
93    script       = "coder stat mem --host"
94    interval     = 10
95    timeout      = 1
96  }
97
98  metadata {
99    display_name = "Load Average (Host)"
100    key          = "6_load_host"
101    # get load avg scaled by number of cores
102    script   = <<EOT
103      echo "`cat /proc/loadavg | awk '{ print $1 }'` `nproc`" | awk '{ printf "%0.2f", $1/$2 }'
104    EOT
105    interval = 60
106    timeout  = 1
107  }
108
109  metadata {
110    display_name = "Swap Usage (Host)"
111    key          = "7_swap_host"
112    script       = <<EOT
113      free -b | awk '/^Swap/ { printf("%.1f/%.1f", $3/1024.0/1024.0/1024.0, $2/1024.0/1024.0/1024.0) }'
114    EOT
115    interval     = 10
116    timeout      = 1
117  }
118}
119
120resource "coder_app" "code-server" {
121  agent_id     = coder_agent.main.id
122  slug         = "code-server"
123  display_name = "code-server"
124  url          = "http://localhost:13337/?folder=/home/${local.username}"
125  icon         = "/icon/code.svg"
126  subdomain    = false
127  share        = "owner"
128
129  healthcheck {
130    url       = "http://localhost:13337/healthz"
131    interval  = 5
132    threshold = 6
133  }
134}
135
136resource "docker_volume" "home_volume" {
137  name = "coder-${data.coder_workspace.me.id}-home"
138  # Protect the volume from being deleted due to changes in attributes.
139  lifecycle {
140    ignore_changes = all
141  }
142  # Add labels in Docker to keep track of orphan resources.
143  labels {
144    label = "coder.owner"
145    value = data.coder_workspace.me.owner
146  }
147  labels {
148    label = "coder.owner_id"
149    value = data.coder_workspace.me.owner_id
150  }
151  labels {
152    label = "coder.workspace_id"
153    value = data.coder_workspace.me.id
154  }
155  # This field becomes outdated if the workspace is renamed but can
156  # be useful for debugging or cleaning out dangling volumes.
157  labels {
158    label = "coder.workspace_name_at_creation"
159    value = data.coder_workspace.me.name
160  }
161}
162
163resource "docker_image" "main" {
164  name = "coder-${data.coder_workspace.me.id}"
165  build {
166    context = "./build"
167    build_args = {
168      USER = local.username
169    }
170  }
171  triggers = {
172    dir_sha1 = sha1(join("", [for f in fileset(path.module, "build/*") : filesha1(f)]))
173  }
174}
175
176resource "docker_container" "workspace" {
177  count = data.coder_workspace.me.start_count
178  image = docker_image.main.name
179  # Uses lower() to avoid Docker restriction on container names.
180  name = "coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}"
181  # Hostname makes the shell more user friendly: coder@my-workspace:~$
182  hostname = data.coder_workspace.me.name
183  # Use the docker gateway if the access URL is 127.0.0.1
184  entrypoint = ["sh", "-c", replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")]
185  env        = ["CODER_AGENT_TOKEN=${coder_agent.main.token}"]
186  host {
187    host = "host.docker.internal"
188    ip   = "host-gateway"
189  }
190  volumes {
191    container_path = "/home/${local.username}"
192    volume_name    = docker_volume.home_volume.name
193    read_only      = false
194  }
195
196  # Add labels in Docker to keep track of orphan resources.
197  labels {
198    label = "coder.owner"
199    value = data.coder_workspace.me.owner
200  }
201  labels {
202    label = "coder.owner_id"
203    value = data.coder_workspace.me.owner_id
204  }
205  labels {
206    label = "coder.workspace_id"
207    value = data.coder_workspace.me.id
208  }
209  labels {
210    label = "coder.workspace_name"
211    value = data.coder_workspace.me.name
212  }
213}
214