{"id":51588699,"url":"https://github.com/coder/box","last_synced_at":"2026-07-12T18:02:14.164Z","repository":{"id":362669211,"uuid":"1211710747","full_name":"coder/box","owner":"coder","description":"The Box™ is a NixOS-based appliance for Coder","archived":false,"fork":false,"pushed_at":"2026-06-29T20:10:58.000Z","size":5386,"stargazers_count":7,"open_issues_count":15,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-29T21:09:24.871Z","etag":null,"topics":["nixos"],"latest_commit_sha":null,"homepage":"","language":"Nix","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/coder.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"agents.md","dco":null,"cla":null}},"created_at":"2026-04-15T17:08:16.000Z","updated_at":"2026-06-29T19:06:21.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/coder/box","commit_stats":null,"previous_names":["coder/box"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/coder/box","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coder%2Fbox","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coder%2Fbox/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coder%2Fbox/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coder%2Fbox/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/coder","download_url":"https://codeload.github.com/coder/box/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coder%2Fbox/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35398566,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-12T02:00:06.386Z","response_time":87,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["nixos"],"created_at":"2026-07-11T13:00:27.429Z","updated_at":"2026-07-12T18:02:14.151Z","avatar_url":"https://github.com/coder.png","language":"Nix","funding_links":[],"categories":["Automation"],"sub_categories":[],"readme":"\u003c!-- markdownlint-disable MD041 --\u003e\n\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://coder.com#gh-light-mode-only\"\u003e\n    \u003cimg src=\"https://github.com/coder/coder/blob/main/docs/images/logo-black.png\" alt=\"Coder Logo Light\" style=\"width: 128px\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://coder.com#gh-dark-mode-only\"\u003e\n    \u003cimg src=\"https://github.com/coder/coder/blob/main/docs/images/logo-white.png\" alt=\"Coder Logo Dark\" style=\"width: 128px\"\u003e\n  \u003c/a\u003e\n\n  \u003ch1\u003e\n  The Box™\n  \u003c/h1\u003e\n\n\u003c/div\u003e\n\nNixOS configuration for Coder demo and workshop boxes.\n\n\u003e **Demo box setup**. This repo configures one or more single-purpose physical machines running Coder + k3s as self-contained workshop and demo environments. It is intentionally simple: no HA, no remote state, no cloud provider. Each machine's secrets (IPs, auth keys, passwords) live in a per-host gitignored `hosts/\u003chost\u003e/local.nix` file. Each box runs a Coder server, k3s (single-node), and a set of workspace templates managed by Terraform via the `coderd` provider.\n\nThis repo is a Nix flake. `flake.nix` auto-discovers every subdirectory of\n`./hosts/` that contains a `default.nix` and exposes it as\n`nixosConfigurations.\u003cfolder-name\u003e`. For normal install hosts the folder name\nis also the hostname, so `nixos-rebuild switch --flake .` auto-selects the\nright config on the running box. Adding a new host means creating a host\nfolder, no flake.nix edit. The installer does this for you.\n\nHosts whose folder name starts with an underscore (`_appliance-iso`,\n`_appliance-disk`, `_installer-iso`) are image builds, not per-machine installs: they\ndo **not** get the folder-name hostname and instead inherit the central\ndefault `networking.hostName = \"coder-box\"` (set in `configuration.nix`).\n\nTwo community tools do the heavy lifting:\n\n- [`disko`](https://github.com/nix-community/disko) declares partition layouts in Nix. `nixos/disko-standard.nix` is a single-disk UEFI layout (1 GB EFI / ZFS root pool; no on-disk swap — zram instead). `install.sh` picks the device at install time; the ZFS `networking.hostId` is derived in Nix from the hostname (sha256 substring), so each host gets a distinct id automatically. ZFS gives cheap on-demand snapshots (take one before a risky rebuild and roll back in seconds), zstd compression, and checksum/scrub integrity.\n- [`nixos-facter`](https://github.com/nix-community/nixos-facter) writes a JSON hardware report (`facter.json`) that replaces `hardware-configuration.nix` on new hosts. The `nixos-facter-modules` module reads it to set kernel modules, microcode, GPU drivers, and so on.\n\n## Installing on a new machine\n\nFrom a NixOS live USB on the target box, with network access (any reasonably recent ISO from [nixos.org](https://nixos.org/download/) works; the installed system pins its own nixpkgs in `flake.lock` independent of what the USB is running):\n\n```sh\nnix-shell -p git --run \"git clone https://github.com/coder/box /tmp/box\"\ncd /tmp/box\nsudo ./install.sh --interactive\n```\n\nWith `--interactive` (`-i`) the installer prompts for any value not already passed as a flag, including a target disk picker. Each prompt offers a default; press Enter to accept it. Without `--interactive`, a target disk must be given via `--disk` (there is no safe default). Other values not passed as a flag fall back to a default: hostname `coder-box-\u003crandom\u003e` (e.g. `coder-box-deadbeef`), Coder admin `admin@coder.com` / `PleaseChangeMe1234`, OS login `coderbox` / `PleaseChangeMe1234`. Passwords in the summary are obfuscated, unless they are left as defaults.\n\nFor a fully unattended install, pass every value as a flag:\n\n```sh\nsudo ./install.sh \\\n  --hostname coder-demo \\\n  --disk /dev/nvme0n1 \\\n  --coder-admin-email you@example.com \\\n  --coder-admin-password 'changeme' \\\n  --nixos-username coderbox \\\n  --nixos-password 'changeme' \\\n  --yes\n```\n\n`./install.sh --help` lists everything. `--coder-admin-password-file PATH` and `--nixos-password-file PATH` read passwords from a file so they don't end up in shell history. `--no-reboot` skips the automatic reboot at the end.\n\nThe installer generates `hosts/\u003chostname\u003e/{default.nix,local.nix,facter.json}`, copies the repo into `/etc/nixos-repo` on the target, and symlinks `/etc/nixos/flake.nix`. After reboot, `nixos-rebuild switch` Just Works. Continue with [After install](#after-install).\n\n\u003e **Different partition layout?** Don't import `nixos/disko-standard.nix`; drop your own disko config into the host folder instead. See [disko examples](https://github.com/nix-community/disko/tree/master/example).\n\n\u003e **BIOS hardware?** The shared config defaults to `systemd-boot` (UEFI). In your host's `default.nix`:\n\u003e ```nix\n\u003e boot.loader.systemd-boot.enable = false;\n\u003e boot.loader.grub = { enable = true; device = \"/dev/sda\"; };\n\u003e ```\n\u003e And use a BIOS-compatible disko layout instead of `disko-standard.nix`.\n\n## Prebuilt images (The Box™ without `install.sh`)\n\nSometimes you don't want to run the installer; you just want The Box™. Two\nimage flavours build the *exact same* configured system — GNOME, the Coder\nserver, k3s, Podman, the bundled templates — with admin bootstrap and template\ndeploy happening on boot just like a real install. Neither is an installer.\n\nThese prebuilt images are called **appliances** (the box, prebuilt — no\n`install.sh`). Build them with `make appliance/\u003cformat\u003e`:\n\n| Format | Host | State | Status | Build |\n|---|---|---|---|---|\n| **iso** (live, ephemeral) | `_appliance-iso` | tmpfs overlay — wiped on reboot | verified | `make appliance/iso` |\n| **qcow2** (persistent disk) | `_appliance-disk` | persists across reboots | ⚠️ untested | `make appliance/qcow2` |\n| **raw** (persistent disk) | `_appliance-disk` | persists across reboots | ⚠️ untested | `make appliance/raw` |\n\nAll builds need a Linux machine with Nix + flakes. Every target also takes an\narchitecture suffix (short names are normalized to `*-linux`); cross-arch\nbuilds need a matching builder (native remote builder or binfmt/QEMU):\n\n```sh\nmake appliance/iso/aarch64-linux\nmake appliance/qcow2/aarch64-linux\nmake appliance/raw/x86_64\n```\n\nEach target drops a `--out-link` (GC-root symlink) in `./out/` named after the\ntarget — e.g. `out/appliance-iso`, `out/appliance-raw-aarch64-linux` — pointing\nstraight at the built image in the Nix store (no copy; `./out` is gitignored).\nThe ISO is then at `out/appliance-iso/iso/coder-box-appliance-*.iso`, and a disk\nimage at `out/appliance-raw/coder-box-appliance-*.raw` (or\n`out/appliance-qcow2/coder-box-appliance-*.qcow2`). All names carry the arch,\ne.g. `coder-box-appliance-aarch64-linux.iso`.\n\nEvery shipped image — ISO, qcow2, and raw — is built alongside a `\u003cname\u003e.sha256`\nsidecar in the same directory (`coder-box-appliance-*.iso.sha256`,\n`coder-box-appliance-*.qcow2.sha256`, etc.), so `sha256sum -c \u003cname\u003e.sha256`\nverifies the image and `cp -L out/\u003ctarget\u003e/*` (or `.../iso/*` for ISOs) copies\nimage + checksum together. The build prints each checksum when it finishes.\n\nThe turn-key login + Coder admin bootstrap shared by all image flavours live in\n[`nixos/_images/box-turnkey.nix`](nixos/_images/box-turnkey.nix): autologin to the `coderbox`\ndesktop, and admin `admin@coder.com` / `PleaseChangeMe1234`. Coder comes up at\n`http://\u003chostname\u003e.local:3000` (or the `*.try.coder.app` tunnel URL in\n`/etc/motd`). Change these before sharing an image by dropping a gitignored\n`hosts/\u003chost\u003e/local.nix` (same shape as `local.nix.example`).\n\n### Appliance ISO (`_appliance-iso`)\n\nThe appliance root filesystem is the squashfs + tmpfs overlay from nixpkgs'\n`iso-image.nix`, so there's no partition to format or mount and **all state is\ndiscarded on reboot**. `hosts/_appliance-iso/default.nix` imports\n[`nixos/_images/appliance/iso.nix`](nixos/_images/appliance/iso.nix) (which pulls in `base/iso.nix` + `box-turnkey.nix`) —\n**no** `disko-standard.nix`, `hardware-configuration.nix`, or `facter.json`.\nThe installed-machine `systemd-boot` / EFI-variable settings are forced off; the\nISO carries its own GRUB-EFI + isolinux loader (BIOS boot is x86-only, so the\naarch64 ISO is EFI-only). Flash it (it's isohybrid) and boot:\n\n```sh\nsudo dd if=out/appliance-iso/iso/coder-box-appliance-*.iso of=/dev/sdX bs=4M status=progress oflag=sync\n```\n\n### Persistent disk image (`_appliance-disk`)\n\n\u003e [!WARNING]\n\u003e **Untested.** The `qcow2` and `raw` disk-image builds evaluate cleanly and\n\u003e produce a valid build plan, but they have not yet been built end-to-end or\n\u003e boot-tested. The live `appliance/iso` is the only flavour verified to build\n\u003e and boot so far. Treat the disk images as experimental until someone confirms\n\u003e a working build + boot.\n\nBuilt with [disko](https://github.com/nix-community/disko)'s image builder, so\nit carries the real on-disk GPT layout from `nixos/disko-standard.nix` (1 GB\nESP + ZFS root pool) and **state survives reboots**, exactly like a machine you ran\n`install.sh` on. `hosts/_appliance-disk/default.nix` imports\n`disko-standard.nix` + `box-turnkey.nix`.\n\n- **`qcow2`** — boot it directly in QEMU/libvirt/UTM. A qcow2 is a container\n  format, so it can **not** be `dd`'d to a drive as-is — convert first\n  (`qemu-img convert -O raw box.qcow2 box.img`) or build the raw image instead.\n- **`raw`** — a plain disk image you can `dd` straight onto a physical drive:\n  ```sh\n  sudo dd if=result/*.img of=/dev/sdX bs=4M status=progress oflag=sync\n  ```\n\nAll image hosts are completely separate from the disk-install flow above\n(`install.sh`, `nixos-facter`); adding them changes nothing for normal\ninstalls. The `_appliance-disk` host shares only the disk *layout*\n(`disko-standard.nix`) with real installs, never the install process itself.\n\n### Installer ISO (`_installer-iso`)\n\nThe installer is the box as an ISO whose eventual job is to install `coder/box`\nonto real hardware. **For now it is identical to the appliance ISO** (full GUI\nbox + turn-key Coder bootstrap), differing only in image identity (volume ID\n`CODER_BOX_INSTALLER`, boot-menu label, and file name\n`coder-box-installer-\u003carch\u003e.iso`); the minimal/installer-only environment is a\nfuture change. It builds **only as an ISO** (no qcow2/raw):\n\n```sh\nmake installer/iso                 # native arch\nmake installer/iso/aarch64-linux   # explicit arch\n# → out/installer-iso/iso/coder-box-installer-*.iso\n```\n\n`hosts/_installer-iso/default.nix` imports\n[`nixos/_images/installer/iso.nix`](nixos/_images/installer/iso.nix), which —\nlike the appliance ISO — pulls in `base/iso.nix` + `box-turnkey.nix`.\n\n## After install\n\nThe installer auto-creates the admin user, mints a long-lived API token to\n`/etc/coder/session-token`, and deploys the workspace templates on first\nboot via `coder-init-admin.service`. After the reboot:\n\n1. Find the box at `http://\u003cyour-hostname\u003e.local:3000`, or look up the\n   `*.try.coder.app` tunnel URL in `/etc/motd` on the box (also tailed to\n   the console on each SSH login).\n2. Log in with the Coder admin email and password set at install time\n   (defaults: `admin@coder.com` / `PleaseChangeMe1234`).\n3. Change the admin password from the user settings page if you used the\n   defaults.\n\nSubsequent edits to `coderd/` templates go out via `coder-template-sync`\non every `sudo nixos-rebuild switch`.\n\n## Applying changes\n\n```sh\nsudo nixos-rebuild switch                    # most changes\nsudo nixos-rebuild boot \u0026\u0026 sudo reboot       # changes that touch the desktop stack\n\n# Edited hosts/\u003chost\u003e/local.nix or facter.json? Re-mark intent-to-add:\nsudo git -C /etc/nixos-repo add --intent-to-add -f \\\n  hosts/\u003chost\u003e/local.nix \\\n  hosts/\u003chost\u003e/facter.json\n```\n\n## Updating nixpkgs / disko / facter\n\n```sh\nsudo nix flake update --flake /etc/nixos-repo\nsudo nixos-rebuild switch\n```\n\nThis bumps `flake.lock` to the latest of each input.\n\n## Workspace Templates\n\n### coder-cli\nFull-featured CLI/dev workspace running the `codercom/oss-dogfood` image (Ubuntu + docker CLI, terraform, gh, go, node, etc.). No sysbox or inner Docker daemon; uses the host's runtime.\n\n### k3s-sysbox\nFull Docker-in-workspace via sysbox-runc. Each workspace gets an isolated Docker daemon. No privileged mode on the host.\n\n### k3s-podman\nDocker-compatible socket via host rootless Podman. Simpler than sysbox; no inner daemon. `docker` CLI works via `DOCKER_HOST`.\n\n### k3s-dev\nLanguage demo workspaces. Pick Python/Node.js/Go/Java/Rust at creation; a real demo app auto-starts (FastAPI, Next.js, Pagoda, Spring PetClinic, rustypaste).\n\n### nook-android *(thinkcentre only)*\nDev environment for building the [trmnl-nook-simple-touch](https://github.com/usetrmnl/trmnl-nook-simple-touch) APK for the Barnes \u0026 Noble Nook Simple Touch. Uses a pre-built image (`localhost/nook-android:latest`) loaded into k3s by the `nook-android-image-build` NixOS service. 32-bit ADT tools run transparently via qemu-i386 binfmt.\n\n## Key Services\n\n| Service | Description |\n|---|---|\n| `coder.service` | Coder server on port 3000 |\n| `postgresql.service` | Local PostgreSQL for Coder |\n| `k3s.service` | Single-node k3s (sysbox-runc runtime) |\n| `tailscaled` + `tailscale-autoauth` | Tailscale (auth key in `hosts/\u003chost\u003e/local.nix`) |\n| `nook-android-image-build` | Builds/imports nook-android image into k3s containerd |\n| `coder-sync-ssh-keys` | Fetches SSH keys from GitHub on boot |\n| `screenconnect-install` | Downloads and installs ScreenConnect client (oneshot) |\n| `screenconnect` | ScreenConnect remote access daemon |\n| `coder-redirect.service` | HTTP 302 redirect: port 80 → live `*.try.coder.app` tunnel URL |\n\n## Reset / Full Wipe\n\nEncoded as a NixOS systemd service. No manual steps needed.\n\n```sh\nsudo systemctl start coder-reset\n```\n\nFully automated, no follow-up steps needed. The service:\n\n1. Stops Coder, coder-redirect\n2. Force-deletes all workspace pods and PVCs from k3s\n3. Drops and recreates the PostgreSQL database\n4. Wipes `/var/lib/coder` (data dir, sentinel, tokens, Podman volumes)\n5. Starts Coder and waits for the API\n6. Re-bootstraps the admin user from credentials in the host's `local.nix`\n7. Mints a fresh long-lived session token → writes to `/etc/coder/session-token`\n8. Restarts `coder-redirect`\n9. Runs `nixos-rebuild switch` to push templates back via `coder-template-sync`\n\n### Changing the admin password\n\n1. Edit `hosts/\u003chost\u003e/local.nix`, update `CODER_ADMIN_PASSWORD`.\n2. Run `sudo nixos-rebuild switch` to bake the new password into the service.\n3. Run `sudo systemctl start coder-reset` to wipe and re-bootstrap with the new password.\n\n\u003e If you need to change the password on a **live** deployment without a full wipe:\n\u003e ```sh\n\u003e TOKEN=$(curl -sf -X POST http://localhost:3000/api/v2/users/login \\\n\u003e   -H 'Content-Type: application/json' \\\n\u003e   -d '{\"email\":\"admin@coder.com\",\"password\":\"\u003cOLD\u003e\"}' | jq -r '.session_token')\n\u003e curl -sf -X PUT http://localhost:3000/api/v2/users/me/password \\\n\u003e   -H \"Coder-Session-Token: $TOKEN\" \\\n\u003e   -H 'Content-Type: application/json' \\\n\u003e   -d '{\"old_password\":\"\u003cOLD\u003e\",\"password\":\"\u003cNEW\u003e\"}'\n\u003e ```\n\n## Notes\n\n- Steps 1 to 4 run while Coder is stopped so the provisioner can't re-create pods mid-wipe; the systemd service handles the stop and restart.\n- If the admin password changed before a reset and the user already exists in Postgres (rare; the wipe drops the DB), update via the API instead of `coder-reset`:\n  ```sh\n  TOKEN=$(curl -sf -X POST http://localhost:3000/api/v2/users/login \\\n    -H 'Content-Type: application/json' \\\n    -d '{\"email\":\"admin@coder.com\",\"password\":\"\u003cOLD_PASSWORD\u003e\"}' \\\n    | jq -r '.session_token')\n  curl -sf -X PUT http://localhost:3000/api/v2/users/me/password \\\n    -H \"Coder-Session-Token: $TOKEN\" \\\n    -H 'Content-Type: application/json' \\\n    -d '{\"old_password\":\"\u003cOLD_PASSWORD\u003e\",\"password\":\"\u003cNEW_PASSWORD\u003e\"}'\n  ```\n- Rootless Podman volumes live under `/var/lib/coder/.local/...`; `rm -rf /var/lib/coder/*` in step 4 clears these too.\n- k3s PVs backed by `local-path-provisioner` live under `/var/lib/rancher/k3s/storage/`; `kubectl delete pvc` in step 2 triggers their cleanup.\n\n## Repo notes\n\n- `hosts/\u003chost\u003e/local.nix` is gitignored. Never commit secrets or machine-specific overrides.\n- The `coderd/` Terraform state is stored in `/var/lib/coder/template-sync/` on the box, not in the repo.\n- `CODER_ACCESS_URL` is intentionally unset; Coder auto-creates a `*.try.coder.app` tunnel on startup. `http://\u003chostname\u003e.local` (port 80) redirects to the live tunnel URL via `coder-redirect.service`, which also writes the URL to `/etc/motd` so it shows on every console and SSH login.\n- The `coder` user (uid 991) runs Coder server and rootless Podman. UID is pinned; do not change.\n- Workspace pods resolve `\u003chostname\u003e.local` via a `hostAliases` entry pointing to the LAN IP (set via `services.coder-nixos.lanIp` in the host's `local.nix`).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoder%2Fbox","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcoder%2Fbox","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoder%2Fbox/lists"}