coder/desktop: KDE Plasma instead of XFCE

Add image/Dockerfile (published as
git.nic-oconnor.com/public/coder-desktop-kde:noble): enterprise-desktop
noble plus KDE Plasma and KasmVNC 1.5.0, baked in so starts skip apt.
It carries three fixes found testing in the cluster:

- kwin-x11 installed explicitly (only a Recommends; without it Plasma
  runs with no window manager),
- startkde -> startplasma-x11 symlink, since KasmVNC's -select-de kde
  checks for startkde and Plasma 5.20+ renamed it,
- KWin compositing off by default (no GPU; it only adds VNC lag).

The template now uses that image with desktop_environment = "kde" and
pulls Always, and drops the XFCE .xsessionrc seeding.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-18 15:02:27 -04:00
co-authored by Claude Opus 5
parent 1524fa8203
commit 2919012206
4 changed files with 88 additions and 31 deletions
+1 -1
View File
@@ -97,7 +97,7 @@ Already have a clone? Just run it directly:
Not run by `bootstrap.sh`: Not run by `bootstrap.sh`:
- `coder/desktop/` — Coder template for coder.nic-oconnor.com: an XFCE - `coder/desktop/` — Coder template for coder.nic-oconnor.com: a KDE Plasma
desktop (KasmVNC) pod on the homelab cluster that applies this repo via the desktop (KasmVNC) pod on the homelab cluster that applies this repo via the
dotfiles module. Push with `coder templates push desktop -d coder/desktop`; dotfiles module. Push with `coder templates push desktop -d coder/desktop`;
see its README. see its README.
+40 -12
View File
@@ -1,7 +1,7 @@
# coder/desktop — Coder desktop template # coder/desktop — Coder desktop template
A Coder template for <https://coder.nic-oconnor.com>: an XFCE desktop in the A Coder template for <https://coder.nic-oconnor.com>: a KDE Plasma desktop in
browser (the [KasmVNC module](https://registry.coder.com/modules/coder/kasmvnc)) 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 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). [dotfiles module](https://registry.coder.com/modules/coder/dotfiles).
@@ -15,14 +15,42 @@ coder templates push desktop -d coder/desktop -y
The in-cluster provisioner uses the `coder` service account, whose workspace The in-cluster provisioner uses the `coder` service account, whose workspace
Role is bound in the `coder` namespace — which is where workspaces go. Role is bound in the `coder` namespace — which is where workspaces go.
## The image
`image/Dockerfile` builds `git.nic-oconnor.com/public/coder-desktop-kde:noble`:
`codercom/enterprise-desktop:ubuntu-noble` plus KDE Plasma (with Dolphin,
Konsole, Kate) and KasmVNC. Both are baked in because only `/home/coder` is
persisted, so installing them at start time would cost minutes on every
start. The package is public on Gitea, so the cluster pulls it without a
secret. Rebuild after changing the Dockerfile:
```sh
docker build -t git.nic-oconnor.com/public/coder-desktop-kde:noble coder/desktop/image
docker push git.nic-oconnor.com/public/coder-desktop-kde:noble
```
The pod pulls with `Always`, so workspaces pick the new image up on their
next start.
Workarounds the Dockerfile carries:
- `kwin-x11` is installed explicitly. It is only a Recommends of
`kde-plasma-desktop`, and without it Plasma starts with no window manager.
- `/usr/local/bin/startkde -> startplasma-x11`. KasmVNC's `-select-de kde`
(what the module runs) checks for `startkde`, which Plasma 5.20+ renamed,
and otherwise fails with "Desktop Environment not installed".
- KWin compositing is off in `/etc/xdg/kwinrc`: there is no GPU, and
software compositing over VNC only adds lag. Turn it back on in System
Settings if you want it.
## What a workspace gets ## What a workspace gets
- `codercom/enterprise-desktop:ubuntu-noble`: XFCE, git, python3, and the - The image above, running as the `coder` user (uid 1000) with passwordless
`coder` user (uid 1000) with passwordless sudo. sudo.
- A PVC (`truenas-iscsi`, default 30 GiB) at `/home/coder`. Everything the - A PVC (`truenas-iscsi`, default 30 GiB) at `/home/coder`. Everything the
dotfiles install into `$HOME` survives stops; system packages (zsh, docker dotfiles install into `$HOME` survives stops, and so do the Plasma settings.
CLI, KasmVNC) are reinstalled on each start because the root filesystem System packages the dotfiles add (zsh, the docker CLI) are reinstalled on
is not persisted. each start.
- The KasmVNC app, path-based (`subdomain = false`) because the deployment has - The KasmVNC app, path-based (`subdomain = false`) because the deployment has
no wildcard access URL. no wildcard access URL.
- A "Dotfiles" startup script: clones this repo into - A "Dotfiles" startup script: clones this repo into
@@ -42,9 +70,9 @@ bootstrap still applies everything else; `secrets`, `mcp-config` and `hermes`
are reported as not completed. Fix that later from a workspace terminal with are reported as not completed. Fix that later from a workspace terminal with
`~/.config/coderv2/dotfiles/secrets/install.sh`, then refresh the dotfiles. `~/.config/coderv2/dotfiles/secrets/install.sh`, then refresh the dotfiles.
## Why these pins ## Why noble
- `ubuntu-noble`, not `ubuntu`: the `ubuntu` tag is now 26.04 (resolute), and The `enterprise-desktop:ubuntu` tag is now 26.04 (resolute), and KasmVNC
KasmVNC publishes no resolute package, so the module's install 404s. publishes no resolute package. The image is pinned to noble with KasmVNC
- `kasm_version = "1.5.0"`: the newest KasmVNC release, which has a noble 1.5.0; revisit once KasmVNC ships a resolute `.deb`, and keep
build. Revisit both once KasmVNC ships a resolute `.deb`. `kasm_version` in `main.tf` in step with `KASMVNC_VERSION`.
+34
View File
@@ -0,0 +1,34 @@
# coder/desktop/image — the workspace image: Coder's desktop base with KDE
# Plasma and KasmVNC baked in, so a start does not spend minutes in apt (the
# root filesystem is not persisted; only /home/coder is).
#
# docker build -t git.nic-oconnor.com/public/coder-desktop-kde:noble coder/desktop/image
# docker push git.nic-oconnor.com/public/coder-desktop-kde:noble
#
# noble, not resolute: KasmVNC publishes no resolute package yet.
FROM codercom/enterprise-desktop:ubuntu-noble
ARG KASMVNC_VERSION=1.5.0
USER root
# kwin-x11 is only a Recommends of kde-plasma-desktop, so --no-install-recommends
# drops it and Plasma comes up with no window manager (no title bars).
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
kde-plasma-desktop kwin-x11 dolphin konsole kate plasma-nm breeze-gtk-theme \
dbus-x11 libdatetime-perl \
&& curl -fsSL -o /tmp/kasmvnc.deb \
"https://github.com/kasmtech/KasmVNC/releases/download/v${KASMVNC_VERSION}/kasmvncserver_noble_${KASMVNC_VERSION}_amd64.deb" \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends /tmp/kasmvnc.deb \
&& rm -f /tmp/kasmvnc.deb /home/coder/.xsessionrc \
&& rm -rf /var/lib/apt/lists/*
# KasmVNC's `-select-de kde` (what the Coder kasmvnc module runs) looks for a
# `startkde` binary, which Plasma 5.20+ renamed to startplasma-x11.
RUN ln -s /usr/bin/startplasma-x11 /usr/local/bin/startkde
# No GPU in the pod: software compositing over VNC only adds lag. Users can
# still turn it on in System Settings (~/.config/kwinrc overrides this).
RUN printf '[Compositing]\nEnabled=false\n' > /etc/xdg/kwinrc
USER coder
+13 -18
View File
@@ -1,4 +1,4 @@
# coder/desktop — Coder template: an XFCE desktop in the browser (KasmVNC) # coder/desktop — Coder template: a KDE Plasma desktop in the browser (KasmVNC)
# on the homelab Kubernetes cluster, set up from this dotfiles repo. # on the homelab Kubernetes cluster, set up from this dotfiles repo.
# #
# Push it with: coder templates push desktop -d coder/desktop # Push it with: coder templates push desktop -d coder/desktop
@@ -174,21 +174,14 @@ resource "coder_agent" "main" {
arch = data.coder_provisioner.me.arch arch = data.coder_provisioner.me.arch
os = "linux" os = "linux"
# The home PVC hides whatever the image put in /home/coder, so seed it on # The home PVC hides whatever the image put in /home/coder, so seed it
# first start: the skel dotfiles, plus the image's .xsessionrc (XFCE session # with the skel dotfiles on first start.
# env that KasmVNC's X session reads).
startup_script = <<-EOT startup_script = <<-EOT
set -e set -e
if [ ! -f ~/.init_done ]; then if [ ! -f ~/.init_done ]; then
cp -rT /etc/skel ~ || true cp -rT /etc/skel ~ || true
touch ~/.init_done touch ~/.init_done
fi 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 EOT
env = merge({ env = merge({
@@ -235,9 +228,10 @@ module "dotfiles" {
manual_update = true manual_update = true
} }
# XFCE is in the image; the module installs KasmVNC and serves it on :6800. # KDE Plasma and KasmVNC are baked into the image (image/Dockerfile), so the
# kasm_version: KasmVNC ships a .deb per Ubuntu codename, and the module's # module skips its install and just configures and starts KasmVNC on :6800.
# default (1.4.0) is older than we need; 1.5.0 has noble. # kasm_version: only used if the image lacks KasmVNC; keep it in step with
# KASMVNC_VERSION in the Dockerfile.
# subdomain = false: the deployment has no wildcard access URL, so the app # subdomain = false: the deployment has no wildcard access URL, so the app
# is served path-based under coder.nic-oconnor.com. # is served path-based under coder.nic-oconnor.com.
module "kasmvnc" { module "kasmvnc" {
@@ -245,7 +239,7 @@ module "kasmvnc" {
source = "registry.coder.com/coder/kasmvnc/coder" source = "registry.coder.com/coder/kasmvnc/coder"
version = "1.3.0" version = "1.3.0"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
desktop_environment = "xfce" desktop_environment = "kde"
kasm_version = "1.5.0" kasm_version = "1.5.0"
subdomain = false subdomain = false
} }
@@ -299,8 +293,9 @@ resource "kubernetes_deployment_v1" "main" {
} }
spec { spec {
# Runs as the image's coder user (uid 1000), which has passwordless # Runs as the image's coder user (uid 1000), which has passwordless
# sudo. The KasmVNC module and the dotfiles modules (zsh, docker) # sudo. The KasmVNC module needs it to write /etc/kasmvnc, and the
# need it for apt; the namespace enforces PSA baseline, which allows it. # dotfiles modules (zsh, docker) for apt; the namespace enforces PSA
# baseline, which allows it.
security_context { security_context {
run_as_user = 1000 run_as_user = 1000
fs_group = 1000 fs_group = 1000
@@ -309,8 +304,8 @@ resource "kubernetes_deployment_v1" "main" {
container { container {
name = "dev" name = "dev"
image = "codercom/enterprise-desktop:ubuntu-noble" image = "git.nic-oconnor.com/public/coder-desktop-kde:noble"
image_pull_policy = "IfNotPresent" image_pull_policy = "Always"
command = ["sh", "-c", coder_agent.main.init_script] command = ["sh", "-c", coder_agent.main.init_script]
env { env {
name = "CODER_AGENT_TOKEN" name = "CODER_AGENT_TOKEN"