TemplatesModules
Back to Templates
Incus System Container with Docker Icon

Incus System Container with Docker

By:
Develop in an Incus System Container with Docker using incus
Source
README
Resources (4)

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    incus = {
7      source = "lxc/incus"
8    }
9  }
10}
11
12data "coder_provisioner" "me" {}
13
14provider "incus" {}
15
16data "coder_workspace" "me" {}
17data "coder_workspace_owner" "me" {}
18
19data "coder_parameter" "image" {
20  name         = "image"
21  display_name = "Image"
22  description  = "The container image to use. Be sure to use a variant with cloud-init installed!"
23  default      = "ubuntu/jammy/cloud/amd64"
24  icon         = "/icon/image.svg"
25  mutable      = true
26}
27
28data "coder_parameter" "cpu" {
29  name         = "cpu"
30  display_name = "CPU"
31  description  = "The number of CPUs to allocate to the workspace (1-8)"
32  type         = "number"
33  default      = "1"
34  icon         = "https://raw.githubusercontent.com/matifali/logos/main/cpu-3.svg"
35  mutable      = true
36  validation {
37    min = 1
38    max = 8
39  }
40}
41
42data "coder_parameter" "memory" {
43  name         = "memory"
44  display_name = "Memory"
45  description  = "The amount of memory to allocate to the workspace in GB (up to 16GB)"
46  type         = "number"
47  default      = "2"
48  icon         = "/icon/memory.svg"
49  mutable      = true
50  validation {
51    min = 1
52    max = 16
53  }
54}
55
56data "coder_parameter" "git_repo" {
57  type        = "string"
58  name        = "Git repository"
59  default     = "https://github.com/coder/coder"
60  description = "Clone a git repo into [base directory]"
61  mutable     = true
62}
63
64data "coder_parameter" "repo_base_dir" {
65  type        = "string"
66  name        = "Repository Base Directory"
67  default     = "~"
68  description = "The directory specified will be created (if missing) and the specified repo will be cloned into [base directory]/{repo}🪄."
69  mutable     = true
70}
71
72resource "coder_agent" "main" {
73  count = data.coder_workspace.me.start_count
74  arch  = data.coder_provisioner.me.arch
75  os    = "linux"
76  dir   = "/home/${local.workspace_user}"
77  env = {
78    CODER_WORKSPACE_ID = data.coder_workspace.me.id
79  }
80
81  metadata {
82    display_name = "CPU Usage"
83    key          = "0_cpu_usage"
84    script       = "coder stat cpu"
85    interval     = 10
86    timeout      = 1
87  }
88
89  metadata {
90    display_name = "RAM Usage"
91    key          = "1_ram_usage"
92    script       = "coder stat mem"
93    interval     = 10
94    timeout      = 1
95  }
96
97  metadata {
98    display_name = "Home Disk"
99    key          = "3_home_disk"
100    script       = "coder stat disk --path /home/${lower(data.coder_workspace_owner.me.name)}"
101    interval     = 60
102    timeout      = 1
103  }
104}
105
106module "git-clone" {
107  source   = "registry.coder.com/modules/git-clone/coder"
108  version  = "1.0.2"
109  agent_id = local.agent_id
110  url      = data.coder_parameter.git_repo.value
111  base_dir = local.repo_base_dir
112}
113
114module "code-server" {
115  source   = "registry.coder.com/modules/code-server/coder"
116  version  = "1.0.2"
117  agent_id = local.agent_id
118  folder   = local.repo_base_dir
119}
120
121module "filebrowser" {
122  source   = "registry.coder.com/modules/filebrowser/coder"
123  version  = "1.0.2"
124  agent_id = local.agent_id
125}
126
127module "coder-login" {
128  source   = "registry.coder.com/modules/coder-login/coder"
129  version  = "1.0.2"
130  agent_id = local.agent_id
131}
132
133resource "incus_volume" "home" {
134  name = "coder-${data.coder_workspace.me.id}-home"
135  pool = local.pool
136}
137
138resource "incus_volume" "docker" {
139  name = "coder-${data.coder_workspace.me.id}-docker"
140  pool = local.pool
141}
142
143resource "incus_cached_image" "image" {
144  source_remote = "images"
145  source_image  = data.coder_parameter.image.value
146}
147
148resource "incus_instance_file" "agent_token" {
149  count              = data.coder_workspace.me.start_count
150  instance           = incus_instance.dev.name
151  content            = <<EOF
152CODER_AGENT_TOKEN=${local.agent_token}
153EOF
154  create_directories = true
155  target_path        = "/opt/coder/init.env"
156}
157
158resource "incus_instance" "dev" {
159  running = data.coder_workspace.me.start_count == 1
160  name    = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}"
161  image   = incus_cached_image.image.fingerprint
162
163  config = {
164    "security.nesting"                     = true
165    "security.syscalls.intercept.mknod"    = true
166    "security.syscalls.intercept.setxattr" = true
167    "boot.autostart"                       = true
168    "cloud-init.user-data"                 = <<EOF
169#cloud-config
170hostname: ${lower(data.coder_workspace.me.name)}
171users:
172  - name: ${local.workspace_user}
173    uid: 1000
174    gid: 1000
175    groups: sudo
176    packages:
177      - curl
178    shell: /bin/bash
179    sudo: ['ALL=(ALL) NOPASSWD:ALL']
180write_files:
181  - path: /opt/coder/init
182    permissions: "0755"
183    encoding: b64
184    content: ${base64encode(local.agent_init_script)}
185  - path: /etc/systemd/system/coder-agent.service
186    permissions: "0644"
187    content: |
188      [Unit]
189      Description=Coder Agent
190      After=network-online.target
191      Wants=network-online.target
192
193      [Service]
194      User=${local.workspace_user}
195      EnvironmentFile=/opt/coder/init.env
196      ExecStart=/opt/coder/init
197      Restart=always
198      RestartSec=10
199      TimeoutStopSec=90
200      KillMode=process
201
202      OOMScoreAdjust=-900
203      SyslogIdentifier=coder-agent
204
205      [Install]
206      WantedBy=multi-user.target
207  - path: /etc/systemd/system/coder-agent-watcher.service
208    permissions: "0644"
209    content: |
210      [Unit]
211      Description=Coder Agent Watcher
212      After=network-online.target
213
214      [Service]
215      Type=oneshot
216      ExecStart=/usr/bin/systemctl restart coder-agent.service
217
218      [Install]
219      WantedBy=multi-user.target
220  - path: /etc/systemd/system/coder-agent-watcher.path
221    permissions: "0644"
222    content: |
223      [Path]
224      PathModified=/opt/coder/init.env
225      Unit=coder-agent-watcher.service
226
227      [Install]
228      WantedBy=multi-user.target
229runcmd:
230  - chown -R ${local.workspace_user}:${local.workspace_user} /home/${local.workspace_user}
231  - |
232    #!/bin/bash
233    apt-get update && apt-get install -y curl docker.io
234    usermod -aG docker ${local.workspace_user}
235    newgrp docker
236  - systemctl enable coder-agent.service coder-agent-watcher.service coder-agent-watcher.path
237  - systemctl start coder-agent.service coder-agent-watcher.service coder-agent-watcher.path
238EOF
239  }
240
241  limits = {
242    cpu    = data.coder_parameter.cpu.value
243    memory = "${data.coder_parameter.cpu.value}GiB"
244  }
245
246  device {
247    name = "home"
248    type = "disk"
249    properties = {
250      path   = "/home/${local.workspace_user}"
251      pool   = local.pool
252      source = incus_volume.home.name
253    }
254  }
255
256  device {
257    name = "docker"
258    type = "disk"
259    properties = {
260      path   = "/var/lib/docker"
261      pool   = local.pool
262      source = incus_volume.docker.name
263    }
264  }
265
266  device {
267    name = "root"
268    type = "disk"
269    properties = {
270      path = "/"
271      pool = local.pool
272    }
273  }
274}
275
276locals {
277  workspace_user    = lower(data.coder_workspace_owner.me.name)
278  pool              = "coder"
279  repo_base_dir     = data.coder_parameter.repo_base_dir.value == "~" ? "/home/${local.workspace_user}" : replace(data.coder_parameter.repo_base_dir.value, "/^~\\//", "/home/${local.workspace_user}/")
280  repo_dir          = module.git-clone.repo_dir
281  agent_id          = data.coder_workspace.me.start_count == 1 ? coder_agent.main[0].id : ""
282  agent_token       = data.coder_workspace.me.start_count == 1 ? coder_agent.main[0].token : ""
283  agent_init_script = data.coder_workspace.me.start_count == 1 ? coder_agent.main[0].init_script : ""
284}
285
286resource "coder_metadata" "info" {
287  count       = data.coder_workspace.me.start_count
288  resource_id = incus_instance.dev.name
289  item {
290    key   = "memory"
291    value = incus_instance.dev.limits.memory
292  }
293  item {
294    key   = "cpus"
295    value = incus_instance.dev.limits.cpu
296  }
297  item {
298    key   = "instance"
299    value = incus_instance.dev.name
300  }
301  item {
302    key   = "image"
303    value = "${incus_cached_image.image.source_remote}:${incus_cached_image.image.source_image}"
304  }
305  item {
306    key   = "image_fingerprint"
307    value = substr(incus_cached_image.image.fingerprint, 0, 12)
308  }
309}
310
311