coder: desktop template (KasmVNC + dotfiles) for the homelab cluster
Kubernetes template for coder.nic-oconnor.com: an XFCE desktop served by the KasmVNC module, with this repo applied by the dotfiles module. Home is a truenas-iscsi PVC so $HOME tools and op.env survive stops. Pinned to enterprise-desktop:ubuntu-noble + KasmVNC 1.5.0 because the plain `ubuntu` tag is now resolute, which KasmVNC has no package for. Path-based app (no wildcard access URL). An ephemeral 1Password token parameter lets bootstrap's secrets module run unattended on first start. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -95,6 +95,13 @@ Already have a clone? Just run it directly:
|
|||||||
are read from the **local checkout**, not re-downloaded from Gitea, so this
|
are read from the **local checkout**, not re-downloaded from Gitea, so this
|
||||||
repo does not have to be public for a shell to work; see its own README.
|
repo does not have to be public for a shell to work; see its own README.
|
||||||
|
|
||||||
|
Not run by `bootstrap.sh`:
|
||||||
|
|
||||||
|
- `coder/desktop/` — Coder template for coder.nic-oconnor.com: an XFCE
|
||||||
|
desktop (KasmVNC) pod on the homelab cluster that applies this repo via the
|
||||||
|
dotfiles module. Push with `coder templates push desktop -d coder/desktop`;
|
||||||
|
see its README.
|
||||||
|
|
||||||
## Per-machine customizations and secrets
|
## Per-machine customizations and secrets
|
||||||
|
|
||||||
`~/.zshrc` is managed — bootstrap overwrites it, so never hand-edit it.
|
`~/.zshrc` is managed — bootstrap overwrites it, so never hand-edit it.
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
.terraform/
|
||||||
|
*.tfstate*
|
||||||
Generated
+43
@@ -0,0 +1,43 @@
|
|||||||
|
# This file is maintained automatically by "terraform init".
|
||||||
|
# Manual edits may be lost in future updates.
|
||||||
|
|
||||||
|
provider "registry.terraform.io/coder/coder" {
|
||||||
|
version = "2.18.0"
|
||||||
|
constraints = ">= 2.5.0"
|
||||||
|
hashes = [
|
||||||
|
"h1:O25nA2tkM1JFJtQpaX2TOTeWvsSyuxDoJo6/QTv78hM=",
|
||||||
|
"zh:107c4eb7a36335ac94637679845c040759e21d3fb108d07081e0689cb93121b1",
|
||||||
|
"zh:1f8d904d59e35948f8f795e58d7895b791bd9a12c8409079f5bb90221303c1b6",
|
||||||
|
"zh:3942d5c6b6fad45b3b92f82c7b3f40a2d7f2b7b72da07f1ac63e1022b46bdbc7",
|
||||||
|
"zh:464f3b53eccfefc3ca02b46da07fb277362c16f81f45cc4570150520e5b9bf75",
|
||||||
|
"zh:6ae37bef989559a212ce04a615444d40452425e01081d8499843a24db0a09e85",
|
||||||
|
"zh:6f1410911124b0078a0f1c077899b4d834cd6821596ed350e4b4ad8f5b0030e7",
|
||||||
|
"zh:9ca356eb0e23bc034091fc9672bbe307a69e242c341a669f10db81e9a75fab2d",
|
||||||
|
"zh:b36687f0793295337286ae71d8157dc4c78a13fb5a9b1276093db82cdb8edd76",
|
||||||
|
"zh:c60a39cbde9fb473bfd0c2ad1685a0ce8d4a56408f060c6d1b858bae2e535d98",
|
||||||
|
"zh:d26ffdb52e095662e16b19942b7579bea7dc60577bd81f5cd671e6632ce48a0b",
|
||||||
|
"zh:da5414d74b21ecdcf8f93adc2199925c530cde1f53cde014201713d315916366",
|
||||||
|
"zh:e7e801f91b8afba90d40aa390c8b822ba064a54932a4a25bbff1593942e9bf72",
|
||||||
|
"zh:f064fb800fc687b2d60d4c12e1e65875a2f4b21cf8bb0b5e018d3ecbf7d29de2",
|
||||||
|
"zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "registry.terraform.io/hashicorp/kubernetes" {
|
||||||
|
version = "3.2.1"
|
||||||
|
hashes = [
|
||||||
|
"h1:XccFuTe/eJ94vkoZNYUL3wsdXJwlxHguIY36jildRQ8=",
|
||||||
|
"zh:067fe16a852d42e0f571712e36cb3e71855f917ea2041415e155f56ebc480d7f",
|
||||||
|
"zh:2815e174f8f0f032ea3a64f2196740ad000a39f88ae5646e7061bf15ed589f62",
|
||||||
|
"zh:2f94f6b689c59c43e596e724228f2861095d02c2a2ac2a257a4619667135ac75",
|
||||||
|
"zh:3e807310c84f11561b9ba06b978f03c46cfeca2e84ad0803d34d5d30a8a637cb",
|
||||||
|
"zh:5cba6f92202c60cac6898141356420709f5341b80ae4c360725cc647f86188ff",
|
||||||
|
"zh:72b841b6f0820d8f87c3d7c5a3611c35121ab9a4c1db4ea7a98b0319f209e474",
|
||||||
|
"zh:74770b892ee9b04829d92318d9e8ca96f8143b0c6c766e4141901908173fd01d",
|
||||||
|
"zh:7a723c8ebf9e218d0f7a0cfe6c0437f2b5eeb7ae015a14fad16e0f7fd9ef79ab",
|
||||||
|
"zh:a0f5073b2636a3894d4e9dd1b6853d5f324dd78728313bff79b842e5e9eca96f",
|
||||||
|
"zh:c13241cba993ef63a537beb6a1caf00e233bb045b50d197349530de0ee3276d5",
|
||||||
|
"zh:d52826f4b0227b7db99ea4a1d48f49a0bfb440563c92ebd2f8faec273c856c2d",
|
||||||
|
"zh:dc1cf5505a39a264a650b0830f74150ad02368787e5ead89e4007034f8f47831",
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# coder/desktop — Coder desktop template
|
||||||
|
|
||||||
|
A Coder template for <https://coder.nic-oconnor.com>: an XFCE desktop in the
|
||||||
|
browser (the [KasmVNC module](https://registry.coder.com/modules/coder/kasmvnc))
|
||||||
|
running as a pod on the homelab Kubernetes cluster, set up from this repo by the
|
||||||
|
[dotfiles module](https://registry.coder.com/modules/coder/dotfiles).
|
||||||
|
|
||||||
|
## Push / update
|
||||||
|
|
||||||
|
```sh
|
||||||
|
coder login https://coder.nic-oconnor.com
|
||||||
|
coder templates push desktop -d coder/desktop -y
|
||||||
|
```
|
||||||
|
|
||||||
|
The in-cluster provisioner uses the `coder` service account, whose workspace
|
||||||
|
Role is bound in the `coder` namespace — which is where workspaces go.
|
||||||
|
|
||||||
|
## What a workspace gets
|
||||||
|
|
||||||
|
- `codercom/enterprise-desktop:ubuntu-noble`: XFCE, git, python3, and the
|
||||||
|
`coder` user (uid 1000) with passwordless sudo.
|
||||||
|
- A PVC (`truenas-iscsi`, default 30 GiB) at `/home/coder`. Everything the
|
||||||
|
dotfiles install into `$HOME` survives stops; system packages (zsh, docker
|
||||||
|
CLI, KasmVNC) are reinstalled on each start because the root filesystem
|
||||||
|
is not persisted.
|
||||||
|
- The KasmVNC app, path-based (`subdomain = false`) because the deployment has
|
||||||
|
no wildcard access URL.
|
||||||
|
- A "Dotfiles" startup script: clones this repo into
|
||||||
|
`~/.config/coderv2/dotfiles` and runs `bootstrap.sh` (log:
|
||||||
|
`~/.dotfiles.log`), plus a "Refresh Dotfiles" button. The first run takes
|
||||||
|
about 5 minutes.
|
||||||
|
- Pod requests 0.5 CPU / 1 GiB; the CPU and memory parameters set limits (the
|
||||||
|
nodes are ~4 CPU / ~5 GiB and mostly requested already).
|
||||||
|
|
||||||
|
## 1Password secrets
|
||||||
|
|
||||||
|
bootstrap's `secrets/` module needs `OP_SERVICE_ACCOUNT_TOKEN` when there is
|
||||||
|
no TTY. Paste it into the **1Password service-account token** parameter when
|
||||||
|
you create the workspace. It is ephemeral: used for that build only, then
|
||||||
|
`~/.config/dotfiles/op.env` on the home volume carries it. Leave it empty and
|
||||||
|
bootstrap still applies everything else; `secrets`, `mcp-config` and `hermes`
|
||||||
|
are reported as not completed. Fix that later from a workspace terminal with
|
||||||
|
`~/.config/coderv2/dotfiles/secrets/install.sh`, then refresh the dotfiles.
|
||||||
|
|
||||||
|
## Why these pins
|
||||||
|
|
||||||
|
- `ubuntu-noble`, not `ubuntu`: the `ubuntu` tag is now 26.04 (resolute), and
|
||||||
|
KasmVNC publishes no resolute package, so the module's install 404s.
|
||||||
|
- `kasm_version = "1.5.0"`: the newest KasmVNC release, which has a noble
|
||||||
|
build. Revisit both once KasmVNC ships a resolute `.deb`.
|
||||||
@@ -0,0 +1,374 @@
|
|||||||
|
# coder/desktop — Coder template: an XFCE desktop in the browser (KasmVNC)
|
||||||
|
# on the homelab Kubernetes cluster, set up from this dotfiles repo.
|
||||||
|
#
|
||||||
|
# Push it with: coder templates push desktop -d coder/desktop
|
||||||
|
#
|
||||||
|
# The workspace is a Deployment (replicas 0/1 follows start/stop) plus a PVC
|
||||||
|
# for /home/coder, so the home directory — ~/.local/bin tools, the dotfiles
|
||||||
|
# checkout, ~/.config/dotfiles/op.env — survives stops and rebuilds.
|
||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
coder = {
|
||||||
|
source = "coder/coder"
|
||||||
|
}
|
||||||
|
kubernetes = {
|
||||||
|
source = "hashicorp/kubernetes"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "coder" {}
|
||||||
|
|
||||||
|
variable "use_kubeconfig" {
|
||||||
|
type = bool
|
||||||
|
description = "Use ~/.kube/config instead of the in-cluster service account. Leave false when the provisioner runs inside the cluster (the default Helm install)."
|
||||||
|
default = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "namespace" {
|
||||||
|
type = string
|
||||||
|
description = "Namespace for workspaces. The coder service account's workspace Role is bound here."
|
||||||
|
default = "coder"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "storage_class" {
|
||||||
|
type = string
|
||||||
|
description = "StorageClass for the home volume (the cluster has no default class)."
|
||||||
|
default = "truenas-iscsi"
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "kubernetes" {
|
||||||
|
config_path = var.use_kubeconfig ? "~/.kube/config" : null
|
||||||
|
}
|
||||||
|
|
||||||
|
data "coder_provisioner" "me" {}
|
||||||
|
data "coder_workspace" "me" {}
|
||||||
|
data "coder_workspace_owner" "me" {}
|
||||||
|
|
||||||
|
# --- parameters --------------------------------------------------------------
|
||||||
|
# Nodes have ~4 CPU / ~5 GiB allocatable and are already mostly requested, so
|
||||||
|
# the pod REQUESTS little (it schedules anywhere) and these set the LIMITS.
|
||||||
|
|
||||||
|
data "coder_parameter" "cpu" {
|
||||||
|
name = "cpu"
|
||||||
|
display_name = "CPU limit"
|
||||||
|
description = "CPU cores the workspace may burst to."
|
||||||
|
icon = "/icon/memory.svg"
|
||||||
|
type = "number"
|
||||||
|
default = "2"
|
||||||
|
mutable = true
|
||||||
|
order = 1
|
||||||
|
option {
|
||||||
|
name = "2 cores"
|
||||||
|
value = "2"
|
||||||
|
}
|
||||||
|
option {
|
||||||
|
name = "3 cores"
|
||||||
|
value = "3"
|
||||||
|
}
|
||||||
|
option {
|
||||||
|
name = "4 cores"
|
||||||
|
value = "4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data "coder_parameter" "memory" {
|
||||||
|
name = "memory"
|
||||||
|
display_name = "Memory limit"
|
||||||
|
description = "Memory in GiB the workspace may use (includes the 1 GiB /dev/shm)."
|
||||||
|
icon = "/icon/memory.svg"
|
||||||
|
type = "number"
|
||||||
|
default = "4"
|
||||||
|
mutable = true
|
||||||
|
order = 2
|
||||||
|
option {
|
||||||
|
name = "3 GB"
|
||||||
|
value = "3"
|
||||||
|
}
|
||||||
|
option {
|
||||||
|
name = "4 GB"
|
||||||
|
value = "4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data "coder_parameter" "home_disk_size" {
|
||||||
|
name = "home_disk_size"
|
||||||
|
display_name = "Home disk size"
|
||||||
|
description = "Size of /home/coder in GiB. Can be grown later, not shrunk."
|
||||||
|
icon = "/emojis/1f4be.png"
|
||||||
|
type = "number"
|
||||||
|
default = "30"
|
||||||
|
mutable = false
|
||||||
|
order = 3
|
||||||
|
validation {
|
||||||
|
min = 10
|
||||||
|
max = 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# The dotfiles secrets/ module reads the 1Password vault non-interactively
|
||||||
|
# when OP_SERVICE_ACCOUNT_TOKEN is in the environment. It writes
|
||||||
|
# ~/.config/dotfiles/op.env on the home volume, so the token is only needed
|
||||||
|
# on the FIRST start: ephemeral means it is not carried into later builds.
|
||||||
|
data "coder_parameter" "op_service_account_token" {
|
||||||
|
name = "op_service_account_token"
|
||||||
|
display_name = "1Password service-account token"
|
||||||
|
description = "Optional, first start only. Lets the dotfiles secrets module pull your vault (LITELLM_KEY, GITEA_KEY, ...). Stored in ~/.config/dotfiles/op.env after that. Leave empty to skip."
|
||||||
|
icon = "/icon/1password.svg"
|
||||||
|
type = "string"
|
||||||
|
default = ""
|
||||||
|
mutable = true
|
||||||
|
ephemeral = true
|
||||||
|
order = 4
|
||||||
|
styling = jsonencode({
|
||||||
|
mask_input = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
data "coder_parameter" "op_vault" {
|
||||||
|
name = "op_vault"
|
||||||
|
display_name = "1Password vault"
|
||||||
|
description = "Vault holding your secrets item. Empty = the only vault the token can see."
|
||||||
|
icon = "/icon/1password.svg"
|
||||||
|
type = "string"
|
||||||
|
default = "t-mint"
|
||||||
|
mutable = true
|
||||||
|
order = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
data "coder_parameter" "op_item" {
|
||||||
|
name = "op_item"
|
||||||
|
display_name = "1Password item"
|
||||||
|
description = "Item whose fields become env vars. Empty = the only item in the vault."
|
||||||
|
icon = "/icon/1password.svg"
|
||||||
|
type = "string"
|
||||||
|
default = "noconnor"
|
||||||
|
mutable = true
|
||||||
|
order = 6
|
||||||
|
}
|
||||||
|
|
||||||
|
locals {
|
||||||
|
name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}"
|
||||||
|
labels = {
|
||||||
|
"app.kubernetes.io/name" = "coder-workspace"
|
||||||
|
"app.kubernetes.io/instance" = local.name
|
||||||
|
"app.kubernetes.io/part-of" = "coder"
|
||||||
|
"com.coder.resource" = "true"
|
||||||
|
"com.coder.workspace.id" = data.coder_workspace.me.id
|
||||||
|
"com.coder.workspace.name" = data.coder_workspace.me.name
|
||||||
|
"com.coder.user.id" = data.coder_workspace_owner.me.id
|
||||||
|
"com.coder.user.username" = data.coder_workspace_owner.me.name
|
||||||
|
}
|
||||||
|
# Only the non-empty ones: an empty OP_VAULT/OP_ITEM would override the
|
||||||
|
# secrets module's own auto-detection.
|
||||||
|
op_env = { for k, v in {
|
||||||
|
OP_SERVICE_ACCOUNT_TOKEN = data.coder_parameter.op_service_account_token.value
|
||||||
|
OP_VAULT = data.coder_parameter.op_vault.value
|
||||||
|
OP_ITEM = data.coder_parameter.op_item.value
|
||||||
|
} : k => v if v != "" }
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- agent -------------------------------------------------------------------
|
||||||
|
|
||||||
|
resource "coder_agent" "main" {
|
||||||
|
arch = data.coder_provisioner.me.arch
|
||||||
|
os = "linux"
|
||||||
|
|
||||||
|
# The home PVC hides whatever the image put in /home/coder, so seed it on
|
||||||
|
# first start: the skel dotfiles, plus the image's .xsessionrc (XFCE session
|
||||||
|
# env that KasmVNC's X session reads).
|
||||||
|
startup_script = <<-EOT
|
||||||
|
set -e
|
||||||
|
if [ ! -f ~/.init_done ]; then
|
||||||
|
cp -rT /etc/skel ~ || true
|
||||||
|
touch ~/.init_done
|
||||||
|
fi
|
||||||
|
if [ ! -f ~/.xsessionrc ]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
'export GNOME_SHELL_SESSION_MODE=ubuntu' \
|
||||||
|
'export XDG_CURRENT_DESKTOP=xfce' \
|
||||||
|
'export XDG_SESSION_TYPE=x11' > ~/.xsessionrc
|
||||||
|
fi
|
||||||
|
EOT
|
||||||
|
|
||||||
|
env = merge({
|
||||||
|
GIT_AUTHOR_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
|
||||||
|
GIT_AUTHOR_EMAIL = data.coder_workspace_owner.me.email
|
||||||
|
GIT_COMMITTER_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
|
||||||
|
GIT_COMMITTER_EMAIL = data.coder_workspace_owner.me.email
|
||||||
|
}, local.op_env)
|
||||||
|
|
||||||
|
metadata {
|
||||||
|
display_name = "CPU Usage"
|
||||||
|
key = "0_cpu_usage"
|
||||||
|
script = "coder stat cpu"
|
||||||
|
interval = 10
|
||||||
|
timeout = 1
|
||||||
|
}
|
||||||
|
metadata {
|
||||||
|
display_name = "RAM Usage"
|
||||||
|
key = "1_ram_usage"
|
||||||
|
script = "coder stat mem"
|
||||||
|
interval = 10
|
||||||
|
timeout = 1
|
||||||
|
}
|
||||||
|
metadata {
|
||||||
|
display_name = "Home Disk"
|
||||||
|
key = "2_home_disk"
|
||||||
|
script = "coder stat disk --path $HOME"
|
||||||
|
interval = 60
|
||||||
|
timeout = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- modules -----------------------------------------------------------------
|
||||||
|
|
||||||
|
# Clones the repo into ~/.config/coderv2/dotfiles and runs its bootstrap.sh on
|
||||||
|
# every start (each module is idempotent). The first run takes ~5 minutes;
|
||||||
|
# progress is in ~/.dotfiles.log and the "Dotfiles" script log in the UI.
|
||||||
|
module "dotfiles" {
|
||||||
|
count = data.coder_workspace.me.start_count
|
||||||
|
source = "registry.coder.com/coder/dotfiles/coder"
|
||||||
|
version = "1.4.2"
|
||||||
|
agent_id = coder_agent.main.id
|
||||||
|
default_dotfiles_uri = "http://git.nic-oconnor.com/public/dotfiles.git"
|
||||||
|
manual_update = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# XFCE is in the image; the module installs KasmVNC and serves it on :6800.
|
||||||
|
# kasm_version: KasmVNC ships a .deb per Ubuntu codename, and the module's
|
||||||
|
# default (1.4.0) is older than we need; 1.5.0 has noble.
|
||||||
|
# subdomain = false: the deployment has no wildcard access URL, so the app
|
||||||
|
# is served path-based under coder.nic-oconnor.com.
|
||||||
|
module "kasmvnc" {
|
||||||
|
count = data.coder_workspace.me.start_count
|
||||||
|
source = "registry.coder.com/coder/kasmvnc/coder"
|
||||||
|
version = "1.3.0"
|
||||||
|
agent_id = coder_agent.main.id
|
||||||
|
desktop_environment = "xfce"
|
||||||
|
kasm_version = "1.5.0"
|
||||||
|
subdomain = false
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- kubernetes resources ----------------------------------------------------
|
||||||
|
|
||||||
|
resource "kubernetes_persistent_volume_claim_v1" "home" {
|
||||||
|
metadata {
|
||||||
|
name = "${local.name}-home"
|
||||||
|
namespace = var.namespace
|
||||||
|
labels = merge(local.labels, { "app.kubernetes.io/component" = "home" })
|
||||||
|
annotations = { "com.coder.user.email" = data.coder_workspace_owner.me.email }
|
||||||
|
}
|
||||||
|
wait_until_bound = false
|
||||||
|
spec {
|
||||||
|
access_modes = ["ReadWriteOnce"]
|
||||||
|
storage_class_name = var.storage_class
|
||||||
|
resources {
|
||||||
|
requests = {
|
||||||
|
storage = "${data.coder_parameter.home_disk_size.value}Gi"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "kubernetes_deployment_v1" "main" {
|
||||||
|
count = data.coder_workspace.me.start_count
|
||||||
|
depends_on = [kubernetes_persistent_volume_claim_v1.home]
|
||||||
|
wait_for_rollout = false
|
||||||
|
|
||||||
|
metadata {
|
||||||
|
name = local.name
|
||||||
|
namespace = var.namespace
|
||||||
|
labels = merge(local.labels, { "app.kubernetes.io/component" = "workspace" })
|
||||||
|
annotations = { "com.coder.user.email" = data.coder_workspace_owner.me.email }
|
||||||
|
}
|
||||||
|
|
||||||
|
spec {
|
||||||
|
replicas = 1
|
||||||
|
selector {
|
||||||
|
match_labels = local.labels
|
||||||
|
}
|
||||||
|
# RWO home volume: the old pod must let go of it before the new one starts.
|
||||||
|
strategy {
|
||||||
|
type = "Recreate"
|
||||||
|
}
|
||||||
|
|
||||||
|
template {
|
||||||
|
metadata {
|
||||||
|
labels = local.labels
|
||||||
|
}
|
||||||
|
spec {
|
||||||
|
# Runs as the image's coder user (uid 1000), which has passwordless
|
||||||
|
# sudo. The KasmVNC module and the dotfiles modules (zsh, docker)
|
||||||
|
# need it for apt; the namespace enforces PSA baseline, which allows it.
|
||||||
|
security_context {
|
||||||
|
run_as_user = 1000
|
||||||
|
fs_group = 1000
|
||||||
|
run_as_non_root = true
|
||||||
|
}
|
||||||
|
|
||||||
|
container {
|
||||||
|
name = "dev"
|
||||||
|
image = "codercom/enterprise-desktop:ubuntu-noble"
|
||||||
|
image_pull_policy = "IfNotPresent"
|
||||||
|
command = ["sh", "-c", coder_agent.main.init_script]
|
||||||
|
env {
|
||||||
|
name = "CODER_AGENT_TOKEN"
|
||||||
|
value = coder_agent.main.token
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
requests = {
|
||||||
|
cpu = "500m"
|
||||||
|
memory = "1Gi"
|
||||||
|
}
|
||||||
|
limits = {
|
||||||
|
cpu = data.coder_parameter.cpu.value
|
||||||
|
memory = "${data.coder_parameter.memory.value}Gi"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
volume_mount {
|
||||||
|
mount_path = "/home/coder"
|
||||||
|
name = "home"
|
||||||
|
}
|
||||||
|
# The default 64 MiB /dev/shm crashes browsers inside the desktop.
|
||||||
|
volume_mount {
|
||||||
|
mount_path = "/dev/shm"
|
||||||
|
name = "dshm"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
volume {
|
||||||
|
name = "home"
|
||||||
|
persistent_volume_claim {
|
||||||
|
claim_name = kubernetes_persistent_volume_claim_v1.home.metadata[0].name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
volume {
|
||||||
|
name = "dshm"
|
||||||
|
empty_dir {
|
||||||
|
medium = "Memory"
|
||||||
|
size_limit = "1Gi"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
affinity {
|
||||||
|
pod_anti_affinity {
|
||||||
|
preferred_during_scheduling_ignored_during_execution {
|
||||||
|
weight = 1
|
||||||
|
pod_affinity_term {
|
||||||
|
topology_key = "kubernetes.io/hostname"
|
||||||
|
label_selector {
|
||||||
|
match_expressions {
|
||||||
|
key = "app.kubernetes.io/name"
|
||||||
|
operator = "In"
|
||||||
|
values = ["coder-workspace"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user