Skip to content

Commit a0feac8

Browse files
authored
feat(desktop): switch from KDE Plasma to GNOME
2 parents 3c9c04b + 3dd1bf9 commit a0feac8

11 files changed

Lines changed: 150 additions & 42 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ The installer generates `hosts/<hostname>/{default.nix,local.nix,facter.json}`,
7575
## Prebuilt images (The Box™ without `install.sh`)
7676
7777
Sometimes you don't want to run the installer; you just want The Box™. Two
78-
image flavours build the *exact same* configured system — KDE Plasma, the Coder
78+
image flavours build the *exact same* configured system — GNOME, the Coder
7979
server, k3s, Podman, the bundled templates — with admin bootstrap and template
8080
deploy happening on boot just like a real install. Neither is an installer.
8181

‎agents.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ sudo k3s kubectl --kubeconfig /etc/rancher/k3s/k3s.yaml ...
2929
# Service, package, or config changes — safe, non-destructive:
3030
cd /etc/nixos-repo && sudo nixos-rebuild switch --flake /etc/nixos-repo
3131

32-
# Desktop stack (KDE, SDDM, Xorg, Wayland) — must reboot:
32+
# Desktop stack (GNOME, GDM, Wayland) — must reboot:
3333
cd /etc/nixos-repo && sudo nixos-rebuild boot --flake /etc/nixos-repo && sudo reboot
3434
```
3535

@@ -40,7 +40,7 @@ it and use `--flake .`) — see the `/etc/nixos` pitfall below.
4040

4141
`nixos-rebuild switch` triggers the `coder-template-sync` activation script, which runs `terraform apply` in `coderd/` and pushes any template changes to Coder. The `/etc/coder/session-token` it needs is populated automatically by `coder-init-admin.service` on first boot, so this just works post-install.
4242

43-
> **Note:** Earlier versions of this file said "never use `nixos-rebuild switch`". That was written when this box ran a KDE desktop as the primary interface and switch could corrupt an active Plasma session. For backend service/package changes — which is most of what we do — `switch` is fine. Only use `boot + reboot` if you're changing KDE, SDDM, Xorg, or display stack config.
43+
> **Note:** Earlier versions of this file said "never use `nixos-rebuild switch`". That was written when this box ran a desktop as the primary interface and switch could corrupt an active session. For backend service/package changes — which is most of what we do — `switch` is fine. Only use `boot + reboot` if you're changing GNOME, GDM, Wayland, or display stack config.
4444
4545
## Tailscale
4646

@@ -199,7 +199,7 @@ sudo k3s kubectl describe pod -n coder-workspaces <pod-name>
199199
- **`coder` binary path** — the binary is in PATH via NixOS environment; don't hardcode nix store paths in scripts (they change with every package update).
200200
- **`--flake /etc/nixos` fails** — `/etc/nixos` is a plain dir holding only a `flake.nix` *symlink* into `/etc/nixos-repo`. Nix follows the symlink into the store but can't find the sibling files (configuration.nix, hosts/, nixos/), dying with `path '/nix/store/...-source/etc/nixos-repo/flake.nix' does not exist`. Always rebuild against the real tree: `--flake /etc/nixos-repo` (or `cd /etc/nixos-repo && nixos-rebuild switch --flake .`).
201201
- **`Git tree '/etc/nixos-repo' is dirty` warning** — harmless. `hosts/<host>/{local.nix,facter.json}` are gitignored and intent-to-added by the installer, so the tree always reads "dirty". After editing them, re-mark intent-to-add so the flake sees them: `sudo git -C /etc/nixos-repo add --intent-to-add -f hosts/<host>/local.nix hosts/<host>/facter.json`.
202-
- **ScreenConnect blank screen** — if SC shows a black/blank view, SDDM may have fallen back to Wayland. Ensure `services.displayManager.sddm.wayland.enable = false` and `services.displayManager.defaultSession = "plasmax11"` in `configuration.nix`, then `nixos-rebuild boot && reboot`.
202+
- **ScreenConnect blank screen** — the box now runs GNOME on Wayland (GDM), and GNOME 49 dropped the Xorg session, so there is no X11 desktop to fall back to. ScreenConnect reaches `DISPLAY=:0` through XWayland (see `nixos/screenconnect.nix`) but **cannot screen-capture the Wayland compositor** through it, so the remote view may be black/blank. Capturing the GNOME session needs a Wayland-aware path (PipeWire/portal, e.g. `gnome-remote-desktop`); the X11 agent will connect but not mirror the desktop.
203203

204204
## Wildcard App Access (TODO)
205205

‎configuration.nix‎

Lines changed: 90 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,26 @@ let
4242
}
4343
}
4444
'';
45+
46+
# Session-startup launcher: open the local Coder dashboard in Firefox.
47+
# Wired up as an XDG autostart entry (see environment.etc below) on the
48+
# INSTALLED box only — the installer ISO disables it (installer/iso.nix), and
49+
# its coder-redirect/coder services are off there anyway. coder-redirect binds
50+
# :80 only AFTER it has discovered the *.try.coder.app tunnel URL and serves a
51+
# 302 to it, so opening http://127.0.0.1 before then would just show a
52+
# connection-refused page. Poll :80 (up to ~2 min) before launching so the
53+
# first paint is the dashboard, not an error. `firefox` resolves from the
54+
# session PATH (the wrapped build that programs.firefox.enable installs).
55+
openDashboard = pkgs.writeShellScript "coder-box-open-dashboard" ''
56+
export PATH=/run/current-system/sw/bin:$PATH
57+
for _ in $(seq 1 60); do
58+
if ${pkgs.curl}/bin/curl -s -o /dev/null --max-time 2 http://127.0.0.1; then
59+
break
60+
fi
61+
sleep 2
62+
done
63+
exec firefox http://127.0.0.1
64+
'';
4565
in
4666
{
4767
# Per-host modules (hardware detection via facter, disk layout via disko,
@@ -141,7 +161,7 @@ in
141161
# LAN and a *.try.coder.app tunnel. Suspending or hibernating drops the
142162
# NIC, so the machine silently falls off the network (no mDNS, no SSH,
143163
# tunnel dies) until someone physically wakes it. The shipped image runs a
144-
# KDE desktop, which exposes Sleep/Hibernate actions, and a stray
164+
# GNOME desktop, which exposes Sleep/Hibernate actions, and a stray
145165
# `systemctl suspend` / `systemctl hibernate` (or the matching D-Bus call)
146166
# would do the same. Mask the suspend, hibernate, and hybrid-sleep targets
147167
# so all of those paths become a no-op.
@@ -199,17 +219,78 @@ in
199219
LC_TIME = "en_US.UTF-8";
200220
};
201221

202-
# ── Desktop: KDE Plasma 6 ─────────────────────────────────────────────────
222+
# ── Desktop: GNOME ────────────────────────────────────────────────────────
223+
# GNOME 49 (the version in the pinned nixpkgs-25.11) is Wayland-only: the
224+
# GNOME-on-Xorg session was dropped upstream (gnome-session now advertises
225+
# `providedSessions = [ "gnome" ]` — no `gnome-xorg`), so there is no X11
226+
# GNOME session to fall back to. GDM runs on Wayland by default and we keep
227+
# it that way; there is therefore no `defaultSession`/`wayland.enable`
228+
# plumbing as the SDDM/Plasma config (KDE Plasma 6 on Xorg) had before it.
229+
# `services.xserver.enable` is still set so XWayland is available for legacy
230+
# X11 clients (e.g. the optional ScreenConnect agent — see screenconnect.nix).
203231
services.xserver.enable = true;
204-
services.displayManager.sddm.enable = true;
205-
services.displayManager.sddm.wayland.enable = false;
206-
services.displayManager.defaultSession = "plasmax11";
207-
services.desktopManager.plasma6.enable = true;
232+
services.displayManager.gdm.enable = true;
233+
services.desktopManager.gnome.enable = true;
208234
services.xserver.xkb = {
209235
layout = "us";
210236
variant = "";
211237
};
212238

239+
# ── Skip GNOME's first-run welcome experience ─────────────────────────────
240+
# Out of the box GNOME greets every new login with two things this appliance
241+
# doesn't want:
242+
# 1. gnome-tour — the "Welcome to NixOS / Take the Tour" dialog. GNOME
243+
# Shell auto-launches it from its .desktop file on first login (it ships
244+
# in the default GNOME package set), so the only way to suppress it is to
245+
# drop the package via environment.gnome.excludePackages.
246+
# 2. The Activities overview (the app/window grid) shown at session
247+
# startup. GNOME has no gsetting to disable this, so we ship the
248+
# "no-overview" Shell extension and enable it for the session, which
249+
# lands you on the bare desktop instead of the overview.
250+
# (the no-overview extension package is added to environment.systemPackages
251+
# in the shared package list below.)
252+
environment.gnome.excludePackages = [ pkgs.gnome-tour ];
253+
programs.dconf.profiles.user.databases = [
254+
{
255+
settings = {
256+
"org/gnome/shell".enabled-extensions = [ "no-overview@fthx" ];
257+
258+
# ── Pinned dash icons: match the old KDE Plasma 6 taskbar ───────────
259+
# Plasma 6 shipped its Icons-Only Task Manager pre-pinned with three
260+
# launchers: System Settings, the file manager (Dolphin), and the web
261+
# browser (Firefox). Reproduce that exact set as the GNOME dash
262+
# favourites so the box looks the same after the KDE→GNOME switch.
263+
# GNOME's own NixOS default (Epiphany/Geary/Calendar/Music/Nautilus)
264+
# is mostly apps we don't even install, so override it outright.
265+
# System Settings → org.gnome.Settings (gnome-control-center)
266+
# Dolphin → org.gnome.Nautilus (GNOME Files)
267+
# Firefox → firefox
268+
"org/gnome/shell".favorite-apps = [
269+
"org.gnome.Settings.desktop"
270+
"org.gnome.Nautilus.desktop"
271+
"firefox.desktop"
272+
];
273+
};
274+
}
275+
];
276+
277+
# ── Open the Coder dashboard on login ──────────────────────────────────────
278+
# On the installed box the coderbox user autologins into GNOME; open Firefox
279+
# on http://127.0.0.1 (the local coder-redirect → tunnel URL) at session
280+
# start so the dashboard is up and ready. See openDashboard in the let block
281+
# for the wait-for-:80 logic. The installer ISO overrides this away
282+
# (installer/iso.nix) since it has no running Coder stack.
283+
environment.etc."xdg/autostart/coder-box-open-dashboard.desktop".text = ''
284+
[Desktop Entry]
285+
Type=Application
286+
Name=Open Coder Dashboard
287+
Comment=Open the local Coder dashboard in Firefox on login
288+
Exec=${openDashboard}
289+
Terminal=false
290+
X-GNOME-Autostart-enabled=true
291+
OnlyShowIn=GNOME;
292+
'';
293+
213294
# ── Audio ─────────────────────────────────────────────────────────────────
214295
services.pulseaudio.enable = false;
215296
security.rtkit.enable = true;
@@ -301,6 +382,9 @@ in
301382
terraform
302383
gh
303384
vlc
385+
# GNOME Shell extension that suppresses the Activities overview shown at
386+
# session startup (enabled via programs.dconf above). See the Desktop block.
387+
gnomeExtensions.no-overview
304388
];
305389

306390
nix.settings.experimental-features = [

‎hosts/coder-thinkcentre/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,5 @@
5353
| Nix | 2.31.4 |
5454
| Hostname | coder-thinkcentre |
5555
| User | coderbox |
56-
| Desktop | KDE Plasma 6 / SDDM |
56+
| Desktop | GNOME (Wayland) / GDM |
5757
| Repo path | /etc/nixos-repo |

‎hosts/incus-vm/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ required.
2929
virtio drivers) — skip this for bare-metal
3030
- `systemd-networkd` DHCP on `enp5s0` (the virtio NIC Incus assigns to VMs on
3131
both x86_64 and aarch64)
32-
- Disables the KDE / PipeWire / printing / Avahi stack — a headless host only
32+
- Disables the GNOME / PipeWire / printing / Avahi stack — a headless host only
3333
needs Coder + PostgreSQL
3434

3535
`default.nix` is the per-host entrypoint that imports `incus-vm.nix`, your

‎hosts/incus-vm/incus-vm.nix‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# agents, virtio drivers, auto-resize, systemd-boot).
99
# - Switches networking to systemd-networkd + DHCP on enp5s0 (the
1010
# virtio NIC Incus assigns to VMs on both x86_64 and aarch64).
11-
# - Disables the full desktop stack (KDE, PipeWire, printing, Avahi)
11+
# - Disables the full desktop stack (GNOME, PipeWire, printing, Avahi)
1212
# that configuration.nix enables by default — a headless VM only needs
1313
# the Coder server + PostgreSQL.
1414
#
@@ -50,8 +50,8 @@
5050
# The full desktop stack from configuration.nix is not needed in a VM.
5151
# Use lib.mkForce to win against the lib.mkDefault values set there.
5252
services.xserver.enable = lib.mkForce false;
53-
services.displayManager.sddm.enable = lib.mkForce false;
54-
services.desktopManager.plasma6.enable = lib.mkForce false;
53+
services.displayManager.gdm.enable = lib.mkForce false;
54+
services.desktopManager.gnome.enable = lib.mkForce false;
5555
services.pipewire.enable = lib.mkForce false;
5656
services.pulseaudio.enable = lib.mkForce false;
5757
security.rtkit.enable = lib.mkForce false;

‎install.sh‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,7 @@ ln -sf /etc/nixos-repo/flake.nix /mnt/etc/nixos/flake.nix
767767
# wrong path) and the chroot `activate` fails: "No such file or directory".
768768
# (NOTE: the target host is `coder-nixos`, a *different* system than the
769769
# image's own host, so its toplevel isn't pre-realised — it must be built.
770-
# The build is cheap: every heavy dependency (KDE, Coder, k3s, …) is reused
770+
# The build is cheap: every heavy dependency (GNOME, Coder, k3s, …) is reused
771771
# from the squashfs; only the few host-specific derivations are new.)
772772
#
773773
# So: build the toplevel, copy its full closure into /mnt with

‎nixos/_images/appliance/iso.nix‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#
33
# Turns the shared Coder box configuration into a bootable *ephemeral* appliance
44
# ISO that runs entirely from the USB/CD + RAM, with no disk install. Booting it
5-
# gives the same system the on-disk install produces (KDE, Coder server, k3s,
5+
# gives the same system the on-disk install produces (GNOME, Coder server, k3s,
66
# Podman, the bundled templates, all started automatically) — but the root
77
# filesystem is a squashfs + tmpfs overlay, so all state is discarded on
88
# reboot. For a *persistent* appliance (state survives reboots) build the

‎nixos/_images/box-turnkey.nix‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ in
173173
nix.registry.nixpkgs.flake = inputs.nixpkgs;
174174

175175
# ── Login + Coder admin bootstrap ────────────────────────────────────────────
176-
# Autologin drops straight into the Plasma desktop, mirroring a
176+
# Autologin drops straight into the GNOME desktop, mirroring a
177177
# freshly-installed, configured box.
178178
services.displayManager.autoLogin = {
179179
enable = true;
@@ -187,7 +187,7 @@ in
187187
"networkmanager"
188188
"wheel"
189189
];
190-
packages = [ pkgs.kdePackages.kate ];
190+
packages = [ pkgs.gnome-text-editor ];
191191
initialPassword = "PleaseChangeMe1234";
192192
};
193193

‎nixos/_images/installer/iso.nix‎

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ let
2828
# Short form for the boot-menu label (full 40-char hashes are unwieldy there).
2929
boxRevShort = if boxRev == "unknown" then "unknown" else builtins.substring 0 12 boxRev;
3030

31-
# Launcher run inside the preopened Konsole: cd into the baked repo, run the
31+
# Launcher run inside the preopened terminal: cd into the baked repo, run the
3232
# installer as root (passwordless sudo is configured in configuration.nix),
3333
# and — whatever happens — drop the user into an interactive bash shell so a
3434
# failed install leaves them at a prompt to inspect/retry instead of a dead
@@ -55,7 +55,7 @@ let
5555
fi
5656
echo
5757
# Interactive login-ish shell so the user can debug/retry. exec so closing
58-
# the shell closes Konsole.
58+
# the shell closes the terminal window.
5959
exec ${pkgs.bashInteractive}/bin/bash -i
6060
'';
6161
in
@@ -94,33 +94,46 @@ in
9494
# build (e.g. coder-box-installer-x86_64-linux-pr-fix-the-thing.iso).
9595
image.baseName = lib.mkForce "coder-box-installer-${pkgs.stdenv.hostPlatform.system}${config.coderBox.prFileSuffix}";
9696

97-
# ── Auto-launch a full-screen Konsole that runs the installer ──────────────
98-
# box-turnkey.nix autologins straight into the Plasma (X11) desktop. For the
99-
# installer we want the install to start on its own: a system-wide XDG
100-
# autostart entry opens Konsole full-screen on session start and runs the
101-
# installer launcher (`konsole -e <launcher>`), which `sudo ./install.sh`s
102-
# and drops to an interactive bash shell if it fails.
103-
environment.systemPackages = [ pkgs.kdePackages.konsole ];
104-
environment.etc."xdg/autostart/coder-box-installer-konsole.desktop".text = ''
97+
# ── Auto-launch a full-screen terminal that runs the installer ─────────────
98+
# box-turnkey.nix autologins straight into the GNOME (Wayland) desktop. For
99+
# the installer we want the install to start on its own: a system-wide XDG
100+
# autostart entry opens GNOME Terminal full-screen on session start and runs
101+
# the installer launcher (`gnome-terminal -- <launcher>`), which
102+
# `sudo ./install.sh`s and drops to an interactive bash shell if it fails.
103+
environment.systemPackages = [ pkgs.gnome-terminal ];
104+
environment.etc."xdg/autostart/coder-box-installer-terminal.desktop".text = ''
105105
[Desktop Entry]
106106
Type=Application
107107
Name=Coder Box Installer Console
108108
Comment=Run the coder/box installer in a full-screen terminal
109-
Exec=${pkgs.kdePackages.konsole}/bin/konsole --fullscreen --workdir /etc/nixos-repo -e ${installerLauncher}
109+
Exec=${pkgs.gnome-terminal}/bin/gnome-terminal --full-screen --working-directory=/etc/nixos-repo -- ${installerLauncher}
110110
Terminal=false
111111
X-GNOME-Autostart-enabled=true
112-
OnlyShowIn=KDE;
112+
OnlyShowIn=GNOME;
113113
'';
114114

115+
# The installed box autostarts Firefox on the Coder dashboard
116+
# (configuration.nix → coder-box-open-dashboard.desktop). The installer has
117+
# no running Coder stack (coder/coder-redirect are disabled below), so drop
118+
# that autostart here — the installer session should only run the installer
119+
# console above.
120+
environment.etc."xdg/autostart/coder-box-open-dashboard.desktop".enable = lib.mkForce false;
121+
115122
# ── Never prompt for a password to get in ──────────────────────────────────
116123
# Login is already passwordless (box-turnkey coderbox autologin + passwordless
117-
# sudo). Disable KDE's screen locker (idle auto-lock / lock-on-resume) so the
118-
# installer is never locked. (The appliance keeps the default locker.)
119-
environment.etc."xdg/kscreenlockerrc".text = ''
120-
[Daemon]
121-
Autolock=false
122-
LockOnResume=false
123-
'';
124+
# sudo). Disable GNOME's screen lock / idle blanking (idle auto-lock and the
125+
# idle-delay screensaver) so the installer is never locked or blanked mid
126+
# install. Shipped as a system dconf default for the autologin user. (The
127+
# appliance keeps the default locker.)
128+
programs.dconf.profiles.user.databases = [
129+
{
130+
settings = {
131+
"org/gnome/desktop/screensaver".lock-enabled = false;
132+
"org/gnome/desktop/session".idle-delay = lib.gvariant.mkUint32 0;
133+
"org/gnome/settings-daemon/plugins/power".sleep-inactive-ac-type = "nothing";
134+
};
135+
}
136+
];
124137

125138
# ── Installer ergonomics ───────────────────────────────────────────────────
126139
# `sudo ./install.sh` from the baked /etc/nixos-repo works because the script

0 commit comments

Comments
 (0)