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