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