TemplatesModules
Back to Templates
Docker Containers Icon

Docker Containers

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

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