nix: add runner images outputs

This commit is contained in:
every.channel 2026-02-17 02:00:26 -08:00
parent 2e5fb0880f
commit 7719b0b763
No known key found for this signature in database
5 changed files with 232 additions and 1 deletions

50
docs/RUNNER_IMAGES.md Normal file
View file

@ -0,0 +1,50 @@
# Runner Images (NixOS)
This repo exports reproducible NixOS runner configurations via flake outputs:
- `nixosConfigurations.ec-runner-aarch64`
- `nixosConfigurations.ec-runner-x86_64`
- `nixosConfigurations.ec-runner-aarch64-netboot`
- `nixosConfigurations.ec-runner-x86_64-netboot`
- `nixosConfigurations.ec-runner-x86_64-iso`
- `nixosConfigurations.ec-runner-aarch64-sdimage`
The runner OS exposes this repo's flake source inside the system at:
- `/etc/every-channel/flake`
This allows a runner to self-build and verify artifacts from the same flake definition.
## Build (OrbStack / Linux)
These commands should be run inside a Linux environment with Nix enabled (e.g. OrbStack VM).
Build netboot artifacts (iPXE/PXE):
```sh
nix build .#nixosConfigurations.ec-runner-aarch64-netboot.config.system.build.netboot
```
Build an installer ISO (x86_64):
```sh
nix build .#nixosConfigurations.ec-runner-x86_64-iso.config.system.build.isoImage
```
Build an aarch64 SD image:
```sh
nix build .#nixosConfigurations.ec-runner-aarch64-sdimage.config.system.build.sdImage
```
## Outputs
After building, artifacts will be in `./result` (a symlink into the Nix store).
Common netboot outputs include:
- `kernel`
- `initrd`
- `netboot.ipxe`
Exact filenames may vary across NixOS releases.

View file

@ -0,0 +1,45 @@
# ECP-0065: NixOS Runner Images + Netboot Artifacts
Status: Draft
## Decision
Publish a first-party, reproducible NixOS "runner" system definition from this repo, and expose build outputs suitable for:
- local-disk installs (pave/reinstall),
- netboot (iPXE/PXE) bootstrap, and
- byte-identical runner OS images produced in CI.
The runner system:
- is defined in-repo as a `nixosConfiguration` in `flake.nix`,
- exports the repo source tree inside the OS at a stable path (read-only) so the node can self-build and verify from the same flake,
- uses `ec-node` as the primary long-running publisher binary, with orchestration via NixOS + systemd.
Initial implementation targets `aarch64-linux` builds first (local builds via OrbStack). `x86_64-linux` is defined in the flake but may not be built until an x86 builder is available.
## Motivation
- "Bootstrap path == update path": the same flake definition and CI-built artifacts should be usable to (re)install and to update.
- Fleet operability: remove per-node hand configuration; treat nodes as cattle.
- Verifiability: runners can rebuild and compare their OS closure against the CI artifacts using the embedded flake source.
## Scope
In scope:
- `nixosConfigurations.ec-runner-{aarch64,x86_64}` in `flake.nix`.
- `nixosConfigurations.ec-runner-*-netboot` and `nixosConfigurations.ec-runner-*-iso` for image artifacts.
- Minimal runner NixOS module for baseline host settings and stable in-OS flake source path.
- Docs/scripts for building netboot outputs locally in OrbStack.
Out of scope (defer):
- CI publishing pipeline (binary cache, attestation, release upload).
- Remote runtime provisioning (fetching per-node channel lists).
- Hardware-accelerated transcode changes (keep current CPU x264 baseline).
## Rollout / Reversibility
- Rollout begins with local builds and a single test machine.
- Reversible by removing the `nixosConfigurations` and runner module; existing nodes can continue to run via manual `tmux` or ad-hoc installs.

View file

@ -12,10 +12,89 @@
let let
nixosModules = rec { nixosModules = rec {
ec-node = import ./nix/modules/ec-node.nix; ec-node = import ./nix/modules/ec-node.nix;
ec-runner = import ./nix/modules/ec-runner.nix;
default = ec-node; default = ec-node;
}; };
in in
{ inherit nixosModules; } {
inherit nixosModules;
nixosConfigurations =
let
mkRunner = system: extraModules:
nixpkgs.lib.nixosSystem {
inherit system;
specialArgs = { inherit self; };
modules = [
./nix/nixos/ec-runner.nix
] ++ extraModules;
};
in
{
# Base runner system (for normal installs).
ec-runner-aarch64 = mkRunner "aarch64-linux" [ ];
ec-runner-x86_64 = mkRunner "x86_64-linux" [ ];
# Netboot artifacts (iPXE/PXE).
ec-runner-aarch64-netboot = mkRunner "aarch64-linux" [
({ modulesPath, ... }: {
imports = [ (modulesPath + "/installer/netboot/netboot-minimal.nix") ];
})
({ config, pkgs, ... }: {
# Convenience output dir: { kernel, initrd, netboot.ipxe }.
system.build.netboot = pkgs.linkFarm "ec-runner-netboot" [
{
name = "kernel";
path = "${config.system.build.kernel}/${config.system.boot.loader.kernelFile}";
}
{
name = "initrd";
path = "${config.system.build.netbootRamdisk}/initrd";
}
{
name = "netboot.ipxe";
path = "${config.system.build.netbootIpxeScript}/netboot.ipxe";
}
];
})
];
ec-runner-x86_64-netboot = mkRunner "x86_64-linux" [
({ modulesPath, ... }: {
imports = [ (modulesPath + "/installer/netboot/netboot-minimal.nix") ];
})
({ config, pkgs, ... }: {
system.build.netboot = pkgs.linkFarm "ec-runner-netboot" [
{
name = "kernel";
path = "${config.system.build.kernel}/${config.system.boot.loader.kernelFile}";
}
{
name = "initrd";
path = "${config.system.build.netbootRamdisk}/initrd";
}
{
name = "netboot.ipxe";
path = "${config.system.build.netbootIpxeScript}/netboot.ipxe";
}
];
})
];
# Installer ISO (primarily for x86_64 bring-up).
ec-runner-x86_64-iso = mkRunner "x86_64-linux" [
({ modulesPath, ... }: {
imports = [ (modulesPath + "/installer/cd-dvd/installation-cd-minimal.nix") ];
})
];
# aarch64 SD image (useful for quick ARM bring-up and as a "real image" build target).
ec-runner-aarch64-sdimage = mkRunner "aarch64-linux" [
({ modulesPath, ... }: {
imports = [ (modulesPath + "/installer/sd-card/sd-image-aarch64.nix") ];
})
];
};
}
// flake-utils.lib.eachDefaultSystem (system: // flake-utils.lib.eachDefaultSystem (system:
let let
pkgs = import nixpkgs { pkgs = import nixpkgs {

28
nix/modules/ec-runner.nix Normal file
View file

@ -0,0 +1,28 @@
{ lib, config, pkgs, self, ... }:
let
cfg = config.services.every-channel.runner;
in
{
options.services.every-channel.runner = {
enable = lib.mkEnableOption "every.channel runner base system profile";
};
config = lib.mkIf cfg.enable {
# Minimal, conservative baseline for headless runners.
networking.useDHCP = lib.mkDefault true;
services.openssh.enable = lib.mkDefault true;
# Keep Nix flakes available on the runner itself.
nix.settings.experimental-features = lib.mkDefault [ "nix-command" "flakes" ];
# Provide the flake source tree at a stable path (symlink into /nix/store).
environment.etc."every-channel/flake".source = self;
environment.systemPackages = with pkgs; [
git
jq
curl
];
};
}

29
nix/nixos/ec-runner.nix Normal file
View file

@ -0,0 +1,29 @@
{ lib, pkgs, ... }:
{
imports = [
../modules/ec-node.nix
../modules/ec-runner.nix
];
services.every-channel.runner.enable = true;
# This is a role image; avoid baking per-host secrets/keys. SSH host keys will be
# generated at first boot by NixOS defaults.
networking.hostName = lib.mkDefault "ec-runner";
time.timeZone = lib.mkDefault "UTC";
# Basic hygiene for unattended boxes.
services.openssh.settings.PasswordAuthentication = false;
services.openssh.settings.KbdInteractiveAuthentication = false;
# Enable serial console logs where possible (helps in headless bring-up).
boot.kernelParams = [
"console=tty0"
];
# Required by NixOS.
system.stateVersion = "24.11";
}