Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
f065018
refactor: reorg image modules into nixos/_images/{_base,_appliance,_i…
phorcys420 Jun 10, 2026
7cb633a
refactor: drop underscore from nixos/_images subfolders (_base/_appli…
phorcys420 Jun 10, 2026
356cdc5
make: put installer/iso first (default goal for bare `make`)
phorcys420 Jun 10, 2026
5819fb2
installer: auto-launch full-screen Konsole on login, cwd /etc/nixos-repo
phorcys420 Jun 10, 2026
3e03de0
installer: disable KDE screen locker so it never prompts for a password
phorcys420 Jun 10, 2026
22ecf1e
installer: make install.sh work on the read-only ISO (clone upstream …
phorcys420 Jun 10, 2026
c5dd548
installer: use the baked repo offline (no re-download) + git init for…
phorcys420 Jun 10, 2026
b8f7d05
installer: anchor the writable install copy to upstream (clean git pu…
phorcys420 Jun 10, 2026
a750ad7
installer: simplify install.sh relocation — permission check, no git …
phorcys420 Jun 10, 2026
8f5be99
installer: exclude zram from disk picker; auto-run install.sh in Kons…
phorcys420 Jun 10, 2026
80e146c
install.sh: copy pre-baked system closure into /mnt (fix chroot activ…
phorcys420 Jun 11, 2026
7ac3deb
install.sh: build+copy the target closure into /mnt on box images (re…
phorcys420 Jun 11, 2026
fb1a9f6
installer: show build revision in boot menu + at install start
phorcys420 Jun 11, 2026
78ac95f
installer: disable Coder box services in the live installer ISO
phorcys420 Jun 11, 2026
cc5670d
installer: fix '(unknown)' build revision in boot menu
phorcys420 Jun 11, 2026
676a1f9
gitignore: stop ignoring *.tar.gz
phorcys420 Jun 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,14 @@ out/
*.iso
*.qcow2
*.raw

# Let users drop their OWN hosts into hosts/ without git noise: ignore the
# whole hosts/ directory, then un-ignore only the hosts we manage centrally
# (the ones we ship + write to). A user-added hosts/<their-host>/ stays
# untracked; our managed hosts remain version-controlled as usual.
hosts/*
!hosts/_appliance-disk/
!hosts/_appliance_iso/
!hosts/_installer-iso/
!hosts/coder-thinkcentre/
!hosts/qemu-arm64/
31 changes: 27 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Coder box — appliance image build targets.
# Coder box — image build targets.
#
# An "appliance" is the box prebuilt as a bootable image (no install.sh):
# it boots straight into the fully-configured Coder box. Three formats:
Expand All @@ -7,12 +7,19 @@
# make appliance/qcow2 # disk image (persistent; boots in QEMU/libvirt)
# make appliance/raw # disk image (persistent; dd-able to a drive)
#
# Each format also takes an architecture suffix; short names are normalized to
# The "installer" is the box as an ISO that will install coder/box onto real
# hardware. For now it boots the same full GUI box as the appliance ISO; ISO only
# (no disk images):
#
# make installer/iso
#
# Each target also takes an architecture suffix; short names are normalized to
# a *-linux triple (e.g. aarch64 -> aarch64-linux):
#
# make appliance/iso/x86_64-linux
# make appliance/qcow2/aarch64-linux
# make appliance/raw/aarch64
# make installer/iso/aarch64-linux
#
# Requires Nix with flakes enabled (nix-command + flakes). All builds run on
# Linux only; cross-arch builds need a matching builder (native remote builder
Expand All @@ -26,6 +33,14 @@
NIX ?= nix
FLAKE ?= .

# Build revision injected into images (installer boot menu, /etc/coder-box-rev).
# We build through a path flakeref (getFlake (toString ./.)), which carries no
# git metadata, so self.rev/dirtyRev are empty — compute the rev here and pass
# it via the installer's `coderBox.rev` option. Full commit hash, with a -dirty
# suffix when the working tree has uncommitted changes. Empty if not a git
# checkout (the module then falls back to self.rev / "unknown").
GIT_REV := $(shell git rev-parse HEAD 2>/dev/null)$(shell git diff-index --quiet HEAD -- 2>/dev/null || echo -dirty)

# Normalize an arch token to a *-linux triple: $(call norm_arch,aarch64) -> aarch64-linux
norm_arch = $(if $(filter %-linux,$(1)),$(1),$(1)-linux)

Expand All @@ -52,10 +67,18 @@ define box_build
@mkdir -p out
$(NIX) build --impure --no-write-lock-file --print-out-paths \
--out-link 'out/$(subst /,-,$@)' --expr \
'let f = builtins.getFlake (toString ./.); in (f.nixosConfigurations.$(1).extendModules { modules = [ { nixpkgs.hostPlatform = "$(if $(4),$(call norm_arch,$(4)),$${builtins.currentSystem})"; $(3) } ]; }).config.system.build.$(2)'
'let f = builtins.getFlake (toString ./.); in (f.nixosConfigurations.$(1).extendModules { modules = [ { nixpkgs.hostPlatform = "$(if $(4),$(call norm_arch,$(4)),$${builtins.currentSystem})"; coderBox.rev = "$(GIT_REV)"; $(3) } ]; }).config.system.build.$(2)'
endef

.PHONY: appliance/iso appliance/qcow2 appliance/raw
.PHONY: installer/iso appliance/iso appliance/qcow2 appliance/raw

# installer/iso is listed first so it's the default goal (bare `make`).

# ── installer/iso — installer ISO (hosts/_installer-iso); ISO only ────────────
installer/iso:
$(call box_build,_installer-iso,isoImage,,)
installer/iso/%:
$(call box_build,_installer-iso,isoImage,,$*)

# ── appliance/iso — ephemeral appliance ISO (hosts/_appliance_iso) ───────────
appliance/iso:
Expand Down
49 changes: 38 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ NixOS configuration for Coder demo and workshop boxes.
flake.nix # entry point: nixosConfigurations.<host> per machine
flake.lock # pinned nixpkgs / disko / nixos-facter-modules
configuration.nix # shared NixOS config (all machines)
Makefile # appliance build targets: appliance/{iso,qcow2,raw}[/<arch>]
Makefile # image build targets: appliance/{iso,qcow2,raw}, installer/iso [/<arch>]
local.nix.example # template copied to hosts/<host>/local.nix by install.sh
.gitignore # ignores hosts/*/local.nix
install.sh # one-shot installer: disko + nixos-install + bake /etc/nixos-repo
Expand All @@ -39,9 +39,15 @@ nixos/
k3s-sysbox.nix # k3s + sysbox-runc runtime class
k3s-podman.nix # k3s + rootless Podman socket
screenconnect.nix # optional ScreenConnect remote access client
_appliance/ # prebuilt-appliance modules (ISO + persistent disk)
box-turnkey.nix # shared turn-key bits for appliances (login + Coder bootstrap)
live-iso.nix # ephemeral appliance ISO module (hosts/_appliance_iso)
_images/ # prebuilt-image modules (appliance + installer)
box-turnkey.nix # shared turn-key Coder box (login + Coder bootstrap); all image hosts
base/ # primitives shared by every image
hardware.nix # all-hardware (boot on arbitrary hardware)
iso.nix # ISO mechanics (iso-image.nix, EFI/BIOS/USB bootable, bootloader)
appliance/
iso.nix # appliance ISO module (hosts/_appliance_iso)
installer/
iso.nix # installer ISO module (hosts/_installer-iso)
pkgs/
coder.nix # custom Coder server package
coderd-provider.nix # terraform-provider-coderd package
Expand All @@ -54,9 +60,11 @@ hosts/
templates/
nook-android/ # Workspace: build trmnl-nook-simple-touch APK
_appliance_iso/ # `_appliance_iso` host: ephemeral appliance ISO (no disk install)
default.nix # imports nixos/_appliance/live-iso.nix (no disko/facter/hardware-config)
default.nix # imports nixos/_images/appliance/iso.nix (no disko/facter/hardware-config)
_appliance-disk/ # `_appliance-disk` host: persistent qcow2/raw disk image
default.nix # imports disko-standard.nix + nixos/_appliance/box-turnkey.nix
default.nix # imports disko-standard.nix + nixos/_images/box-turnkey.nix
_installer-iso/ # `_installer-iso` host: installer ISO (ISO only; installs box onto hardware)
default.nix # imports nixos/_images/installer/iso.nix
coderd/
main.tf # manages all Coder templates via coderd Terraform provider
templates/
Expand All @@ -74,7 +82,7 @@ right config on the running box. Adding a new host means creating a host
folder, no flake.nix edit. The installer does this for you.

Hosts whose folder name starts with an underscore (`_appliance_iso`,
`_appliance-disk`) are image/appliance builds, not per-machine installs: they
`_appliance-disk`, `_installer-iso`) are image builds, not per-machine installs: they
do **not** get the folder-name hostname and instead inherit the central
default `networking.hostName = "coder-box"` (set in `configuration.nix`).

Expand Down Expand Up @@ -155,8 +163,8 @@ image at `out/appliance-raw/coder-box-appliance-*.raw` (or
`out/appliance-qcow2/coder-box-appliance-*.qcow2`). All names carry the arch,
e.g. `coder-box-appliance-aarch64-linux.iso`.

The turn-key login + Coder admin bootstrap shared by both flavours live in
[`nixos/_appliance/box-turnkey.nix`](nixos/_appliance/box-turnkey.nix): autologin to the `coderbox`
The turn-key login + Coder admin bootstrap shared by all image flavours live in
[`nixos/_images/box-turnkey.nix`](nixos/_images/box-turnkey.nix): autologin to the `coderbox`
desktop, and admin `admin@coder.com` / `PleaseChangeMe1234`. Coder comes up at
`http://<hostname>.local:3000` (or the `*.try.coder.app` tunnel URL in
`/etc/motd`). Change these before sharing an image by dropping a gitignored
Expand All @@ -167,7 +175,7 @@ desktop, and admin `admin@coder.com` / `PleaseChangeMe1234`. Coder comes up at
The appliance root filesystem is the squashfs + tmpfs overlay from nixpkgs'
`iso-image.nix`, so there's no partition to format or mount and **all state is
discarded on reboot**. `hosts/_appliance_iso/default.nix` imports
[`nixos/_appliance/live-iso.nix`](nixos/_appliance/live-iso.nix) (which pulls in `box-turnkey.nix`) —
[`nixos/_images/appliance/iso.nix`](nixos/_images/appliance/iso.nix) (which pulls in `base/iso.nix` + `box-turnkey.nix`) —
**no** `disko-standard.nix`, `hardware-configuration.nix`, or `facter.json`.
The installed-machine `systemd-boot` / EFI-variable settings are forced off; the
ISO carries its own GRUB-EFI + isolinux loader (BIOS boot is x86-only, so the
Expand Down Expand Up @@ -200,11 +208,30 @@ ESP + ext4 root) and **state survives reboots**, exactly like a machine you ran
sudo dd if=result/*.img of=/dev/sdX bs=4M status=progress oflag=sync
```

Both image hosts are completely separate from the disk-install flow above
All image hosts are completely separate from the disk-install flow above
(`install.sh`, `nixos-facter`); adding them changes nothing for normal
installs. The `_appliance-disk` host shares only the disk *layout*
(`disko-standard.nix`) with real installs, never the install process itself.

### Installer ISO (`_installer-iso`)

The installer is the box as an ISO whose eventual job is to install `coder/box`
onto real hardware. **For now it is identical to the appliance ISO** (full GUI
box + turn-key Coder bootstrap), differing only in image identity (volume ID
`CODER_BOX_INSTALLER`, boot-menu label, and file name
`coder-box-installer-<arch>.iso`); the minimal/installer-only environment is a
future change. It builds **only as an ISO** (no qcow2/raw):

```sh
make installer/iso # native arch
make installer/iso/aarch64-linux # explicit arch
# → out/installer-iso/iso/coder-box-installer-*.iso
```

`hosts/_installer-iso/default.nix` imports
[`nixos/_images/installer/iso.nix`](nixos/_images/installer/iso.nix), which —
like the appliance ISO — pulls in `base/iso.nix` + `box-turnkey.nix`.

## After install

The installer auto-creates the admin user, mints a long-lived API token to
Expand Down
13 changes: 9 additions & 4 deletions agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,17 +184,22 @@ sudo k3s kubectl describe pod -n coder-workspaces <pod-name>
k3s-sysbox.nix # k3s + sysbox runtime
k3s-podman.nix # k3s + rootless Podman socket
screenconnect.nix # ScreenConnect remote access client
_appliance/ # prebuilt-appliance modules (ISO + persistent disk)
box-turnkey.nix # shared turn-key bits for appliances (login + Coder bootstrap)
live-iso.nix # ephemeral appliance ISO module (imported by hosts/_appliance_iso)
_images/ # prebuilt-image modules (appliance + installer)
box-turnkey.nix # shared turn-key Coder box (login + Coder bootstrap); all image hosts
base/hardware.nix # all-hardware (boot on arbitrary hardware)
base/iso.nix # shared ISO mechanics (iso-image.nix, EFI/BIOS/USB bootable, bootloader)
appliance/iso.nix # appliance ISO module (imported by hosts/_appliance_iso)
installer/iso.nix # installer ISO module (imported by hosts/_installer-iso)
pkgs/
coder.nix # Coder server package derivation
coderd-provider.nix # terraform-provider-coderd derivation
hosts/
_appliance_iso/ # `_appliance_iso` host: ephemeral live "Box" ISO; no disko/facter/hardware-config
_appliance_iso/ # `_appliance_iso` host: ephemeral appliance ISO; no disko/facter/hardware-config
# build: make appliance/iso (or appliance/iso/<arch>)
_appliance-disk/ # `_appliance-disk` host: persistent qcow2/raw disk image (disko image builder)
# build: make appliance/qcow2 | make appliance/raw (or .../<arch>)
_installer-iso/ # `_installer-iso` host: installer ISO (ISO only; full GUI box for now)
# build: make installer/iso (or installer/iso/<arch>)
coder-thinkcentre/ # folder name = hostname; default.nix has a hardware-model header comment
default.nix # host module: imports facter/legacy + local.nix + thinkcentre-only services
hardware-configuration.nix # legacy fallback (used until facter.json exists)
Expand Down
5 changes: 3 additions & 2 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@
# hostname, so `nixos-rebuild switch --flake .` auto-selects the right
# config on the running box without needing `.#<attr>`. Adding a new host
# means just creating ./hosts/<hostname>/default.nix; no flake.nix edit.
# (Underscore-prefixed folders like _appliance_iso are image builds that
# skip the folder-name hostname; see mkHost below.)
# (Underscore-prefixed folders like _appliance_iso, _appliance-disk, and
# _installer-iso are image builds that skip the folder-name hostname; see
# mkHost below.)
hostNames = lib.attrNames (lib.filterAttrs
(name: type:
type == "directory"
Expand Down
6 changes: 3 additions & 3 deletions hosts/_appliance-disk/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@
# This host is independent of install.sh; it shares the disk LAYOUT with
# real installs (disko-standard.nix) but is never itself part of the install
# flow. The turn-key login + Coder admin bootstrap (shared with the appliance ISO)
# live in nixos/_appliance/box-turnkey.nix.
# live in nixos/_images/box-turnkey.nix.

{ lib, pkgs, ... }:

{
imports = [
../../nixos/disko-standard.nix # 1 GB ESP + ext4 root single-disk layout
../../nixos/_appliance/box-turnkey.nix # shared turn-key config (login + Coder bootstrap)
../../nixos/disko-standard.nix # 1 GB ESP + ext4 root single-disk layout
../../nixos/_images/box-turnkey.nix # shared turn-key config (login + Coder bootstrap)
] ++ lib.optional (builtins.pathExists ./local.nix) ./local.nix;

# No networking.hostName here on purpose: underscore-prefixed image hosts get
Expand Down
4 changes: 2 additions & 2 deletions hosts/_appliance_iso/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
# Unlike the install hosts (coder-thinkcentre, qemu-arm64), this host does NOT
# import nixos/disko-standard.nix, hardware-configuration.nix, or facter.json:
# the appliance root filesystem is the squashfs + tmpfs overlay provided by
# nixos/_appliance/live-iso.nix. All of the appliance-ISO wiring lives there.
# nixos/_images/appliance/iso.nix. All of the appliance-ISO wiring lives there.
#
# This host is independent of install.sh and never participates in the
# disk-install flow; adding it changes nothing for disko/nixos-install installs.

{ lib, ... }:

{
imports = [ ../../nixos/_appliance/live-iso.nix ]
imports = [ ../../nixos/_images/appliance/iso.nix ]
++ lib.optional (builtins.pathExists ./local.nix) ./local.nix;

# No networking.hostName here on purpose: underscore-prefixed image hosts get
Expand Down
29 changes: 29 additions & 0 deletions hosts/_installer-iso/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Installer ISO host — boots the Coder box to install it onto real hardware.
#
# Folder name = nixosConfigurations attribute (see flake.nix host
# auto-discovery), so this host is exposed as `nixosConfigurations._installer-iso`.
# It's normally built via the Makefile rather than by attribute:
#
# make installer/iso # → out/installer-iso/iso/coder-box-installer-*.iso
# # equivalently:
# nix build .#nixosConfigurations._installer-iso.config.system.build.isoImage
#
# For now the installer is identical to the appliance ISO (full GUI box +
# turn-key Coder bootstrap) and differs only in image identity; the eventual
# minimal, GUI-less installer environment is deferred. Unlike the appliance, the
# installer ships ONLY as an ISO (no qcow2/raw disk images). All of the
# installer-ISO wiring lives in nixos/_images/installer/iso.nix.
#
# This host is independent of install.sh and never participates in the
# disk-install flow; adding it changes nothing for disko/nixos-install installs.

{ lib, ... }:

{
imports = [ ../../nixos/_images/installer/iso.nix ]
++ lib.optional (builtins.pathExists ./local.nix) ./local.nix;

# No networking.hostName here on purpose: underscore-prefixed image hosts get
# no folder-name injection from flake.nix and inherit the central default
# "coder-box" (configuration.nix). Override in local.nix if you need another.
}
Loading