Archived
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e601ce85c | ||
|
|
cbdb288bc3 |
-15
@@ -12,7 +12,6 @@ keys:
|
|||||||
- &lxc-gui age190htw7prp4vln076dxjx3gxxaq06h0zl0te7cqgpx79vl3lhkaes8suy05
|
- &lxc-gui age190htw7prp4vln076dxjx3gxxaq06h0zl0te7cqgpx79vl3lhkaes8suy05
|
||||||
- &proxmox-server age1ukpqxzl44mnjpy5r96sfuc5sqzm47u4k8ujjh5qdgy6jvl9uqgpspymqfk
|
- &proxmox-server age1ukpqxzl44mnjpy5r96sfuc5sqzm47u4k8ujjh5qdgy6jvl9uqgpspymqfk
|
||||||
- &vm-server age15kh7akxlx7zn00tey79rq2g8lgs4j5y77rcnyfxrxap8ckfu0a9sqvtdhh
|
- &vm-server age15kh7akxlx7zn00tey79rq2g8lgs4j5y77rcnyfxrxap8ckfu0a9sqvtdhh
|
||||||
- &baremetal-gui age1ehkswwz2pqaz4svzh7ela5tdnssl8kn6d4vwwxd6zwg8exfpd43syyrrjp
|
|
||||||
|
|
||||||
creation_rules:
|
creation_rules:
|
||||||
# Shared across every currently-deployed host: root/nixos password hash,
|
# Shared across every currently-deployed host: root/nixos password hash,
|
||||||
@@ -35,7 +34,6 @@ creation_rules:
|
|||||||
- *lxc-gui
|
- *lxc-gui
|
||||||
- *proxmox-server
|
- *proxmox-server
|
||||||
- *vm-server
|
- *vm-server
|
||||||
- *baremetal-gui
|
|
||||||
|
|
||||||
- path_regex: secrets/nix-cache\.yaml$
|
- path_regex: secrets/nix-cache\.yaml$
|
||||||
key_groups:
|
key_groups:
|
||||||
@@ -57,16 +55,3 @@ creation_rules:
|
|||||||
- age:
|
- age:
|
||||||
- *admin
|
- *admin
|
||||||
- *docker
|
- *docker
|
||||||
|
|
||||||
# gui-host-specific secrets (currently: wifi-password, see
|
|
||||||
# modules/networking/wifi.nix). Only *lxc-gui has a registered key today
|
|
||||||
# -- proxmox-gui/linode-gui/baremetal-gui haven't been provisioned via
|
|
||||||
# scripts/secrets/sync-host-keys.sh yet, so whichever variant is actually
|
|
||||||
# deployed next needs its recipient added here (and `sops updatekeys` rerun)
|
|
||||||
# before it can decrypt this.
|
|
||||||
- path_regex: secrets/gui\.yaml$
|
|
||||||
key_groups:
|
|
||||||
- age:
|
|
||||||
- *admin
|
|
||||||
- *lxc-gui
|
|
||||||
- *baremetal-gui
|
|
||||||
|
|||||||
@@ -164,39 +164,13 @@ before committing.
|
|||||||
|
|
||||||
Beyond `codex-setup.sh`/`codex-maintenance.sh` above, `scripts/` is
|
Beyond `codex-setup.sh`/`codex-maintenance.sh` above, `scripts/` is
|
||||||
organized by purpose: `scripts/secrets/` (sops/age + SSH host-key
|
organized by purpose: `scripts/secrets/` (sops/age + SSH host-key
|
||||||
management), `scripts/proxmox/` (Proxmox deployment), `scripts/installer/`
|
management), `scripts/proxmox/` (Proxmox deployment), `scripts/lib/`
|
||||||
(the auto-installer's own shell script, templated into the image — see
|
(shared helpers, sourced by the scripts below — not run directly), and a
|
||||||
below), `scripts/lib/` (shared helpers, sourced by the scripts below — not
|
handful of repo-wide scripts left at the top level (`env.sh`,
|
||||||
run directly), and a handful of repo-wide scripts left at the top level
|
`bump-nixpkgs-release.sh`, plus `codex-setup.sh`/`codex-maintenance.sh`
|
||||||
(`env.sh`, `bump-nixpkgs-release.sh`, plus `codex-setup.sh`/
|
above). When adding a new script, put it in the matching subfolder rather
|
||||||
`codex-maintenance.sh` above). When adding a new script, put it in the
|
than the top level, and if it duplicates logic another script already has,
|
||||||
matching subfolder rather than the top level, and if it duplicates logic
|
lift the shared part into `scripts/lib/` instead of copying it.
|
||||||
another script already has, lift the shared part into `scripts/lib/`
|
|
||||||
instead of copying it.
|
|
||||||
|
|
||||||
### `scripts/installer/`
|
|
||||||
|
|
||||||
- `scripts/installer/auto-install.sh` — the interactive install script
|
|
||||||
baked into the auto-installer image (see `docs/auto-installer.md`), kept
|
|
||||||
as a real, version-controlled shell file rather than inline in
|
|
||||||
`modules/installer/common.nix`'s Nix. It sources `scripts/env.sh` itself
|
|
||||||
for `LAN_DOMAIN` (`export LAN_DOMAIN`/`: "${LAN_DOMAIN:=...}"`, matching
|
|
||||||
`variables.nix`'s `lanDomain` — manually kept in sync, same pattern as
|
|
||||||
`NIX_CACHE_HOST` mirroring `nixCacheHost`), rather than Nix-level string
|
|
||||||
substitution — that's what makes it work identically whether run
|
|
||||||
straight from a git checkout or from inside the built installer image.
|
|
||||||
`common.nix` bakes `scripts/env.sh` in alongside it at a matching
|
|
||||||
relative path (`/etc/nixos-installer/env.sh` next to
|
|
||||||
`/etc/nixos-installer/installer/auto-install.sh`) so the script's own
|
|
||||||
`source "$(dirname ...)/../env.sh"` line resolves the same way in both
|
|
||||||
contexts — this is also why it's invoked from
|
|
||||||
`/etc/nixos-installer/installer/auto-install.sh` rather than a flat
|
|
||||||
`/etc/auto-install.sh`. `#!/usr/bin/env bash`, not
|
|
||||||
`#!/run/current-system/sw/bin/bash`: the latter only resolves on an
|
|
||||||
already-activated NixOS system, breaking the checked-out-file case
|
|
||||||
entirely (confirmed live: "cannot execute: required file not found" on
|
|
||||||
a non-NixOS box); `/usr/bin/env` is reliably present on both NixOS
|
|
||||||
(`environment.usrbinenv`'s own default) and any normal Linux distro.
|
|
||||||
|
|
||||||
### `scripts/secrets/`
|
### `scripts/secrets/`
|
||||||
|
|
||||||
@@ -308,9 +282,8 @@ Sourced by the scripts above, never run directly:
|
|||||||
### Top level
|
### Top level
|
||||||
|
|
||||||
- `scripts/env.sh` — shared config (`PROXMOX_HOST`, storage pool, bridge,
|
- `scripts/env.sh` — shared config (`PROXMOX_HOST`, storage pool, bridge,
|
||||||
default cores/memory, `NIX_CACHE_HOST`, `LAN_DOMAIN`) sourced by
|
default cores/memory) sourced by `create-proxmox-resource.sh`. Add new
|
||||||
`create-proxmox-resource.sh` and `scripts/installer/auto-install.sh`. Add
|
cross-script config here instead of duplicating it per-script.
|
||||||
new cross-script config here instead of duplicating it per-script.
|
|
||||||
- `scripts/bump-nixpkgs-release.sh` — bumps `flake.nix`'s `nixpkgs.url`/
|
- `scripts/bump-nixpkgs-release.sh` — bumps `flake.nix`'s `nixpkgs.url`/
|
||||||
`home-manager.url` in place. Exists because flake input URLs can't
|
`home-manager.url` in place. Exists because flake input URLs can't
|
||||||
reference `variables.nix` (confirmed empirically — `nix flake metadata`
|
reference `variables.nix` (confirmed empirically — `nix flake metadata`
|
||||||
@@ -348,14 +321,11 @@ nixosSystem {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Platforms: `linode`, `proxmox`, `lxc`, `baremetal`. Build types: `minimal`,
|
Platforms: `linode`, `proxmox`, `lxc`. Build types: `minimal`, `nix-cache`,
|
||||||
`nix-cache`, `server`, `docker`, `gui`, `pxe-boot`, `tailscale-exit-node`,
|
`server`, `docker`, `gui`, `pxe-boot`, `tailscale-exit-node`, `tor-relay`. Not
|
||||||
`tor-relay`. Not every combination is built — e.g. `pxe-boot` has no `linode`
|
every combination is built — e.g. `pxe-boot` has no `linode` variant
|
||||||
variant (PXE/DHCP/TFTP need LAN L2 adjacency a Linode VPS doesn't have),
|
(PXE/DHCP/TFTP need LAN L2 adjacency a Linode VPS doesn't have), and
|
||||||
`tor-relay` currently only exists as `lxc-tor-relay`, and `baremetal`
|
`tor-relay` currently only exists as `lxc-tor-relay`. Treat `flake.nix`'s
|
||||||
currently only exists as `baremetal-gui` (the real gui-host hardware —
|
|
||||||
see `hosts/nixos/host.nix` and `modules/platforms/baremetal.nix`). Treat
|
|
||||||
`flake.nix`'s
|
|
||||||
`generatedTargets` as the source
|
`generatedTargets` as the source
|
||||||
of truth for which hosts exist — `README.md`, `AGENTS.md`,
|
of truth for which hosts exist — `README.md`, `AGENTS.md`,
|
||||||
`docs/flake-lock-automation.md`, and the CI eval workflows
|
`docs/flake-lock-automation.md`, and the CI eval workflows
|
||||||
@@ -371,16 +341,12 @@ removing a host.
|
|||||||
of their own beyond narrow parameterized helpers (see
|
of their own beyond narrow parameterized helpers (see
|
||||||
`modules/beszel/host-token.nix` below) — all shared behavior comes from the
|
`modules/beszel/host-token.nix` below) — all shared behavior comes from the
|
||||||
platform/build-type modules composed in `flake.nix`, not from the host file.
|
platform/build-type modules composed in `flake.nix`, not from the host file.
|
||||||
- `modules/platforms/{linode,proxmox,lxc,baremetal}.nix` — platform-specific
|
- `modules/platforms/{linode,proxmox,lxc}.nix` — platform-specific config:
|
||||||
config: boot method, guest tooling, and the hardware config, imported
|
boot method, guest tooling, and (for linode/proxmox) the hypervisor-specific
|
||||||
directly by the platform module itself — **not** wired in from
|
hardware config, imported directly by the platform module itself
|
||||||
`flake.nix`. VM platforms use `../hardware-configuration/vm/{proxmox,linode}.nix`;
|
(`../hardware-configuration/vm/{proxmox,linode}.nix`) — **not** wired in
|
||||||
`baremetal.nix` uses `../hardware-configuration/baremetal.nix` (adapted
|
from `flake.nix`. `lxc.nix` has no hardware-configuration counterpart since
|
||||||
from a real `nixos-generate-config` run on the actual hardware, not a
|
containers share the host kernel; instead it imports nixpkgs' own
|
||||||
vm/ file, since it isn't a VM) plus `hardware.enableRedistributableFirmware
|
|
||||||
= true` for real wifi/GPU/microcode firmware that VMs never needed.
|
|
||||||
`lxc.nix` has no hardware-configuration counterpart since containers
|
|
||||||
share the host kernel; instead it imports nixpkgs' own
|
|
||||||
`virtualisation/proxmox-lxc.nix`, which gives every `lxc-*` host a
|
`virtualisation/proxmox-lxc.nix`, which gives every `lxc-*` host a
|
||||||
`config.system.build.tarball` output — a plain rootfs tarball, used as a
|
`config.system.build.tarball` output — a plain rootfs tarball, used as a
|
||||||
`pct create ... vztmpl` CT template (**not** `pct restore`, which expects
|
`pct create ... vztmpl` CT template (**not** `pct restore`, which expects
|
||||||
@@ -404,16 +370,6 @@ removing a host.
|
|||||||
boots, so this declares them with `destroy = false` (disko never wipes
|
boots, so this declares them with `destroy = false` (disko never wipes
|
||||||
them) and a bare `filesystem`/`swap` content type instead of a partition
|
them) and a bare `filesystem`/`swap` content type instead of a partition
|
||||||
table — idempotent against an already-provisioned disk, never destructive.
|
table — idempotent against an already-provisioned disk, never destructive.
|
||||||
- `modules/disko/baremetal.nix` — `baremetal-gui`'s disko config: a ZFS
|
|
||||||
RAID0 (striped, no redundancy — disko's zpool `mode` defaults to `""`,
|
|
||||||
which is a plain stripe rather than `"mirror"`/`"raidz"`) root pool
|
|
||||||
across two disks, ESP + systemd-boot on the first. Device paths
|
|
||||||
(`vars.guiRootDisk1`/`guiRootDisk2`) are placeholders — fill in stable
|
|
||||||
`/dev/disk/by-id/...` paths before running disko for real.
|
|
||||||
`modules/platforms/baremetal.nix` also imports
|
|
||||||
`modules/services/zfs/enable-service.nix` for this (the `zfs_unstable`
|
|
||||||
package, autoScrub/autoSnapshot/trim) — the only other importer today is
|
|
||||||
`server`'s NFS data pool, an unrelated non-root ZFS use.
|
|
||||||
- `modules/boot/efi.nix` — systemd-boot + EFI vars, paired with the disko module.
|
- `modules/boot/efi.nix` — systemd-boot + EFI vars, paired with the disko module.
|
||||||
- `modules/installer/` — the auto-installer environment (ISO, also served as
|
- `modules/installer/` — the auto-installer environment (ISO, also served as
|
||||||
PXE netboot): `common.nix` (shared config + the generated
|
PXE netboot): `common.nix` (shared config + the generated
|
||||||
|
|||||||
@@ -8,15 +8,13 @@ workstation.
|
|||||||
Targets are named `<platform>-<buildtype>`, generated from two orthogonal
|
Targets are named `<platform>-<buildtype>`, generated from two orthogonal
|
||||||
pieces composed in `flake.nix`:
|
pieces composed in `flake.nix`:
|
||||||
|
|
||||||
- **Platforms** (what it runs on): `linode`, `proxmox`, `lxc`, `baremetal`
|
- **Platforms** (what it runs on): `linode`, `proxmox`, `lxc`
|
||||||
- **Build types** (what it's for): `minimal`, `nix-cache`, `server`, `docker`,
|
- **Build types** (what it's for): `minimal`, `nix-cache`, `server`, `docker`,
|
||||||
`gui`, `pxe-boot`, `tailscale-exit-node`, `tor-relay`
|
`gui`, `pxe-boot`, `tailscale-exit-node`, `tor-relay`
|
||||||
|
|
||||||
Not every combination exists — `pxe-boot` has no `linode` variant, since
|
Not every combination exists — `pxe-boot` has no `linode` variant, since
|
||||||
PXE/DHCP/TFTP need LAN L2 adjacency that a Linode VPS doesn't have,
|
PXE/DHCP/TFTP need LAN L2 adjacency that a Linode VPS doesn't have, and
|
||||||
`tor-relay` currently only exists as `lxc-tor-relay`, and `baremetal`
|
`tor-relay` currently only exists as `lxc-tor-relay`. The full list:
|
||||||
currently only exists as `baremetal-gui` (the real gui-host hardware). The
|
|
||||||
full list:
|
|
||||||
|
|
||||||
| Target | Purpose |
|
| Target | Purpose |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
@@ -27,7 +25,6 @@ full list:
|
|||||||
| `linode-server` / `proxmox-server` / `lxc-server` | Storage, NFS, backup, and monitoring exporter host — previously the flat `server` target |
|
| `linode-server` / `proxmox-server` / `lxc-server` | Storage, NFS, backup, and monitoring exporter host — previously the flat `server` target |
|
||||||
| `linode-docker` / `proxmox-docker` / `lxc-docker` | Docker host for the main container stack — previously the flat `docker` target |
|
| `linode-docker` / `proxmox-docker` / `lxc-docker` | Docker host for the main container stack — previously the flat `docker` target |
|
||||||
| `linode-gui` / `proxmox-gui` / `lxc-gui` | Cinnamon desktop workstation — previously the flat `nixos` target |
|
| `linode-gui` / `proxmox-gui` / `lxc-gui` | Cinnamon desktop workstation — previously the flat `nixos` target |
|
||||||
| `baremetal-gui` | Same Cinnamon desktop workstation, on the real gui-host hardware — ZFS RAID0 root, systemd-boot |
|
|
||||||
| `proxmox-pxe-boot` / `lxc-pxe-boot` | HTTP/iPXE boot asset host — previously the flat `pxe-boot` target |
|
| `proxmox-pxe-boot` / `lxc-pxe-boot` | HTTP/iPXE boot asset host — previously the flat `pxe-boot` target |
|
||||||
| `linode-tailscale-exit-node` / `proxmox-tailscale-exit-node` / `lxc-tailscale-exit-node` | Tailscale exit node |
|
| `linode-tailscale-exit-node` / `proxmox-tailscale-exit-node` / `lxc-tailscale-exit-node` | Tailscale exit node |
|
||||||
| `lxc-tor-relay` | Tor middle relay |
|
| `lxc-tor-relay` | Tor middle relay |
|
||||||
@@ -67,7 +64,7 @@ nix eval --json .#nixosConfigurations --apply builtins.attrNames | jq -r '.[]'
|
|||||||
| `variables.nix` | Single source of truth for shared values (LAN domain/CIDR, hostnames, timezone, primary username, storage root, NFS share subpaths/mountpoints, service ports, ...) — passed to every module and Home Manager config as the `vars` argument via `specialArgs`/`extraSpecialArgs` |
|
| `variables.nix` | Single source of truth for shared values (LAN domain/CIDR, hostnames, timezone, primary username, storage root, NFS share subpaths/mountpoints, service ports, ...) — passed to every module and Home Manager config as the `vars` argument via `specialArgs`/`extraSpecialArgs` |
|
||||||
| `hosts/<name>/host.nix` | Per-machine identity: hostname, hostId, per-machine secrets, `system.stateVersion` |
|
| `hosts/<name>/host.nix` | Per-machine identity: hostname, hostId, per-machine secrets, `system.stateVersion` |
|
||||||
| `hosts/nixos/home.nix` | Workstation-specific Home Manager config (used by the `gui` build type) |
|
| `hosts/nixos/home.nix` | Workstation-specific Home Manager config (used by the `gui` build type) |
|
||||||
| `modules/platforms/` | Platform-specific config: virtualisation guest tools, boot method, hardware config (`linode.nix`, `proxmox.nix`, `lxc.nix`, `baremetal.nix`) |
|
| `modules/platforms/` | Platform-specific config: virtualisation guest tools, boot method, hardware config (`linode.nix`, `proxmox.nix`, `lxc.nix`) |
|
||||||
| `modules/build-types/` | Build-type-specific config: what makes a system minimal/server/docker/gui/pxe-boot/nix-cache |
|
| `modules/build-types/` | Build-type-specific config: what makes a system minimal/server/docker/gui/pxe-boot/nix-cache |
|
||||||
| `modules/common/` | Shared NixOS config, Home Manager, aliases imported by every host |
|
| `modules/common/` | Shared NixOS config, Home Manager, aliases imported by every host |
|
||||||
| `modules/nix-cache/` | Binary cache and remote builder client/server modules |
|
| `modules/nix-cache/` | Binary cache and remote builder client/server modules |
|
||||||
|
|||||||
+6
-31
@@ -8,13 +8,10 @@ lives here.
|
|||||||
The installer provides a small NixOS install environment (ISO, or the same
|
The installer provides a small NixOS install environment (ISO, or the same
|
||||||
image netbooted via PXE) with SSH access, Git support, and an interactive
|
image netbooted via PXE) with SSH access, Git support, and an interactive
|
||||||
installation script.
|
installation script.
|
||||||
Logging in as any user (root or `nixos`) runs
|
Logging in as any user (root or `nixos`) runs `/etc/auto-install.sh`,
|
||||||
`/etc/nixos-installer/installer/auto-install.sh` (the same file as
|
discovers available hosts from this same flake, lets the operator choose a
|
||||||
`scripts/installer/auto-install.sh` in this repo — see "Installer process"
|
target, applies that host's Disko storage configuration, installs NixOS, and
|
||||||
below for why it's baked in at that path rather than a flat
|
reboots.
|
||||||
`/etc/auto-install.sh`), discovers available hosts from this same flake,
|
|
||||||
lets the operator choose a target, applies that host's Disko storage
|
|
||||||
configuration, installs NixOS, and reboots.
|
|
||||||
|
|
||||||
**This applies to every `nixosConfigurations` target except `lxc-*` hosts —
|
**This applies to every `nixosConfigurations` target except `lxc-*` hosts —
|
||||||
see "LXC hosts" immediately below for why those are different.**
|
see "LXC hosts" immediately below for why those are different.**
|
||||||
@@ -153,11 +150,7 @@ use case.
|
|||||||
|
|
||||||
The `pxe` variant is also built automatically as part of the `pxe-boot` host
|
The `pxe` variant is also built automatically as part of the `pxe-boot` host
|
||||||
itself (`modules/pxe-boot/stage-installer-artifacts.nix`) and served over
|
itself (`modules/pxe-boot/stage-installer-artifacts.nix`) and served over
|
||||||
iPXE as the menu's "NixOS Auto-Installer" entry — see `docs/pxe-boot.md`.
|
iPXE — see `docs/pxe-boot.md`.
|
||||||
That same host also builds and serves `packages.x86_64-linux.pxe-minimal`,
|
|
||||||
a vanilla NixOS minimal netboot image with none of this auto-installer's
|
|
||||||
wiring, as a separate "NixOS Minimal" menu entry — also documented in
|
|
||||||
`docs/pxe-boot.md`, not covered further here since it's not this installer.
|
|
||||||
|
|
||||||
## Host keys
|
## Host keys
|
||||||
|
|
||||||
@@ -195,10 +188,6 @@ default.
|
|||||||
`auto-install.sh` still supports the older manual path as a fallback: if a
|
`auto-install.sh` still supports the older manual path as a fallback: if a
|
||||||
host's key isn't baked in (`/etc/host-keys`), it checks `/root/host-keys`
|
host's key isn't baked in (`/etc/host-keys`), it checks `/root/host-keys`
|
||||||
next, where you can `scp` a key in after boot, same as before this migration.
|
next, where you can `scp` a key in after boot, same as before this migration.
|
||||||
If neither has it and the script is running interactively (an actual
|
|
||||||
operator at the other end of stdin, not an unattended run), it prompts for
|
|
||||||
an arbitrary directory to check (a mounted USB stick, another filesystem,
|
|
||||||
etc.) and copies the key pair into `/root/host-keys` from there if found.
|
|
||||||
|
|
||||||
## Storage
|
## Storage
|
||||||
|
|
||||||
@@ -224,21 +213,7 @@ entirely (see "LXC hosts" above), so it never reaches this code path.
|
|||||||
|
|
||||||
## Installer process
|
## Installer process
|
||||||
|
|
||||||
`scripts/installer/auto-install.sh` is a real, version-controlled shell
|
`/etc/auto-install.sh`:
|
||||||
script — not an inline Nix string. It sources `scripts/env.sh` for
|
|
||||||
`LAN_DOMAIN` itself (same as every other script in `scripts/`), so it
|
|
||||||
behaves identically whether it's run straight from a git checkout (e.g.
|
|
||||||
manually, from a stock NixOS ISO that isn't this repo's own installer
|
|
||||||
image) or from inside the built installer image. That's also why it's
|
|
||||||
baked in at `/etc/nixos-installer/installer/auto-install.sh` rather than a
|
|
||||||
flat `/etc/auto-install.sh` — `modules/installer/common.nix` bakes
|
|
||||||
`scripts/env.sh` in alongside it at `/etc/nixos-installer/env.sh`,
|
|
||||||
preserving the same relative layout (`installer/auto-install.sh` ->
|
|
||||||
`../env.sh`) the checked-out repo has, so the script's own
|
|
||||||
`source ".../env.sh"` line resolves correctly in both places without any
|
|
||||||
Nix-level templating.
|
|
||||||
|
|
||||||
Once running, it:
|
|
||||||
|
|
||||||
1. Queries `nixosConfigurations` from this flake over the network (`git+https://<lanDomain>/beatzaplenty/nixos.git`) — this happens at *install* time, not build time, so a generic installer image always sees whatever hosts are currently committed, without needing a rebuild.
|
1. Queries `nixosConfigurations` from this flake over the network (`git+https://<lanDomain>/beatzaplenty/nixos.git`) — this happens at *install* time, not build time, so a generic installer image always sees whatever hosts are currently committed, without needing a rebuild.
|
||||||
2. Presents them as a menu; confirms the choice.
|
2. Presents them as a menu; confirms the choice.
|
||||||
|
|||||||
+16
-35
@@ -1,10 +1,9 @@
|
|||||||
# pxe-boot
|
# pxe-boot
|
||||||
|
|
||||||
The `pxe-boot` host serves HTTP boot assets for iPXE clients — including
|
The `pxe-boot` host serves HTTP boot assets for iPXE clients — including a
|
||||||
self-staged copies of both this flake's own auto-installer netboot image
|
self-staged copy of this flake's own auto-installer netboot image, see
|
||||||
(see `docs/auto-installer.md` for what that image actually is and does once
|
`docs/auto-installer.md` for what that image actually is and does once
|
||||||
booted) and a vanilla, unmodified NixOS minimal netboot image for plain
|
booted.
|
||||||
rescue/inspection use.
|
|
||||||
|
|
||||||
## Host Role
|
## Host Role
|
||||||
|
|
||||||
@@ -29,8 +28,7 @@ The host creates these directories with systemd tmpfiles:
|
|||||||
/srv/pxe
|
/srv/pxe
|
||||||
/srv/pxe/http
|
/srv/pxe/http
|
||||||
/srv/pxe/http/images
|
/srv/pxe/http/images
|
||||||
/srv/pxe/http/auto-installer
|
/srv/pxe/http/nixos
|
||||||
/srv/pxe/http/nixos-minimal
|
|
||||||
/srv/pxe/http/systemrescue
|
/srv/pxe/http/systemrescue
|
||||||
/srv/pxe/http/ubuntu
|
/srv/pxe/http/ubuntu
|
||||||
/srv/pxe/http/rescue
|
/srv/pxe/http/rescue
|
||||||
@@ -39,7 +37,7 @@ The host creates these directories with systemd tmpfiles:
|
|||||||
|
|
||||||
Mount shared image storage under `/srv/pxe/http`, preferably
|
Mount shared image storage under `/srv/pxe/http`, preferably
|
||||||
`/srv/pxe/http/images` unless a menu entry expects files in a specific
|
`/srv/pxe/http/images` unless a menu entry expects files in a specific
|
||||||
directory such as `/srv/pxe/http/auto-installer`.
|
directory such as `/srv/pxe/http/nixos`.
|
||||||
|
|
||||||
The HTTP iPXE chain is:
|
The HTTP iPXE chain is:
|
||||||
|
|
||||||
@@ -52,37 +50,20 @@ undionly.kpxe or ipxe.efi
|
|||||||
|
|
||||||
The generated menu currently exposes entries for:
|
The generated menu currently exposes entries for:
|
||||||
|
|
||||||
- NixOS Auto-Installer
|
- NixOS installer
|
||||||
- NixOS Minimal
|
|
||||||
- SystemRescue environment
|
- SystemRescue environment
|
||||||
- iPXE shell
|
- iPXE shell
|
||||||
- Reboot
|
- Reboot
|
||||||
|
|
||||||
Both NixOS entries chain-load a `netboot.ipxe` staged into their own
|
The NixOS installer entry chain-loads `/srv/pxe/http/nixos/netboot.ipxe`,
|
||||||
directory (`/srv/pxe/http/auto-installer/netboot.ipxe` and
|
which is nixpkgs' own generated netboot iPXE script (correct `init=`/`initrd=`
|
||||||
`/srv/pxe/http/nixos-minimal/netboot.ipxe`), each nixpkgs' own generated
|
kernel parameters included) rather than a hand-rolled boot line — that script
|
||||||
netboot iPXE script (correct `init=`/`initrd=` kernel parameters included)
|
in turn expects its kernel/initrd siblings in the same directory. All three
|
||||||
rather than a hand-rolled boot line — that script in turn expects its
|
files (`bzImage`, `initrd`, `netboot.ipxe`) are built from this flake's own
|
||||||
kernel/initrd siblings in the same directory. Each directory's three files
|
`modules/installer/iso.nix` netboot image (the same one `nix build .#pxe`
|
||||||
(`bzImage`, `initrd`, `netboot.ipxe`) are built from source and staged
|
produces) and staged automatically by
|
||||||
automatically by `modules/pxe-boot/stage-installer-artifacts.nix` via
|
`modules/pxe-boot/stage-installer-artifacts.nix` via `systemd.tmpfiles.rules`
|
||||||
`systemd.tmpfiles.rules` — no manual operator step required:
|
— no manual operator step required.
|
||||||
|
|
||||||
- `auto-installer` is this flake's own `netbootSystem` (`flake.nix`) — the
|
|
||||||
same auto-installer image `nix build .#pxe` produces. See
|
|
||||||
`docs/auto-installer.md`.
|
|
||||||
- `nixos-minimal` is `netbootMinimalSystem` (`flake.nix`) — nixpkgs'
|
|
||||||
`netboot-minimal.nix` composed on its own, with none of this flake's
|
|
||||||
auto-installer wiring (no `common.nix`, no `auto-install.sh`, no baked
|
|
||||||
host keys or custom users). Same `nix build .#pxe-minimal` mechanism as
|
|
||||||
the auto-installer image, just a different module composition. Useful
|
|
||||||
as a plain rescue/inspection shell that doesn't assume anything about
|
|
||||||
this flake.
|
|
||||||
|
|
||||||
Both images set `networking.hostName` to match their menu entry/staged
|
|
||||||
directory name (`auto-installer` / `nixos-minimal`), so each one's
|
|
||||||
generated system name (`nixos-system-<name>-*`) is self-describing rather
|
|
||||||
than the nixpkgs default of `nixos-system-nixos-*` for both.
|
|
||||||
|
|
||||||
The SystemRescue entry expects the source ISO at:
|
The SystemRescue entry expects the source ISO at:
|
||||||
|
|
||||||
|
|||||||
Generated
+6
-6
@@ -95,11 +95,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1784350909,
|
"lastModified": 1783740085,
|
||||||
"narHash": "sha256-ZWyzLbS1yKUTeFJLmdVuWNnHttL333/ldJbEE+KzCrM=",
|
"narHash": "sha256-qajyHfZY29G2oEQk+uHxmsJcRoBUBXP9maTpFlwP/dI=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "4ce190229c73d44536caa7072f6308fb2d8feeb3",
|
"rev": "3cd22efe6471dc7365c822bd9ad73a21e55f38fb",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -147,11 +147,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1784432872,
|
"lastModified": 1784011430,
|
||||||
"narHash": "sha256-n3gKTBIV4ZA5VQpUakffBe3KGu4+mhPoA34rrqS0GkA=",
|
"narHash": "sha256-lDebytrYdd47IBLwvNOD+6AGeoqZ78CIKlp70hzW280=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "fd1462031fdee08f65fd0b4c6b64e22239a77870",
|
"rev": "8eeec934ae0dbeca3d7868c059568a65c08b2fc3",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
# file without a same-option circular dependency (a module
|
# file without a same-option circular dependency (a module
|
||||||
# contributing to environment.etc can't read the merged
|
# contributing to environment.etc can't read the merged
|
||||||
# environment.etc it's itself contributing to).
|
# environment.etc it's itself contributing to).
|
||||||
specialArgs = { inherit inputs vars netbootSystem netbootMinimalSystem flakeTarget; };
|
specialArgs = { inherit inputs vars netbootSystem flakeTarget; };
|
||||||
};
|
};
|
||||||
|
|
||||||
# Generated platform x build-type matrix. pxe-boot has no linode
|
# Generated platform x build-type matrix. pxe-boot has no linode
|
||||||
@@ -91,7 +91,6 @@
|
|||||||
linode-gui = mkTarget { platform = "linode"; buildType = "gui"; hostPath = ./hosts/nixos/host.nix; homeFile = ./hosts/nixos/home.nix; };
|
linode-gui = mkTarget { platform = "linode"; buildType = "gui"; hostPath = ./hosts/nixos/host.nix; homeFile = ./hosts/nixos/home.nix; };
|
||||||
proxmox-gui = mkTarget { platform = "proxmox"; buildType = "gui"; hostPath = ./hosts/nixos/host.nix; homeFile = ./hosts/nixos/home.nix; };
|
proxmox-gui = mkTarget { platform = "proxmox"; buildType = "gui"; hostPath = ./hosts/nixos/host.nix; homeFile = ./hosts/nixos/home.nix; };
|
||||||
lxc-gui = mkTarget { platform = "lxc"; buildType = "gui"; hostPath = ./hosts/nixos/host.nix; homeFile = ./hosts/nixos/home.nix; };
|
lxc-gui = mkTarget { platform = "lxc"; buildType = "gui"; hostPath = ./hosts/nixos/host.nix; homeFile = ./hosts/nixos/home.nix; };
|
||||||
baremetal-gui = mkTarget { platform = "baremetal"; buildType = "gui"; hostPath = ./hosts/nixos/host.nix; homeFile = ./hosts/nixos/home.nix; };
|
|
||||||
|
|
||||||
proxmox-pxe-boot = mkTarget { platform = "proxmox"; buildType = "pxe-boot"; hostPath = ./hosts/pxe-boot/host.nix; };
|
proxmox-pxe-boot = mkTarget { platform = "proxmox"; buildType = "pxe-boot"; hostPath = ./hosts/pxe-boot/host.nix; };
|
||||||
lxc-pxe-boot = mkTarget { platform = "lxc"; buildType = "pxe-boot"; hostPath = ./hosts/pxe-boot/host.nix; };
|
lxc-pxe-boot = mkTarget { platform = "lxc"; buildType = "pxe-boot"; hostPath = ./hosts/pxe-boot/host.nix; };
|
||||||
@@ -120,61 +119,19 @@
|
|||||||
|
|
||||||
# Same installer environment, built as netboot (kernel + initrd +
|
# Same installer environment, built as netboot (kernel + initrd +
|
||||||
# iPXE script) instead of an ISO — this is what packages.pxe bundles.
|
# iPXE script) instead of an ISO — this is what packages.pxe bundles.
|
||||||
#
|
|
||||||
# Deliberately imports common.nix directly, NOT ./modules/installer/iso.nix
|
|
||||||
# (which pulls in nixpkgs' installation-cd-minimal.nix) -- confirmed live
|
|
||||||
# that composing the ISO module together with netboot-minimal.nix hangs
|
|
||||||
# every boot waiting for a device that can never exist on a netboot
|
|
||||||
# client ("A start job is running for /dev/disk/by-label/nixos-minimal-...").
|
|
||||||
# Both installation-cd-base.nix and netboot.nix set fileSystems."/" via
|
|
||||||
# the identical lib.mkImageMediaOverride (mkOverride 60) priority --
|
|
||||||
# genuinely conflicting root-filesystem strategies (ISO-by-label vs.
|
|
||||||
# netboot-tmpfs) at the same priority, and the ISO one was winning.
|
|
||||||
# netboot-minimal.nix's own chain (netboot-base.nix) already imports
|
|
||||||
# profiles/installation-device.nix independently, so common.nix's
|
|
||||||
# initialHashedPassword override (which assumes that profile is
|
|
||||||
# present) still applies correctly without iso.nix in the mix.
|
|
||||||
#
|
|
||||||
# networking.hostName is set explicitly (rather than left at nixpkgs'
|
|
||||||
# own "nixos" default) so this image's generated system name
|
|
||||||
# (nixos-system-auto-installer-*) matches its iPXE menu entry —
|
|
||||||
# see modules/build-types/pxe-boot.nix's :auto-installer item — and
|
|
||||||
# its staged directory, /srv/pxe/http/auto-installer.
|
|
||||||
netbootSystem = nixpkgs.lib.nixosSystem {
|
netbootSystem = nixpkgs.lib.nixosSystem {
|
||||||
inherit system;
|
inherit system;
|
||||||
modules = [
|
modules = [
|
||||||
./modules/installer/common.nix
|
./modules/installer/iso.nix
|
||||||
({ modulesPath, ... }: {
|
({ modulesPath, ... }: {
|
||||||
imports = [
|
imports = [
|
||||||
(modulesPath + "/installer/netboot/netboot-minimal.nix")
|
(modulesPath + "/installer/netboot/netboot-minimal.nix")
|
||||||
];
|
];
|
||||||
})
|
})
|
||||||
{ networking.hostName = "auto-installer"; }
|
|
||||||
];
|
];
|
||||||
specialArgs = { inherit vars; };
|
specialArgs = { inherit vars; };
|
||||||
};
|
};
|
||||||
|
|
||||||
# A genuinely vanilla NixOS minimal netboot image: nixpkgs'
|
|
||||||
# netboot-minimal.nix on its own, with none of this flake's
|
|
||||||
# auto-installer wiring (no common.nix — no auto-install.sh, no
|
|
||||||
# baked host keys, no custom users/passwords). Built from source via
|
|
||||||
# the same nixosSystem + netboot-minimal.nix path as netbootSystem
|
|
||||||
# above, so both go through an identical build mechanism; the only
|
|
||||||
# difference is what's composed in. hostName again matches this
|
|
||||||
# image's iPXE menu entry (:nixos-minimal) and staged directory
|
|
||||||
# (/srv/pxe/http/nixos-minimal).
|
|
||||||
netbootMinimalSystem = nixpkgs.lib.nixosSystem {
|
|
||||||
inherit system;
|
|
||||||
modules = [
|
|
||||||
({ modulesPath, ... }: {
|
|
||||||
imports = [
|
|
||||||
(modulesPath + "/installer/netboot/netboot-minimal.nix")
|
|
||||||
];
|
|
||||||
})
|
|
||||||
{ networking.hostName = "nixos-minimal"; }
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -196,15 +153,6 @@
|
|||||||
{ name = "initrd"; path = netbootSystem.config.system.build.netbootRamdisk; }
|
{ name = "initrd"; path = netbootSystem.config.system.build.netbootRamdisk; }
|
||||||
{ name = "kernel"; path = netbootSystem.config.system.build.kernel; }
|
{ name = "kernel"; path = netbootSystem.config.system.build.kernel; }
|
||||||
];
|
];
|
||||||
|
|
||||||
# Vanilla NixOS minimal netboot bundle — see netbootMinimalSystem
|
|
||||||
# above. Staged onto the pxe-boot host alongside packages.pxe by
|
|
||||||
# modules/pxe-boot/stage-installer-artifacts.nix.
|
|
||||||
pxe-minimal = pkgs.linkFarm "pxe-minimal" [
|
|
||||||
{ name = "netboot.ipxe"; path = netbootMinimalSystem.config.system.build.netbootIpxeScript; }
|
|
||||||
{ name = "initrd"; path = netbootMinimalSystem.config.system.build.netbootRamdisk; }
|
|
||||||
{ name = "kernel"; path = netbootMinimalSystem.config.system.build.kernel; }
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,8 @@
|
|||||||
_:
|
_:
|
||||||
|
|
||||||
{
|
{
|
||||||
imports = [
|
|
||||||
../../modules/networking/wifi.nix
|
|
||||||
];
|
|
||||||
|
|
||||||
networking.hostName = "nixos";
|
networking.hostName = "nixos";
|
||||||
|
|
||||||
# Only needed now that baremetal-gui exists (ZFS root) -- harmless on the
|
|
||||||
# ext4-rooted linode/proxmox/lxc-gui variants, so set unconditionally
|
|
||||||
# rather than only on the baremetal platform.
|
|
||||||
networking.hostId = "de6a9ffc";
|
|
||||||
|
|
||||||
# Preserved from the pre-refactor `nixos` target — stateVersion must never
|
# Preserved from the pre-refactor `nixos` target — stateVersion must never
|
||||||
# be bumped on an already-installed machine.
|
# be bumped on an already-installed machine.
|
||||||
system.stateVersion = "25.05";
|
system.stateVersion = "25.05";
|
||||||
|
|||||||
@@ -68,19 +68,15 @@ let
|
|||||||
set base ${pxeBaseUrl}
|
set base ${pxeBaseUrl}
|
||||||
|
|
||||||
menu PXE Boot Menu
|
menu PXE Boot Menu
|
||||||
item auto-installer NixOS Auto-Installer
|
item nixos NixOS Installer
|
||||||
item nixos-minimal NixOS Minimal
|
|
||||||
item rescue Rescue Environment
|
item rescue Rescue Environment
|
||||||
item shell iPXE Shell
|
item shell iPXE Shell
|
||||||
item reboot Reboot
|
item reboot Reboot
|
||||||
|
|
||||||
choose target && goto ''${target}
|
choose target && goto ''${target}
|
||||||
|
|
||||||
:auto-installer
|
:nixos
|
||||||
chain ''${base}/auto-installer/netboot.ipxe
|
chain ''${base}/nixos/netboot.ipxe
|
||||||
|
|
||||||
:nixos-minimal
|
|
||||||
chain ''${base}/nixos-minimal/netboot.ipxe
|
|
||||||
|
|
||||||
:rescue
|
:rescue
|
||||||
chain ''${base}/systemrescue.ipxe
|
chain ''${base}/systemrescue.ipxe
|
||||||
@@ -133,8 +129,7 @@ in
|
|||||||
"d ${pxeRoot} 0755 root root -"
|
"d ${pxeRoot} 0755 root root -"
|
||||||
"d ${httpRoot} 0755 root root -"
|
"d ${httpRoot} 0755 root root -"
|
||||||
"d ${httpRoot}/images 0755 root root -"
|
"d ${httpRoot}/images 0755 root root -"
|
||||||
"d ${httpRoot}/auto-installer 0755 root root -"
|
"d ${httpRoot}/nixos 0755 root root -"
|
||||||
"d ${httpRoot}/nixos-minimal 0755 root root -"
|
|
||||||
"d ${httpRoot}/systemrescue 0755 root root -"
|
"d ${httpRoot}/systemrescue 0755 root root -"
|
||||||
"d ${httpRoot}/ubuntu 0755 root root -"
|
"d ${httpRoot}/ubuntu 0755 root root -"
|
||||||
"d ${httpRoot}/rescue 0755 root root -"
|
"d ${httpRoot}/rescue 0755 root root -"
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
{ vars, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
# ZFS RAID0 (striped, no redundancy) root pool for the bare-metal gui
|
|
||||||
# host — two disks, each contributing its own top-level vdev. disko's
|
|
||||||
# zpool `mode` defaults to "" (plain stripe) when left unset, which is
|
|
||||||
# what gives RAID0 semantics here rather than mirror/raidz.
|
|
||||||
#
|
|
||||||
# Device paths are placeholders until the real hardware profile lands —
|
|
||||||
# fill in vars.guiRootDisk1/guiRootDisk2 (stable /dev/disk/by-id/...
|
|
||||||
# paths, not /dev/sdX) before running disko against real hardware. Swap
|
|
||||||
# is deliberately left out for now — sizing that sensibly needs the
|
|
||||||
# box's actual RAM size, which comes with the hardware profile too.
|
|
||||||
#
|
|
||||||
# Not yet imported anywhere: this awaits the new bare-metal platform
|
|
||||||
# module (alongside modules/boot/efi.nix for systemd-boot, matching
|
|
||||||
# modules/platforms/proxmox.nix's pattern) once the hardware config is
|
|
||||||
# in hand.
|
|
||||||
disko.devices = {
|
|
||||||
disk = {
|
|
||||||
disk1 = {
|
|
||||||
type = "disk";
|
|
||||||
device = vars.guiRootDisk1;
|
|
||||||
|
|
||||||
content = {
|
|
||||||
type = "gpt";
|
|
||||||
|
|
||||||
partitions = {
|
|
||||||
esp = {
|
|
||||||
priority = 1;
|
|
||||||
name = "ESP";
|
|
||||||
size = "512M";
|
|
||||||
type = "EF00";
|
|
||||||
|
|
||||||
content = {
|
|
||||||
type = "filesystem";
|
|
||||||
format = "vfat";
|
|
||||||
mountpoint = "/boot";
|
|
||||||
mountOptions = [ "umask=0077" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
zfs = {
|
|
||||||
size = "100%";
|
|
||||||
|
|
||||||
content = {
|
|
||||||
type = "zfs";
|
|
||||||
pool = "rpool";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
disk2 = {
|
|
||||||
type = "disk";
|
|
||||||
device = vars.guiRootDisk2;
|
|
||||||
|
|
||||||
content = {
|
|
||||||
type = "gpt";
|
|
||||||
|
|
||||||
partitions = {
|
|
||||||
zfs = {
|
|
||||||
size = "100%";
|
|
||||||
|
|
||||||
content = {
|
|
||||||
type = "zfs";
|
|
||||||
pool = "rpool";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
zpool.rpool = {
|
|
||||||
type = "zpool";
|
|
||||||
|
|
||||||
rootFsOptions = {
|
|
||||||
compression = "zstd";
|
|
||||||
"com.sun:auto-snapshot" = "false";
|
|
||||||
};
|
|
||||||
mountpoint = "/";
|
|
||||||
options.ashift = "12";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# Adapted from the output of `nixos-generate-config`, run from a live GUI
|
|
||||||
# ISO boot on the actual gui-host hardware (AMD CPU). fileSystems and
|
|
||||||
# swapDevices are deliberately omitted -- the live ISO had no formatted
|
|
||||||
# disks to detect, and disko (modules/disko/baremetal.nix) generates both
|
|
||||||
# from the declarative zpool layout anyway.
|
|
||||||
{ config, lib, pkgs, modulesPath, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
imports =
|
|
||||||
[
|
|
||||||
(modulesPath + "/installer/scan/not-detected.nix")
|
|
||||||
];
|
|
||||||
|
|
||||||
boot = {
|
|
||||||
initrd.availableKernelModules = [ "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
|
|
||||||
initrd.kernelModules = [ ];
|
|
||||||
kernelModules = [ "kvm-amd" ];
|
|
||||||
extraModulePackages = [ ];
|
|
||||||
};
|
|
||||||
|
|
||||||
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
|
||||||
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
|
|
||||||
}
|
|
||||||
+133
-14
@@ -45,23 +45,142 @@
|
|||||||
disko
|
disko
|
||||||
];
|
];
|
||||||
|
|
||||||
# Auto-install script, kept as a real, version-controlled shell file at
|
# Write auto-install script to /root
|
||||||
# scripts/installer/auto-install.sh rather than an inline Nix string.
|
etc."auto-install.sh" = {
|
||||||
# It sources scripts/env.sh itself (for LAN_DOMAIN, same as every other
|
text = ''
|
||||||
# script in this repo) rather than relying on Nix-level templating, so
|
#!/run/current-system/sw/bin/bash
|
||||||
# it behaves identically whether it's run straight from a git checkout
|
set -eux
|
||||||
# or from here -- baking scripts/env.sh in alongside it at a matching
|
|
||||||
# relative path (installer/auto-install.sh -> ../env.sh) is what makes
|
set -euo pipefail
|
||||||
# that resolve correctly in both places.
|
|
||||||
etc = {
|
export FLAKE_BASE_URL="git+https://${vars.lanDomain}/beatzaplenty/nixos.git"
|
||||||
"nixos-installer/env.sh".source = ../../scripts/env.sh;
|
|
||||||
|
echo "Fetching available NixOS hosts from flake..."
|
||||||
|
# Two categories deliberately excluded from the menu:
|
||||||
|
# lxc-* — these build a config.system.build.tarball meant for
|
||||||
|
# `pct restore` on Proxmox directly, not an install.
|
||||||
|
# Running nixos-install against one here would
|
||||||
|
# bind-mount / onto /mnt and then refuse to touch the
|
||||||
|
# filesystem it's currently running on — see
|
||||||
|
# docs/auto-installer.md.
|
||||||
|
# installer — this *is* the installer image's own flake target,
|
||||||
|
# not a deployable host; "installing" it means
|
||||||
|
# nixos-install-ing a copy of the installer into
|
||||||
|
# itself.
|
||||||
|
mapfile -t options < <(
|
||||||
|
nix eval --json --no-use-registries --no-accept-flake-config --extra-experimental-features "flakes nix-command" \
|
||||||
|
"''${FLAKE_BASE_URL}#nixosConfigurations" \
|
||||||
|
--apply builtins.attrNames \
|
||||||
|
| jq -r '.[]
|
||||||
|
| select(startswith("lxc-") | not)
|
||||||
|
| select(. != "installer")'
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ ''${#options[@]} -eq 0 ]]; then
|
||||||
|
echo "ERROR: No NixOS hosts found in ''${FLAKE_BASE_URL}#nixosConfigurations" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Note: lxc-* targets aren't installed this way — build them with"
|
||||||
|
echo " nix build .#nixosConfigurations.<name>.config.system.build.tarball"
|
||||||
|
echo "and 'pct restore' the result on Proxmox directly. See docs/auto-installer.md."
|
||||||
|
|
||||||
|
echo "Choose the flake profile to install:"
|
||||||
|
select choice in "''${options[@]}"; do
|
||||||
|
if [[ -n "$choice" ]]; then
|
||||||
|
echo "You selected: $choice"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo "Invalid selection. Try again."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Starting install with flake: ''${FLAKE_BASE_URL}#''${choice}"
|
||||||
|
|
||||||
|
# Optional: confirm before proceeding
|
||||||
|
read -rp "Proceed with installation? (y/N): " confirm
|
||||||
|
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
|
||||||
|
echo "Aborted."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# A nix-cache host is *the* substituter/remote-builder for every other
|
||||||
|
# host once installed (its own config explicitly excludes itself from
|
||||||
|
# using either — see buildType != "nix-cache" in the nixos flake.nix).
|
||||||
|
# Installing one shouldn't depend on a nix-cache substituter either,
|
||||||
|
# for the same reason — plus in practice "nix-cache" only resolves over
|
||||||
|
# Tailscale, which a fresh installer environment was never connected to
|
||||||
|
# anyway, so it's dead weight even for non-nix-cache installs until
|
||||||
|
# that's sorted out. Override it away here specifically for nix-cache
|
||||||
|
# targets to keep install-time behaviour consistent with run-time.
|
||||||
|
nix_extra_opts=()
|
||||||
|
if [[ "''${choice}" == *-nix-cache ]]; then
|
||||||
|
echo "Installing a nix-cache host — skipping the nix-cache substituter."
|
||||||
|
nix_extra_opts+=(--option substituters "https://cache.nixos.org/")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Every host reachable through this menu has a Disko config (lxc-*
|
||||||
|
# is filtered out above, and is the only category that doesn't —
|
||||||
|
# see docs/auto-installer.md), so this can run unconditionally: no
|
||||||
|
# need to probe the flake first and branch on whether Disko applies.
|
||||||
|
disko --mode destroy,format,mount \
|
||||||
|
--flake "''${FLAKE_BASE_URL}#''${choice}" "''${nix_extra_opts[@]}" --yes-wipe-all-disks
|
||||||
|
|
||||||
|
# sops-nix derives this host's decryption key from its own SSH host key
|
||||||
|
# at *activation* time, which runs before systemd would otherwise
|
||||||
|
# generate one on first boot. Without pre-seeding it here, secrets
|
||||||
|
# (including the login password) fail to decrypt on first boot.
|
||||||
|
# Generate the key with scripts/secrets/prepare-host-key.sh first.
|
||||||
|
#
|
||||||
|
# Two places a key can come from, checked in order:
|
||||||
|
# /etc/host-keys — baked into this image at build time (see
|
||||||
|
# modules/installer/host-keys.nix; only present
|
||||||
|
# if built with NIXOS_HOST_KEYS_DIR set)
|
||||||
|
# /root/host-keys — scp'd in manually after boot (older fallback,
|
||||||
|
# still supported for images built without keys)
|
||||||
|
mkdir -p /root/host-keys
|
||||||
|
if [[ -f "/etc/host-keys/''${choice}_ssh_host_ed25519_key" ]]; then
|
||||||
|
echo "Found baked-in SSH host key for ''${choice}, installing to target..."
|
||||||
|
install -D -m 0600 "/etc/host-keys/''${choice}_ssh_host_ed25519_key" /mnt/etc/ssh/ssh_host_ed25519_key
|
||||||
|
install -D -m 0644 "/etc/host-keys/''${choice}_ssh_host_ed25519_key.pub" /mnt/etc/ssh/ssh_host_ed25519_key.pub
|
||||||
|
elif [[ -f "/root/host-keys/''${choice}_ssh_host_ed25519_key" ]]; then
|
||||||
|
echo "Found pre-seeded SSH host key for ''${choice}, installing to target..."
|
||||||
|
install -D -m 0600 "/root/host-keys/''${choice}_ssh_host_ed25519_key" /mnt/etc/ssh/ssh_host_ed25519_key
|
||||||
|
install -D -m 0644 "/root/host-keys/''${choice}_ssh_host_ed25519_key.pub" /mnt/etc/ssh/ssh_host_ed25519_key.pub
|
||||||
|
else
|
||||||
|
echo "WARNING: no SSH host key found for ''${choice} (checked /etc/host-keys and /root/host-keys)"
|
||||||
|
echo "sops-nix secrets (including the login password) will NOT decrypt on first boot."
|
||||||
|
echo "Run scripts/secrets/prepare-host-key.sh for host ''${choice} on your admin workstation first,"
|
||||||
|
echo "then either rebuild this image with NIXOS_HOST_KEYS_DIR set, or scp the result to"
|
||||||
|
echo "/root/host-keys/ on this machine."
|
||||||
|
read -rp "Continue without a pre-seeded key anyway? (y/N): " skip_key
|
||||||
|
if [[ ! "$skip_key" =~ ^[Yy]$ ]]; then
|
||||||
|
echo "Aborted."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p /mnt/install-tmp
|
||||||
|
export TMPDIR=/mnt/install-tmp
|
||||||
|
|
||||||
|
nixos-install \
|
||||||
|
--flake "''${FLAKE_BASE_URL}#''${choice}" \
|
||||||
|
"''${nix_extra_opts[@]}" \
|
||||||
|
--no-root-password
|
||||||
|
|
||||||
|
|
||||||
|
rm -rf /mnt/install-tmp
|
||||||
|
# Redundant copy of the host's private key — the real one is now at
|
||||||
|
# /etc/ssh/ssh_host_ed25519_key. Nothing NixOS-managed ever cleans this
|
||||||
|
# up on its own since it was written imperatively, not declaratively.
|
||||||
|
rm -rf /root/host-keys
|
||||||
|
sleep 10
|
||||||
|
reboot
|
||||||
|
'';
|
||||||
|
|
||||||
"nixos-installer/installer/auto-install.sh" = {
|
|
||||||
source = ../../scripts/installer/auto-install.sh;
|
|
||||||
mode = "0755";
|
mode = "0755";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
programs.git.enable = true;
|
programs.git.enable = true;
|
||||||
|
|
||||||
@@ -73,7 +192,7 @@
|
|||||||
# file-copying/chown.
|
# file-copying/chown.
|
||||||
programs.bash.loginShellInit = ''
|
programs.bash.loginShellInit = ''
|
||||||
if [ -n "$PS1" ] && [ ! -e "$HOME/.auto_install_ran" ]; then
|
if [ -n "$PS1" ] && [ ! -e "$HOME/.auto_install_ran" ]; then
|
||||||
sudo /etc/nixos-installer/installer/auto-install.sh
|
sudo /etc/auto-install.sh
|
||||||
touch "$HOME/.auto_install_ran"
|
touch "$HOME/.auto_install_ran"
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
{ config, lib, vars, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
# Prestages a NetworkManager connection profile for vars.wifiSsid so the
|
|
||||||
# host associates on first boot with no manual nmtui/nmcli step. Guarded
|
|
||||||
# on a non-empty SSID so leaving the placeholder blank in variables.nix
|
|
||||||
# is a no-op rather than an empty, broken profile — fill it in once the
|
|
||||||
# network is known.
|
|
||||||
#
|
|
||||||
# The password itself lives in secrets/gui.yaml, not variables.nix --
|
|
||||||
# NetworkManager's ensureProfiles renders `psk = "$WIFI_PASSWORD"`
|
|
||||||
# literally into the store (see nixpkgs' own ensureProfiles example,
|
|
||||||
# which does the same for exactly this reason) and its systemd service
|
|
||||||
# envsubst-expands it from environmentFiles at activation time, so the
|
|
||||||
# real value only ever touches /run (root-only, UMask 0177), never the
|
|
||||||
# Nix store.
|
|
||||||
sops.secrets."wifi-password" = lib.mkIf (vars.wifiSsid != "") {
|
|
||||||
sopsFile = ../../secrets/gui.yaml;
|
|
||||||
};
|
|
||||||
|
|
||||||
sops.templates."wifi-password.env" = lib.mkIf (vars.wifiSsid != "") {
|
|
||||||
content = "WIFI_PASSWORD=${config.sops.placeholder."wifi-password"}";
|
|
||||||
};
|
|
||||||
|
|
||||||
networking.networkmanager.ensureProfiles = lib.mkIf (vars.wifiSsid != "") {
|
|
||||||
environmentFiles = [ config.sops.templates."wifi-password.env".path ];
|
|
||||||
|
|
||||||
profiles.${vars.wifiSsid} = {
|
|
||||||
connection = {
|
|
||||||
id = vars.wifiSsid;
|
|
||||||
type = "wifi";
|
|
||||||
};
|
|
||||||
wifi = {
|
|
||||||
mode = "infrastructure";
|
|
||||||
ssid = vars.wifiSsid;
|
|
||||||
};
|
|
||||||
wifi-security = {
|
|
||||||
key-mgmt = "wpa-psk";
|
|
||||||
psk = "$WIFI_PASSWORD";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
{ ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
../hardware-configuration/baremetal.nix
|
|
||||||
../boot/efi.nix
|
|
||||||
../disko/baremetal.nix
|
|
||||||
../services/zfs/enable-service.nix
|
|
||||||
];
|
|
||||||
|
|
||||||
# Needed for real wifi/bluetooth/GPU firmware blobs and CPU microcode
|
|
||||||
# updates (hardware-configuration/baremetal.nix's amd.updateMicrocode
|
|
||||||
# keys off this) -- irrelevant on the linode/proxmox/lxc platforms,
|
|
||||||
# which are all VMs with no real hardware to load firmware for.
|
|
||||||
hardware.enableRedistributableFirmware = true;
|
|
||||||
|
|
||||||
# AMD GPU: the amdgpu kernel driver autoloads from the PCI ID with no
|
|
||||||
# extra boot.kernelModules entry needed; this is the userspace half --
|
|
||||||
# the dedicated Xorg driver (not just the generic modesetting fallback)
|
|
||||||
# plus Mesa OpenGL/Vulkan (amdgpu/RADV), same firmware blobs as above.
|
|
||||||
# 32-bit support is for compatibility with 32-bit apps/games.
|
|
||||||
services.xserver.videoDrivers = [ "amdgpu" ];
|
|
||||||
|
|
||||||
hardware.graphics = {
|
|
||||||
enable = true;
|
|
||||||
enable32Bit = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
# The systemd-based initrd (default here since this host has a ZFS root --
|
|
||||||
# see modules/disko/baremetal.nix) locks the root account by default, so
|
|
||||||
# sulogin refuses to hand over a shell if something in the initrd (e.g.
|
|
||||||
# the ZFS pool import) fails and it drops to emergency mode -- confirmed
|
|
||||||
# live: it just loops re-entering the target instead of prompting. This
|
|
||||||
# only affects the pre-switch-root initrd shell, not the installed
|
|
||||||
# system's own login, and is worth the tradeoff on a box already reachable
|
|
||||||
# at the physical console.
|
|
||||||
boot.initrd.systemd.emergencyAccess = true;
|
|
||||||
}
|
|
||||||
@@ -1,32 +1,23 @@
|
|||||||
{ netbootSystem, netbootMinimalSystem, ... }:
|
{ netbootSystem, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
# config.system.build.kernel and .netbootRamdisk are directories, not the
|
# config.system.build.kernel and .netbootRamdisk are directories, not the
|
||||||
# files themselves — nixpkgs' own system.build.kexecTree does the same
|
# files themselves — nixpkgs' own system.build.kexecTree does the same
|
||||||
# ${...}/<file> dereference for the same reason.
|
# ${...}/<file> dereference for the same reason.
|
||||||
mkStageRules = { dirName, system }:
|
inherit (netbootSystem.config.system.boot.loader) kernelFile;
|
||||||
let
|
|
||||||
inherit (system.config.system.boot.loader) kernelFile;
|
|
||||||
dir = "/srv/pxe/http/${dirName}";
|
|
||||||
in
|
|
||||||
[
|
|
||||||
# Declared here too (not just in build-types/pxe-boot.nix) so this
|
|
||||||
# module's C+ rules don't depend on cross-module list-merge ordering —
|
|
||||||
# tmpfiles' C type needs the target directory to already exist.
|
|
||||||
"d ${dir} 0755 root root -"
|
|
||||||
"C+ ${dir}/${kernelFile} 0644 root root - ${system.config.system.build.kernel}/${kernelFile}"
|
|
||||||
"C+ ${dir}/initrd 0644 root root - ${system.config.system.build.netbootRamdisk}/initrd"
|
|
||||||
"C+ ${dir}/netboot.ipxe 0644 root root - ${system.config.system.build.netbootIpxeScript}/netboot.ipxe"
|
|
||||||
];
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
# Builds this flake's own installer netboot image (the same one
|
# Builds this flake's own installer netboot image (the same one
|
||||||
# `nix build .#pxe` produces) plus the vanilla NixOS minimal netboot image
|
# `nix build .#pxe` produces) and stages it where menu.ipxe's :nixos
|
||||||
# (`nix build .#pxe-minimal`), and stages both where menu.ipxe's
|
# entry expects it, so the pxe-boot host is self-contained — no manual
|
||||||
# :auto-installer / :nixos-minimal entries expect them, so the pxe-boot
|
# operator step to populate /srv/pxe/http/nixos after deploy.
|
||||||
# host is self-contained — no manual operator step to populate
|
systemd.tmpfiles.rules = [
|
||||||
# /srv/pxe/http after deploy.
|
# Declared here too (not just in build-types/pxe-boot.nix) so this
|
||||||
systemd.tmpfiles.rules =
|
# module's C+ rules don't depend on cross-module list-merge ordering —
|
||||||
mkStageRules { dirName = "auto-installer"; system = netbootSystem; }
|
# tmpfiles' C type needs the target directory to already exist.
|
||||||
++ mkStageRules { dirName = "nixos-minimal"; system = netbootMinimalSystem; };
|
"d /srv/pxe/http/nixos 0755 root root -"
|
||||||
|
"C+ /srv/pxe/http/nixos/${kernelFile} 0644 root root - ${netbootSystem.config.system.build.kernel}/${kernelFile}"
|
||||||
|
"C+ /srv/pxe/http/nixos/initrd 0644 root root - ${netbootSystem.config.system.build.netbootRamdisk}/initrd"
|
||||||
|
"C+ /srv/pxe/http/nixos/netboot.ipxe 0644 root root - ${netbootSystem.config.system.build.netbootIpxeScript}/netboot.ipxe"
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,6 @@
|
|||||||
#
|
#
|
||||||
# --dry-run: adds `nix build --dry-run --no-link` for whatever scope is
|
# --dry-run: adds `nix build --dry-run --no-link` for whatever scope is
|
||||||
# active (changed-files scope by default, full scope under --full-check).
|
# active (changed-files scope by default, full scope under --full-check).
|
||||||
#
|
|
||||||
# Per-host/per-package eval and dry-run build calls run concurrently (see
|
|
||||||
# scripts/lib/nix-parallel.sh) since they're independent of each other.
|
|
||||||
# Concurrency defaults to core count capped by available memory (~1GB/job)
|
|
||||||
# rather than plain core count, since each concurrent `nix eval` evaluates a
|
|
||||||
# whole NixOS system closure and can OOM a small/memory-constrained CI
|
|
||||||
# runner otherwise; override via NIX_PARALLEL_JOBS if a runner has more (or
|
|
||||||
# less) room than that estimate assumes.
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
@@ -31,8 +23,6 @@ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|||||||
source "${script_dir}/lib/nix-bootstrap.sh"
|
source "${script_dir}/lib/nix-bootstrap.sh"
|
||||||
# shellcheck source=lib/nix-eval.sh
|
# shellcheck source=lib/nix-eval.sh
|
||||||
source "${script_dir}/lib/nix-eval.sh"
|
source "${script_dir}/lib/nix-eval.sh"
|
||||||
# shellcheck source=lib/nix-parallel.sh
|
|
||||||
source "${script_dir}/lib/nix-parallel.sh"
|
|
||||||
|
|
||||||
repo_root="$(cd "${script_dir}/.." && pwd)"
|
repo_root="$(cd "${script_dir}/.." && pwd)"
|
||||||
cd "$repo_root"
|
cd "$repo_root"
|
||||||
@@ -256,64 +246,66 @@ echo
|
|||||||
if [[ ${#hosts[@]} -eq 0 ]]; then
|
if [[ ${#hosts[@]} -eq 0 ]]; then
|
||||||
echo "No hosts affected by changed files; skipping host eval."
|
echo "No hosts affected by changed files; skipping host eval."
|
||||||
else
|
else
|
||||||
echo "Evaluating host toplevel derivations (${scope_desc}, up to ${NIX_PARALLEL_JOBS} at a time)..."
|
echo "Evaluating host toplevel derivations (${scope_desc})..."
|
||||||
|
for host in "${hosts[@]}"; do
|
||||||
|
echo "==> $host"
|
||||||
|
nix eval --raw "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.toplevel.drvPath"
|
||||||
|
|
||||||
# lxc-* hosts deploy via a directly pct-restore-able tarball instead of
|
# lxc-* hosts deploy via a directly pct-restore-able tarball instead of
|
||||||
# nixos-install (see docs/auto-installer.md); proxmox-* hosts can
|
# nixos-install (see docs/auto-installer.md); proxmox-* hosts can
|
||||||
# alternatively be built as a standalone disk image (see
|
# alternatively be built as a standalone disk image (see
|
||||||
# docs/proxmox-images.md). Both are otherwise-unvalidated buildable
|
# docs/proxmox-images.md). Both are otherwise-unvalidated buildable
|
||||||
# surface, easy to silently break without this.
|
# surface, easy to silently break without this.
|
||||||
declare -a host_eval_jobs=()
|
|
||||||
for host in "${hosts[@]}"; do
|
|
||||||
host_eval_jobs+=("${host}${NIX_PARALLEL_SEP}.#nixosConfigurations.${host}.config.system.build.toplevel.drvPath")
|
|
||||||
case "$host" in
|
case "$host" in
|
||||||
lxc-*)
|
lxc-*)
|
||||||
host_eval_jobs+=("${host} (tarball)${NIX_PARALLEL_SEP}.#nixosConfigurations.${host}.config.system.build.tarball.drvPath")
|
echo "==> $host (tarball)"
|
||||||
|
nix eval --raw "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.tarball.drvPath"
|
||||||
;;
|
;;
|
||||||
proxmox-*)
|
proxmox-*)
|
||||||
host_eval_jobs+=("${host} (diskoImagesScript)${NIX_PARALLEL_SEP}.#nixosConfigurations.${host}.config.system.build.diskoImagesScript.drvPath")
|
echo "==> $host (diskoImagesScript)"
|
||||||
|
nix eval --raw "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.diskoImagesScript.drvPath"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
run_nix_parallel host_eval_jobs eval --raw "${NIX_EVAL_FLAGS[@]}"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo
|
echo
|
||||||
if ! $eval_packages; then
|
if ! $eval_packages; then
|
||||||
echo "No packages affected by changed files; skipping package eval."
|
echo "No packages affected by changed files; skipping package eval."
|
||||||
else
|
else
|
||||||
echo "Evaluating buildable packages (up to ${NIX_PARALLEL_JOBS} at a time)..."
|
echo "Evaluating buildable packages..."
|
||||||
declare -a package_eval_jobs=()
|
|
||||||
for pkg in "${all_packages[@]}"; do
|
for pkg in "${all_packages[@]}"; do
|
||||||
package_eval_jobs+=("packages.x86_64-linux.${pkg}${NIX_PARALLEL_SEP}.#packages.x86_64-linux.${pkg}")
|
echo "==> packages.x86_64-linux.${pkg}"
|
||||||
|
nix eval --raw "${NIX_EVAL_FLAGS[@]}" ".#packages.x86_64-linux.${pkg}"
|
||||||
done
|
done
|
||||||
run_nix_parallel package_eval_jobs eval --raw "${NIX_EVAL_FLAGS[@]}"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if $dry_run; then
|
if $dry_run; then
|
||||||
echo
|
echo
|
||||||
echo "Running dry-run builds for the active scope (up to ${NIX_PARALLEL_JOBS} at a time). This will not create result symlinks."
|
echo "Running dry-run builds for the active scope. This will not create result symlinks."
|
||||||
declare -a host_build_jobs=()
|
|
||||||
for host in "${hosts[@]:-}"; do
|
for host in "${hosts[@]:-}"; do
|
||||||
host_build_jobs+=("Dry-run build: ${host}${NIX_PARALLEL_SEP}.#nixosConfigurations.${host}.config.system.build.toplevel")
|
echo "==> Dry-run build: $host"
|
||||||
|
nix build --dry-run --no-link "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.toplevel"
|
||||||
|
|
||||||
case "$host" in
|
case "$host" in
|
||||||
lxc-*)
|
lxc-*)
|
||||||
host_build_jobs+=("Dry-run build: ${host} (tarball)${NIX_PARALLEL_SEP}.#nixosConfigurations.${host}.config.system.build.tarball")
|
echo "==> Dry-run build: $host (tarball)"
|
||||||
|
nix build --dry-run --no-link "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.tarball"
|
||||||
;;
|
;;
|
||||||
proxmox-*)
|
proxmox-*)
|
||||||
host_build_jobs+=("Dry-run build: ${host} (diskoImagesScript)${NIX_PARALLEL_SEP}.#nixosConfigurations.${host}.config.system.build.diskoImagesScript")
|
echo "==> Dry-run build: $host (diskoImagesScript)"
|
||||||
|
nix build --dry-run --no-link "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.diskoImagesScript"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
run_nix_parallel host_build_jobs build --dry-run --no-link "${NIX_EVAL_FLAGS[@]}"
|
|
||||||
|
|
||||||
if $eval_packages; then
|
if $eval_packages; then
|
||||||
echo
|
echo
|
||||||
echo "Running dry-run builds for packages."
|
echo "Running dry-run builds for packages."
|
||||||
declare -a package_build_jobs=()
|
|
||||||
for pkg in "${all_packages[@]}"; do
|
for pkg in "${all_packages[@]}"; do
|
||||||
package_build_jobs+=("Dry-run build: packages.x86_64-linux.${pkg}${NIX_PARALLEL_SEP}.#packages.x86_64-linux.${pkg}")
|
echo "==> Dry-run build: packages.x86_64-linux.${pkg}"
|
||||||
|
nix build --dry-run --no-link "${NIX_EVAL_FLAGS[@]}" ".#packages.x86_64-linux.${pkg}"
|
||||||
done
|
done
|
||||||
run_nix_parallel package_build_jobs build --dry-run --no-link "${NIX_EVAL_FLAGS[@]}"
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -82,12 +82,6 @@ export PVE1_HOST PVE_TEST_HOST PROXMOX_HOST PROXMOX_SSH_USER PROXMOX_STORAGE \
|
|||||||
: "${NIX_CACHE_HOST:=nix-cache}"
|
: "${NIX_CACHE_HOST:=nix-cache}"
|
||||||
export NIX_CACHE_HOST
|
export NIX_CACHE_HOST
|
||||||
|
|
||||||
# Matches variables.nix's lanDomain (the Gitea host this flake's own repo
|
|
||||||
# is served from -- see scripts/installer/auto-install.sh's FLAKE_BASE_URL)
|
|
||||||
# -- update both if it ever changes.
|
|
||||||
: "${LAN_DOMAIN:=gitea.lan.ddnsgeek.com}"
|
|
||||||
export LAN_DOMAIN
|
|
||||||
|
|
||||||
# nix_extra_opts: call as a plain statement (NOT inside $(...)/<(...) --
|
# nix_extra_opts: call as a plain statement (NOT inside $(...)/<(...) --
|
||||||
# that forks a subshell, and the whole point is exporting a decision back
|
# that forks a subshell, and the whole point is exporting a decision back
|
||||||
# into *this* shell) to populate the global NIX_OPTS array with whatever
|
# into *this* shell) to populate the global NIX_OPTS array with whatever
|
||||||
|
|||||||
@@ -1,206 +0,0 @@
|
|||||||
#!/usr/bin/env nix-shell
|
|
||||||
#!nix-shell -i bash -p jq disko nixos-install-tools zfs
|
|
||||||
# shellcheck shell=bash
|
|
||||||
# The only genuinely external tools this script calls directly: `jq`
|
|
||||||
# (parsing the `nix eval` host list), `disko`/`nixos-install` (the
|
|
||||||
# install itself), and `zpool` (exporting a ZFS root pool before reboot,
|
|
||||||
# see the comment above that call below). Everything disko shells out to
|
|
||||||
# internally (parted/sgdisk/mkfs.*/zfs/...) is self-contained -- disko's
|
|
||||||
# own generated scripts hardcode absolute Nix store paths for those, they
|
|
||||||
# don't rely on this script's PATH at all (confirmed by inspecting a
|
|
||||||
# generated system.build.formatScript). The built installer image
|
|
||||||
# (modules/installer/common.nix, plus the upstream
|
|
||||||
# installation-cd-minimal.nix it imports via iso.nix) already has all
|
|
||||||
# four in environment.systemPackages, so this nix-shell wrapper is a
|
|
||||||
# fast no-op there; it's what makes the script also work standalone
|
|
||||||
# (e.g. run directly from a checkout on a stock ISO), where they aren't
|
|
||||||
# guaranteed.
|
|
||||||
set -eux
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# shellcheck source=../env.sh
|
|
||||||
source "$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/env.sh"
|
|
||||||
|
|
||||||
export FLAKE_BASE_URL="git+https://${LAN_DOMAIN}/beatzaplenty/nixos.git"
|
|
||||||
|
|
||||||
echo "Fetching available NixOS hosts from flake..."
|
|
||||||
# Two categories deliberately excluded from the menu:
|
|
||||||
# lxc-* — these build a config.system.build.tarball meant for
|
|
||||||
# `pct restore` on Proxmox directly, not an install.
|
|
||||||
# Running nixos-install against one here would
|
|
||||||
# bind-mount / onto /mnt and then refuse to touch the
|
|
||||||
# filesystem it's currently running on — see
|
|
||||||
# docs/auto-installer.md.
|
|
||||||
# installer — this *is* the installer image's own flake target,
|
|
||||||
# not a deployable host; "installing" it means
|
|
||||||
# nixos-install-ing a copy of the installer into
|
|
||||||
# itself.
|
|
||||||
mapfile -t options < <(
|
|
||||||
nix eval --json --no-use-registries --no-accept-flake-config --extra-experimental-features "flakes nix-command" \
|
|
||||||
"${FLAKE_BASE_URL}#nixosConfigurations" \
|
|
||||||
--apply builtins.attrNames \
|
|
||||||
| jq -r '.[]
|
|
||||||
| select(startswith("lxc-") | not)
|
|
||||||
| select(. != "installer")'
|
|
||||||
)
|
|
||||||
|
|
||||||
if [[ ${#options[@]} -eq 0 ]]; then
|
|
||||||
echo "ERROR: No NixOS hosts found in ${FLAKE_BASE_URL}#nixosConfigurations" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Note: lxc-* targets aren't installed this way — build them with"
|
|
||||||
echo " nix build .#nixosConfigurations.<name>.config.system.build.tarball"
|
|
||||||
echo "and 'pct restore' the result on Proxmox directly. See docs/auto-installer.md."
|
|
||||||
|
|
||||||
echo "Choose the flake profile to install:"
|
|
||||||
select choice in "${options[@]}"; do
|
|
||||||
if [[ -n "$choice" ]]; then
|
|
||||||
echo "You selected: $choice"
|
|
||||||
break
|
|
||||||
else
|
|
||||||
echo "Invalid selection. Try again."
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Starting install with flake: ${FLAKE_BASE_URL}#${choice}"
|
|
||||||
|
|
||||||
# Optional: confirm before proceeding
|
|
||||||
read -rp "Proceed with installation? (y/N): " confirm
|
|
||||||
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
|
|
||||||
echo "Aborted."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# A nix-cache host is *the* substituter/remote-builder for every other
|
|
||||||
# host once installed (its own config explicitly excludes itself from
|
|
||||||
# using either — see buildType != "nix-cache" in the nixos flake.nix).
|
|
||||||
# Installing one shouldn't depend on a nix-cache substituter either,
|
|
||||||
# for the same reason — plus in practice "nix-cache" only resolves over
|
|
||||||
# Tailscale, which a fresh installer environment was never connected to
|
|
||||||
# anyway, so it's dead weight even for non-nix-cache installs until
|
|
||||||
# that's sorted out. Override it away here specifically for nix-cache
|
|
||||||
# targets to keep install-time behaviour consistent with run-time.
|
|
||||||
nix_extra_opts=()
|
|
||||||
if [[ "${choice}" == *-nix-cache ]]; then
|
|
||||||
echo "Installing a nix-cache host — skipping the nix-cache substituter."
|
|
||||||
nix_extra_opts+=(--option substituters "https://cache.nixos.org/")
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Every host reachable through this menu has a Disko config (lxc-*
|
|
||||||
# is filtered out above, and is the only category that doesn't —
|
|
||||||
# see docs/auto-installer.md), so this can run unconditionally: no
|
|
||||||
# need to probe the flake first and branch on whether Disko applies.
|
|
||||||
disko --mode destroy,format,mount \
|
|
||||||
--flake "${FLAKE_BASE_URL}#${choice}" "${nix_extra_opts[@]}" --yes-wipe-all-disks
|
|
||||||
|
|
||||||
# sops-nix derives this host's decryption key from its own SSH host key
|
|
||||||
# at *activation* time, which runs before systemd would otherwise
|
|
||||||
# generate one on first boot. Without pre-seeding it here, secrets
|
|
||||||
# (including the login password) fail to decrypt on first boot.
|
|
||||||
# Generate the key with scripts/secrets/prepare-host-key.sh first.
|
|
||||||
#
|
|
||||||
# Two places a key can come from, checked in order:
|
|
||||||
# /etc/host-keys — baked into this image at build time (see
|
|
||||||
# modules/installer/host-keys.nix; only present
|
|
||||||
# if built with NIXOS_HOST_KEYS_DIR set)
|
|
||||||
# /root/host-keys — scp'd in manually after boot (older fallback,
|
|
||||||
# still supported for images built without keys)
|
|
||||||
mkdir -p /root/host-keys
|
|
||||||
if [[ -f "/etc/host-keys/${choice}_ssh_host_ed25519_key" ]]; then
|
|
||||||
echo "Found baked-in SSH host key for ${choice}, installing to target..."
|
|
||||||
install -D -m 0600 "/etc/host-keys/${choice}_ssh_host_ed25519_key" /mnt/etc/ssh/ssh_host_ed25519_key
|
|
||||||
install -D -m 0644 "/etc/host-keys/${choice}_ssh_host_ed25519_key.pub" /mnt/etc/ssh/ssh_host_ed25519_key.pub
|
|
||||||
elif [[ -f "/root/host-keys/${choice}_ssh_host_ed25519_key" ]]; then
|
|
||||||
echo "Found pre-seeded SSH host key for ${choice}, installing to target..."
|
|
||||||
install -D -m 0600 "/root/host-keys/${choice}_ssh_host_ed25519_key" /mnt/etc/ssh/ssh_host_ed25519_key
|
|
||||||
install -D -m 0644 "/root/host-keys/${choice}_ssh_host_ed25519_key.pub" /mnt/etc/ssh/ssh_host_ed25519_key.pub
|
|
||||||
else
|
|
||||||
# Third place a key can come from: an arbitrary path the operator
|
|
||||||
# points at interactively (e.g. a USB stick, a mount from another
|
|
||||||
# machine) -- only offered when there's an actual human at the other
|
|
||||||
# end of stdin to ask, never in a non-interactive run.
|
|
||||||
key_copied=0
|
|
||||||
if [[ -t 0 ]]; then
|
|
||||||
echo "No SSH host key found for ${choice} (checked /etc/host-keys and /root/host-keys)."
|
|
||||||
read -rp "Path to a directory containing ${choice}_ssh_host_ed25519_key(.pub) (blank to skip): " key_src_dir
|
|
||||||
if [[ -n "$key_src_dir" && -f "${key_src_dir}/${choice}_ssh_host_ed25519_key" && -f "${key_src_dir}/${choice}_ssh_host_ed25519_key.pub" ]]; then
|
|
||||||
cp "${key_src_dir}/${choice}_ssh_host_ed25519_key" "${key_src_dir}/${choice}_ssh_host_ed25519_key.pub" /root/host-keys/
|
|
||||||
key_copied=1
|
|
||||||
elif [[ -n "$key_src_dir" ]]; then
|
|
||||||
echo "WARNING: ${choice}_ssh_host_ed25519_key(.pub) not found in ${key_src_dir}."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$key_copied" -eq 1 ]]; then
|
|
||||||
echo "Copied SSH host key for ${choice} from ${key_src_dir}, installing to target..."
|
|
||||||
install -D -m 0600 "/root/host-keys/${choice}_ssh_host_ed25519_key" /mnt/etc/ssh/ssh_host_ed25519_key
|
|
||||||
install -D -m 0644 "/root/host-keys/${choice}_ssh_host_ed25519_key.pub" /mnt/etc/ssh/ssh_host_ed25519_key.pub
|
|
||||||
else
|
|
||||||
echo "WARNING: no SSH host key found for ${choice} (checked /etc/host-keys and /root/host-keys)"
|
|
||||||
echo "sops-nix secrets (including the login password) will NOT decrypt on first boot."
|
|
||||||
echo "Run scripts/secrets/prepare-host-key.sh for host ${choice} on your admin workstation first,"
|
|
||||||
echo "then either rebuild this image with NIXOS_HOST_KEYS_DIR set, scp the result to"
|
|
||||||
echo "/root/host-keys/ on this machine, or point at it when prompted above."
|
|
||||||
read -rp "Continue without a pre-seeded key anyway? (y/N): " skip_key
|
|
||||||
if [[ ! "$skip_key" =~ ^[Yy]$ ]]; then
|
|
||||||
echo "Aborted."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p /mnt/install-tmp
|
|
||||||
export TMPDIR=/mnt/install-tmp
|
|
||||||
|
|
||||||
nixos-install \
|
|
||||||
--flake "${FLAKE_BASE_URL}#${choice}" \
|
|
||||||
"${nix_extra_opts[@]}" \
|
|
||||||
--no-root-password
|
|
||||||
|
|
||||||
|
|
||||||
rm -rf /mnt/install-tmp
|
|
||||||
# Redundant copy of the host's private key — the real one is now at
|
|
||||||
# /etc/ssh/ssh_host_ed25519_key. Nothing NixOS-managed ever cleans this
|
|
||||||
# up on its own since it was written imperatively, not declaratively.
|
|
||||||
rm -rf /root/host-keys
|
|
||||||
|
|
||||||
# disko's --mode ...,mount left any ZFS root pool imported (that's what
|
|
||||||
# let nixos-install write into /mnt). If we reboot with it still
|
|
||||||
# imported, it isn't just "not exported" -- it's stamped with *this*
|
|
||||||
# live installer environment's hostid, which almost never matches the
|
|
||||||
# target's own networking.hostId (see hosts/*/host.nix; the installer
|
|
||||||
# itself sets none). modules/services/zfs/enable-service.nix and
|
|
||||||
# modules/common/configuration.nix both set boot.zfs.forceImportRoot =
|
|
||||||
# false deliberately (the safe option per that setting's own docs), so
|
|
||||||
# the freshly-installed system's first real boot sees a pool "in use by
|
|
||||||
# another system" and refuses to import it without -f -- which is what
|
|
||||||
# makes boot stall waiting on the ZFS import. Exporting here (a no-op
|
|
||||||
# if the chosen host has no ZFS root, e.g. proxmox-*/linode-*) clears
|
|
||||||
# that in-use state so the next import, from any hostid, succeeds.
|
|
||||||
#
|
|
||||||
# Anything still mounted under /mnt -- nixos-install's own leftover
|
|
||||||
# chroot bind mounts for running the target's activation script
|
|
||||||
# (/mnt/dev, /mnt/proc, /mnt/sys, /mnt/run), and disko's own /mnt/boot
|
|
||||||
# ESP mount (modules/disko/baremetal.nix) -- blocks ZFS from unmounting
|
|
||||||
# its root dataset at /mnt, the same way any nested mount blocks
|
|
||||||
# unmounting its parent. Confirmed live: zpool export failed with
|
|
||||||
# "cannot unmount '/mnt': pool or dataset busy" even after handling the
|
|
||||||
# chroot mounts alone, because /mnt/boot was still mounted too. Because
|
|
||||||
# of this script's `set -e`, that killed the script before it ever
|
|
||||||
# reached reboot, silently defeating the whole point of exporting first.
|
|
||||||
# Unmounting everything under /mnt up front (recursively, so nested
|
|
||||||
# mounts like /mnt/dev/pts come along for free) sidesteps needing to
|
|
||||||
# enumerate every mount disko/nixos-install might leave behind.
|
|
||||||
if mountpoint -q /mnt; then
|
|
||||||
umount -R /mnt
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -n "$(zpool list -H -o name 2>/dev/null)" ]]; then
|
|
||||||
echo "Exporting ZFS pool(s) before reboot..."
|
|
||||||
zpool export -a
|
|
||||||
fi
|
|
||||||
|
|
||||||
sleep 10
|
|
||||||
reboot
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Shared parallel-nix-invocation helper for scripts/codex-maintenance.sh.
|
|
||||||
# Source alongside nix-eval.sh:
|
|
||||||
# source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/nix-parallel.sh"
|
|
||||||
#
|
|
||||||
# The per-host/per-package `nix eval`/`nix build --dry-run` calls in
|
|
||||||
# codex-maintenance.sh are independent of each other, so running them one at
|
|
||||||
# a time leaves most cores idle for most of the sweep -- run_nix_parallel
|
|
||||||
# fans a batch of them out across up to NIX_PARALLEL_JOBS processes instead.
|
|
||||||
|
|
||||||
# NIX_PARALLEL_JOBS: how many `nix` invocations run_nix_parallel runs at
|
|
||||||
# once. Defaults to core count capped by available memory (~1GB/job,
|
|
||||||
# floor 1) rather than plain `nproc` -- each concurrent `nix eval` here
|
|
||||||
# evaluates a whole NixOS system closure from scratch, and on a small/
|
|
||||||
# memory-constrained CI runner, `nproc` concurrent evals can OOM-kill each
|
|
||||||
# other (confirmed empirically: on a 4GB/6-core box, 5-6 concurrent evals
|
|
||||||
# started getting killed while 3-4 ran clean and were still ~2x faster than
|
|
||||||
# serial). Override via env if a given machine/CI runner has room to spare
|
|
||||||
# or needs a tighter cap.
|
|
||||||
default_nix_parallel_jobs() {
|
|
||||||
local cores mem_avail_kb mem_cap
|
|
||||||
cores="$(nproc 2>/dev/null || echo 4)"
|
|
||||||
mem_avail_kb="$(awk '/^MemAvailable:/ {print $2}' /proc/meminfo 2>/dev/null)"
|
|
||||||
if [[ -z "$mem_avail_kb" ]]; then
|
|
||||||
echo "$cores"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
mem_cap=$((mem_avail_kb / 1024 / 1024))
|
|
||||||
((mem_cap < 1)) && mem_cap=1
|
|
||||||
((mem_cap < cores)) && echo "$mem_cap" || echo "$cores"
|
|
||||||
}
|
|
||||||
NIX_PARALLEL_JOBS="${NIX_PARALLEL_JOBS:-$(default_nix_parallel_jobs)}"
|
|
||||||
|
|
||||||
# Separator between a job's label and its flake attr in the arrays
|
|
||||||
# run_nix_parallel takes -- a control character so it can't collide with
|
|
||||||
# anything a label or attr path would plausibly contain.
|
|
||||||
NIX_PARALLEL_SEP=$'\x1f'
|
|
||||||
|
|
||||||
# run_nix_parallel <jobs_array_name> <nix subcommand + flags...>
|
|
||||||
#
|
|
||||||
# jobs_array_name: name of an already-populated bash array whose entries are
|
|
||||||
# "<label>${NIX_PARALLEL_SEP}<attr>" pairs, e.g.
|
|
||||||
# jobs=("proxmox-docker${NIX_PARALLEL_SEP}.#nixosConfigurations.proxmox-docker...drvPath")
|
|
||||||
# Remaining args are passed to `nix` before the attr, e.g.:
|
|
||||||
# run_nix_parallel jobs eval --raw "${NIX_EVAL_FLAGS[@]}"
|
|
||||||
# run_nix_parallel jobs build --dry-run --no-link "${NIX_EVAL_FLAGS[@]}"
|
|
||||||
#
|
|
||||||
# Prints "==> <label>" followed by that job's stdout+stderr for every job,
|
|
||||||
# in submission order (not completion order) so a run stays readable and
|
|
||||||
# diffable across invocations even though the work itself doesn't finish in
|
|
||||||
# that order. Returns non-zero if any job failed, only after every job has
|
|
||||||
# finished and been printed -- same "surface everything, then fail" contract
|
|
||||||
# a `set -e` caller gets, just parallelized instead of stopping at the first
|
|
||||||
# failure.
|
|
||||||
run_nix_parallel() {
|
|
||||||
local -n jobs_ref="$1"
|
|
||||||
shift
|
|
||||||
local -a nix_args=("$@")
|
|
||||||
|
|
||||||
local n=${#jobs_ref[@]}
|
|
||||||
[[ $n -eq 0 ]] && return 0
|
|
||||||
|
|
||||||
local tmp_dir
|
|
||||||
tmp_dir="$(mktemp -d)"
|
|
||||||
|
|
||||||
local i=0 running=0
|
|
||||||
for job in "${jobs_ref[@]}"; do
|
|
||||||
local attr="${job#*"${NIX_PARALLEL_SEP}"}"
|
|
||||||
printf '%s\n' "${job%%"${NIX_PARALLEL_SEP}"*}" >"${tmp_dir}/${i}.label"
|
|
||||||
(
|
|
||||||
if nix "${nix_args[@]}" "$attr" >"${tmp_dir}/${i}.out" 2>&1; then
|
|
||||||
echo 0 >"${tmp_dir}/${i}.status"
|
|
||||||
else
|
|
||||||
echo 1 >"${tmp_dir}/${i}.status"
|
|
||||||
fi
|
|
||||||
) &
|
|
||||||
i=$((i + 1))
|
|
||||||
running=$((running + 1))
|
|
||||||
if ((running >= NIX_PARALLEL_JOBS)); then
|
|
||||||
wait -n
|
|
||||||
running=$((running - 1))
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
wait
|
|
||||||
|
|
||||||
local failed=0 j
|
|
||||||
for ((j = 0; j < n; j++)); do
|
|
||||||
echo "==> $(cat "${tmp_dir}/${j}.label")"
|
|
||||||
cat "${tmp_dir}/${j}.out"
|
|
||||||
[[ "$(cat "${tmp_dir}/${j}.status")" -ne 0 ]] && failed=1
|
|
||||||
done
|
|
||||||
|
|
||||||
rm -rf "$tmp_dir"
|
|
||||||
return $failed
|
|
||||||
}
|
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Ad hoc clone of a single VM/CT from pve1 (production) to pve-test
|
|
||||||
# (sandbox), via vzdump + qmrestore/pct restore -- not a general-purpose
|
|
||||||
# backup tool, just a quick "give me a disposable copy of this thing on
|
|
||||||
# pve-test" for testing against real-ish data without touching prod.
|
|
||||||
#
|
|
||||||
# Flow:
|
|
||||||
# 1. vzdump the resource on pve1 into its "local" storage (--mode
|
|
||||||
# snapshot by default, so the source keeps running throughout --
|
|
||||||
# see --mode below for when that's not possible).
|
|
||||||
# 2. Stream the resulting archive straight from pve1 to pve-test
|
|
||||||
# (ssh pve1 cat ... | ssh pve-test cat > ...) -- this machine is
|
|
||||||
# just the relay, no separate on-disk staging copy here.
|
|
||||||
# 3. qmrestore / pct restore it on pve-test under --new-vmid (default:
|
|
||||||
# same VMID as the source -- pve-test is a separate node/cluster, so
|
|
||||||
# no collision unless that VMID is already in use there too).
|
|
||||||
# Always restored with --unique 1 (fresh MAC addresses) since the
|
|
||||||
# source is typically still running on the same LAN -- restoring
|
|
||||||
# with the *same* MAC would put two live guests on the wire with
|
|
||||||
# identical hardware addresses.
|
|
||||||
# 4. Delete the vzdump archive from pve1's local storage and the
|
|
||||||
# relayed copy on pve-test, so neither node accumulates ad hoc
|
|
||||||
# backup files from this script. Only the pve1 original is
|
|
||||||
# preserved on any failure after step 1, so a failed
|
|
||||||
# transfer/restore can be retried without re-running the backup.
|
|
||||||
#
|
|
||||||
# This script's own defaults are pve1 -> pve-test, unlike
|
|
||||||
# create-proxmox-resource.sh's --node (which defaults to production) --
|
|
||||||
# see CLAUDE.md's "Two Proxmox nodes" section. pve1 is only ever touched
|
|
||||||
# here after typing the source VMID back to confirm; pve-test is treated
|
|
||||||
# as disposable, matching this repo's usual policy for that node.
|
|
||||||
#
|
|
||||||
# See --help for the full option list.
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
repo_root="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
||||||
# shellcheck source=../env.sh
|
|
||||||
source "${repo_root}/scripts/env.sh"
|
|
||||||
# shellcheck source=../lib/confirm.sh
|
|
||||||
source "${repo_root}/scripts/lib/confirm.sh"
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
cat <<EOF
|
|
||||||
Usage: $0 --vmid <n> [options]
|
|
||||||
|
|
||||||
--vmid <n> Required: VMID on the source node to clone.
|
|
||||||
Kind (qemu VM vs LXC CT) is auto-detected.
|
|
||||||
--new-vmid <n> VMID to restore as on the target node
|
|
||||||
(default: same as --vmid).
|
|
||||||
--mode snapshot|suspend|stop
|
|
||||||
vzdump backup mode (default: snapshot -- the
|
|
||||||
source resource keeps running throughout;
|
|
||||||
requires snapshot-capable storage, e.g.
|
|
||||||
ZFS/LVM-thin/Ceph/qcow2). Fall back to
|
|
||||||
"suspend" (brief pause) or "stop" (source
|
|
||||||
goes down for the duration) if the source's
|
|
||||||
storage doesn't support live snapshots --
|
|
||||||
vzdump's own error will say so.
|
|
||||||
--source-node <host> (default: \$PVE1_HOST, ${PVE1_HOST})
|
|
||||||
--target-node <host> (default: \$PVE_TEST_HOST, ${PVE_TEST_HOST})
|
|
||||||
--source-storage <pool> Where vzdump writes the backup on the
|
|
||||||
source node (default: local).
|
|
||||||
--target-storage <pool> Where the restored disk/rootfs lands on
|
|
||||||
the target node (default: \$PROXMOX_STORAGE, ${PROXMOX_STORAGE}).
|
|
||||||
--keep-backup Don't delete the vzdump archive from
|
|
||||||
either node afterward (debugging aid).
|
|
||||||
--yes Skip the typed VMID confirmation
|
|
||||||
before touching the source node.
|
|
||||||
--dry-run Print the full plan and skip every
|
|
||||||
mutating step (vzdump, transfer,
|
|
||||||
restore, delete) and the confirm
|
|
||||||
prompt. Still makes read-only SSH
|
|
||||||
calls to look up the source kind
|
|
||||||
and check the target VMID is free
|
|
||||||
-- harmless on either node.
|
|
||||||
-h, --help
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
vmid=""
|
|
||||||
new_vmid=""
|
|
||||||
mode="snapshot"
|
|
||||||
source_node="$PVE1_HOST"
|
|
||||||
target_node="$PVE_TEST_HOST"
|
|
||||||
source_storage="local"
|
|
||||||
target_storage="$PROXMOX_STORAGE"
|
|
||||||
keep_backup=0
|
|
||||||
skip_confirm=0
|
|
||||||
dry_run=0
|
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
|
||||||
case "$1" in
|
|
||||||
--vmid) vmid="$2"; shift 2 ;;
|
|
||||||
--new-vmid) new_vmid="$2"; shift 2 ;;
|
|
||||||
--mode) mode="$2"; shift 2 ;;
|
|
||||||
--source-node) source_node="$2"; shift 2 ;;
|
|
||||||
--target-node) target_node="$2"; shift 2 ;;
|
|
||||||
--source-storage) source_storage="$2"; shift 2 ;;
|
|
||||||
--target-storage) target_storage="$2"; shift 2 ;;
|
|
||||||
--keep-backup) keep_backup=1; shift ;;
|
|
||||||
--yes) skip_confirm=1; shift ;;
|
|
||||||
--dry-run) dry_run=1; shift ;;
|
|
||||||
-h | --help) usage; exit 0 ;;
|
|
||||||
*) echo "Unknown option: $1" >&2; usage >&2; exit 1 ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ -z "$vmid" ]]; then
|
|
||||||
echo "ERROR: --vmid is required." >&2
|
|
||||||
usage >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [[ "$mode" != "snapshot" && "$mode" != "suspend" && "$mode" != "stop" ]]; then
|
|
||||||
echo "ERROR: --mode must be snapshot, suspend, or stop." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
[[ -z "$new_vmid" ]] && new_vmid="$vmid"
|
|
||||||
|
|
||||||
source_target="${PROXMOX_SSH_USER}@${source_node}"
|
|
||||||
target_target="${PROXMOX_SSH_USER}@${target_node}"
|
|
||||||
|
|
||||||
# No dry-run wrapper needed for the calls below: every mutating step
|
|
||||||
# (vzdump, transfer, restore, delete) is reached only after the --dry-run
|
|
||||||
# early-exit further down, so a plain `ssh` call is never in the dry-run
|
|
||||||
# path.
|
|
||||||
|
|
||||||
# --- identify the resource kind on the source node -----------------------
|
|
||||||
echo "==> Looking up VMID ${vmid} on ${source_node}..."
|
|
||||||
kind=""
|
|
||||||
if ssh "$source_target" "qm status ${vmid}" >/dev/null 2>&1; then
|
|
||||||
kind="vm"
|
|
||||||
elif ssh "$source_target" "pct status ${vmid}" >/dev/null 2>&1; then
|
|
||||||
kind="lxc"
|
|
||||||
else
|
|
||||||
echo "ERROR: VMID ${vmid} doesn't exist on ${source_node} as either a VM or CT." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "VMID ${vmid} on ${source_node} is a ${kind}."
|
|
||||||
|
|
||||||
# --- refuse to clobber an existing resource on the target node -----------
|
|
||||||
if ssh "$target_target" "qm status ${new_vmid}" >/dev/null 2>&1 \
|
|
||||||
|| ssh "$target_target" "pct status ${new_vmid}" >/dev/null 2>&1; then
|
|
||||||
echo "ERROR: VMID ${new_vmid} already exists on ${target_node}. Pass --new-vmid" >&2
|
|
||||||
echo "with a free ID, or remove the existing resource there first." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Plan:"
|
|
||||||
echo " source: ${kind} VMID ${vmid} on ${source_node} (storage: ${source_storage}, mode: ${mode})"
|
|
||||||
echo " target: VMID ${new_vmid} on ${target_node} (storage: ${target_storage}, fresh MAC via --unique)"
|
|
||||||
[[ "$keep_backup" -eq 1 ]] && echo " backup archives are kept on both nodes afterward (--keep-backup)"
|
|
||||||
|
|
||||||
if [[ "$dry_run" -eq 1 ]]; then
|
|
||||||
echo
|
|
||||||
echo "[dry-run] No backup, transfer, restore, or delete was performed."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$skip_confirm" -ne 1 ]]; then
|
|
||||||
echo
|
|
||||||
if ! confirm_typed "$vmid" "Type the source VMID (${vmid}) to confirm backing it up from ${source_node}: "; then
|
|
||||||
echo "Cancelled -- input didn't match ${vmid}." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- vzdump on the source node --------------------------------------------
|
|
||||||
echo
|
|
||||||
echo "==> Backing up VMID ${vmid} on ${source_node} (mode=${mode}, storage=${source_storage})..."
|
|
||||||
vzdump_log="$(ssh "$source_target" \
|
|
||||||
"vzdump ${vmid} --mode ${mode} --storage ${source_storage} --compress zstd" 2>&1)" \
|
|
||||||
|| {
|
|
||||||
echo "$vzdump_log" >&2
|
|
||||||
echo "ERROR: vzdump failed on ${source_node}." >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
echo "$vzdump_log"
|
|
||||||
|
|
||||||
archive="$(echo "$vzdump_log" | grep -oP "creating vzdump archive '\K[^']+" | tail -n1)"
|
|
||||||
if [[ -z "$archive" ]]; then
|
|
||||||
echo "ERROR: couldn't find the archive path in vzdump's output above." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
archive_basename="$(basename "$archive")"
|
|
||||||
target_tmp_archive="/var/tmp/${archive_basename}"
|
|
||||||
echo "Archive: ${archive}"
|
|
||||||
|
|
||||||
# Always clean up the relayed copy on the target node, success or failure
|
|
||||||
# -- it's only ever a working copy, restored or not.
|
|
||||||
cleanup_target_tmp() {
|
|
||||||
if [[ "$keep_backup" -ne 1 ]]; then
|
|
||||||
ssh "$target_target" "rm -f '${target_tmp_archive}'" >/dev/null 2>&1 || true
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
trap cleanup_target_tmp EXIT
|
|
||||||
|
|
||||||
# --- relay the archive from source to target ------------------------------
|
|
||||||
echo
|
|
||||||
echo "==> Transferring archive to ${target_node}..."
|
|
||||||
ssh "$source_target" "cat '${archive}'" | ssh "$target_target" "cat > '${target_tmp_archive}'"
|
|
||||||
|
|
||||||
# --- restore on the target node --------------------------------------------
|
|
||||||
echo
|
|
||||||
echo "==> Restoring as VMID ${new_vmid} on ${target_node} (storage=${target_storage})..."
|
|
||||||
if [[ "$kind" == "vm" ]]; then
|
|
||||||
ssh "$target_target" "qmrestore '${target_tmp_archive}' ${new_vmid} --storage ${target_storage} --unique 1"
|
|
||||||
else
|
|
||||||
ssh "$target_target" "pct restore ${new_vmid} '${target_tmp_archive}' --storage ${target_storage} --unique 1"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- clean up the source backup now that the restore succeeded -----------
|
|
||||||
if [[ "$keep_backup" -ne 1 ]]; then
|
|
||||||
echo
|
|
||||||
echo "==> Deleting backup archive from ${source_node}'s ${source_storage} storage..."
|
|
||||||
ssh "$source_target" "rm -f '${archive}' '${archive}.notes' '${archive}.log'" >/dev/null 2>&1 || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Done. VMID ${new_vmid} (${kind}) is now on ${target_node}, cloned from" \
|
|
||||||
"VMID ${vmid} on ${source_node}."
|
|
||||||
+65
-74
@@ -5,130 +5,121 @@ sops:
|
|||||||
age:
|
age:
|
||||||
- enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBIakJkWDQ2TjVIS1Q1UlRJ
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQUFhWVFVlVlBnNE5FTnMz
|
||||||
elVLNmhEVUpMNjROWmN6VC8wTnZzeWhLb0hFCmxmTXp3ZVdQMUhDeGlscThSdFhP
|
VkxkTmxpRXlzZ3pSNTVZWFUrSllsYWo0alRnCkJSc25TYktSTFFJdkQydHcxOUlj
|
||||||
eWNZSkFpeEg0cUUxbGFyQWIrTkJkc0EKLS0tIGRBTHdaZ2d5eHhxd3BtWVBLbFlo
|
ajhQU1ZIb1lodEpHTnVhQjJ6WEthaDQKLS0tIDJCY1E2UVBaU3BoMzhXUXlIdnMv
|
||||||
OVJlYnQ0N09qWFp2TmtXb2E0Wkk2bzQKTxA4rfkF3qlGpDqaZF/J9tgTx1UZ7ZFt
|
djZTcE1rcWNTOXFPMmFDYTVoRGo4ZTQKYy8g6pqP3VpTKDIBPbnC8NzCdDvOCKnL
|
||||||
W1KevMq/Klnkjb9XDuTEx7zgrjILoViVspe6eGI14myBNYtAP4nLSA==
|
14kSrKmKlzefTrbkVyriz2Jdl2s0F374yfQQFreZ3m4AffSACCxziQ==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
recipient: age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad
|
recipient: age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad
|
||||||
- enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUWXYyUG5UdHdFa2NDZmtt
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVQnpVWC9wcnIreG9GeE9U
|
||||||
ZG1GSmxVbTg5Qnh4UGxJaitmTmFqK2J1cUV3CmdPSU5GTC9hWkROTkJzMk5Tcmpw
|
UWhuRytkc2Flc0hyQm5yMjZnelNwaXhlWWc4CjBkWnd4cHNRRXQ0UXFkZGp4QlR1
|
||||||
ODJzdlIvNkp0ZXdZNlRlUHBkOGVoR1kKLS0tIGsvUWxpU2hkanFNaExJWGozMTJL
|
eW5NNnE1WFhnb054M1pac2ZidFg4Y3MKLS0tIFdTNmk2V1l2WC9rUk8yd0ZnOEJS
|
||||||
bndPNkpQNktmQVNKNEhGOEtHSlRBRXMK9rr6NHf3H91GIqTmckjD1eV94FW1vk9G
|
VkNnejVGVUZPZkorQkltVEplN2FmdTAKRY7DPP5HeFQntn2f/fXLjU6M1V6iug86
|
||||||
h2KauyebWPyBe8hsCExiPd45ZGqKF0g8pEtWUzonMq9NU/MNVL8YVQ==
|
BD09PI+T2DbIBQPotRZisw8IzHu9gY/O3+h0TccyIsXjI9wy/XPCAQ==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
recipient: age19gfn2yedg76dmztm4hncr7vf3r3c9j0qpt4rap7y7gersjk4m3ks2lhd0e
|
recipient: age19gfn2yedg76dmztm4hncr7vf3r3c9j0qpt4rap7y7gersjk4m3ks2lhd0e
|
||||||
- enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXdnpGR2ZnaFRSUUNlOUVy
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkczFSTVhxWHlIWjhRcWlV
|
||||||
ME9pWkFJRTl6ampuZTR0QlQ2TUVEVHV3dkRFCmxML1NSV2dwanRPK3AySDJiUU9P
|
V2JPQXd5Wnk5R3NwWC81T3Z0MW4vYnd5S1ZZClV1NlU1Tzd6UkxPQ2M4MmhLV01G
|
||||||
a2VKZ09EaktYU2xvWWpESWtrU3oxalEKLS0tIEt1aWxuNmlaV3l1OGNqS2RtMmhm
|
d3VIb0RhR1RiNTZqNjlQcmg2YjdPeGsKLS0tIDQ1RTFTWGN4MnEvWkRUR3VnN204
|
||||||
Rk9SUEpBaXY0RG9uMk53ejdJdVZ4NDQKfn4paPsHrfU3Ki2AgPBB8aLBbmD2yh1O
|
WVdFOXdmNC9FVFhBSGNEUUgyYWpYYzAKfdpeaFL/RrIbqpD9hNj8L7UxpmiBjE2I
|
||||||
9rDxv/6xSsDXNTquP11smPOKsRG7mDMDHVByn6GieZrpSxUf9vu3Iw==
|
go/dR2E1LLXsDPtnSuJb2EZYoFvSsjsIQQQDt+YwRv0fplRtssKdxQ==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
recipient: age1ll6hj5ggruetgjwjfnplpn5xtq35uhlcdflksx3xmnjm6s3uad9sz70jkf
|
recipient: age1ll6hj5ggruetgjwjfnplpn5xtq35uhlcdflksx3xmnjm6s3uad9sz70jkf
|
||||||
- enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArNHZpMDV5a2JCN1NxY2hF
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6TjhmOTJ0bUpMQWowb0hB
|
||||||
MWxtNUNVQXp6K3lWYndTRU85Wjh4YlN3UFFrCnNZbHA2Z2RUSmxITm4yeU9rYnV0
|
T3Era1loU1pMdmkxdnAvRkViekpqZWZjaWgwCkFRQXhFUy9PRVBma2JMUDhqY2F1
|
||||||
TTFkZUczcTZLU2pJRDQ3TVl5ZGErWDgKLS0tIDJGbzZZNjQ2RXUzRmUwKzdiUHlK
|
VVFDRFNVbWpNaEczY1JVQUMyck9XdEkKLS0tIGpxc0tGdVFKK3FteVJKM1Fxa2ky
|
||||||
TEU5VERpZ1p3bU1KcnEvRDRCSVNqT2MKQPfu0lskXaEAYgecmN1a0kPHF+fGEm7R
|
a21WLy9qV05hUURCTVBvcVh3cE45Z3cKXCYfXSjhApBoLbHDu2OOd57Y1zN54yy+
|
||||||
wiY7TFNLeXM5aJqEnKARtOotJDetI+9VNssTT21X/Qaik6fqgM+b1A==
|
WDQvz8PpMxhc1nU5Kw/cI+WmL1KvN0qQZfOx/7D4W+dy/ZDWX27TpA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
recipient: age120le4a5l8dh3lyfgvmj3d9ksmej6ajs5mer5y7r0vfg3x9fn69dqf8xgzu
|
recipient: age120le4a5l8dh3lyfgvmj3d9ksmej6ajs5mer5y7r0vfg3x9fn69dqf8xgzu
|
||||||
- enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOUlp5MmcvSFRnaVdWZ1g0
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZK0FYQnBHMHZ6dlpMYTlC
|
||||||
NmwvS3pGa1JCd0RuQnFMeU5wZDZYK0NQR1FJClF0Z0VENThtTzZ1OUtmUjdDenV4
|
WFdOWDFkRVBuY1pmdTFiUndLV3JXcndZa3pNCnJsd0tHN0FveWV6UUNQSEdpdWw5
|
||||||
Mko0T2o4UzVubVRCdUlCMERyV2w5WmcKLS0tIGZUallPeUhXUGtVMmNIWWRkaFVT
|
dWZITkxWelNIRlpKS1pnN0ZmVlQvZjAKLS0tIEUwMXdtNFdkUWdIRjlxc0owdTRr
|
||||||
TVNVTjhiQzJkdlVQN3p1bnR1aUYyQmcKVnqbCuaSYEA7stk1MyfCzRbqt7EL+E5/
|
c1o1TmptWWd1ZGxzcWJJNzJ0K25PTTAKoos5rnkyQBCm+ZuhCCaMJwqJBo1fpnsl
|
||||||
jgxraiFmaZqjDI5mxG/e7eFdXfv53AtKzZm92TlzvV+4bfidj5SvYw==
|
G74wu5vbTBG4VjVhI5KqyiuiTRU4jPcGxysECqe7AyZUBGp7ndewgw==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
recipient: age1jy444f9d9stygj4p3w9kh54cqcfr654tvr75tdvee5cxsgtdtc9q3v60ep
|
recipient: age1jy444f9d9stygj4p3w9kh54cqcfr654tvr75tdvee5cxsgtdtc9q3v60ep
|
||||||
- enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCWTdCWHhTamI1VHpvUzZ3
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTVkVXaCtGTXVkRXRZMFZ1
|
||||||
M2VnV3NFZTcrc2Jzazc0V3NsMDQ4RmtYZ0ZrCmVhV0pweTVLdzBLRnlJZURwNXVT
|
ZUdXM1hRcDhaSURqcGh2eksxTUNCckk5SVJjCnhkSDRKdDFSckxUWXd4SmVxWG5p
|
||||||
YzN1aHZyTmRqc0dtYjJWekRaODZoR3MKLS0tIHZPV2ppSTVwb2VHWStRY0pKSTZ5
|
bU5OV0hzaWR6VDFwcDY2WlY4WnN3cFEKLS0tIElOVzRCcXR4U0dhajJySUhaZGps
|
||||||
MXVFSE1odzRMeW9LQjVQUytBTWhHUDAK+UUowhIQ3w5O4Y/m9Pq41X2l0DZQfzOT
|
MW9rQk1JVDFWRnFxVzhCUkRIS09EamsK1rVidD48PqwlEWQyjF7iQWU7aBdPqQHy
|
||||||
itFkXJRnvUmkCxWYRDFJjQ6FpHHaNDqs0BlQZ5QZ+chzcKuUAG+uEg==
|
z5LaSi3LvJX3rNE/+q0E8/gbZyjGpbEn3AUI5mBF64GY3IZkRxZSXQ==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
recipient: age120whqj96g26lsgy4udvgsn8dc9lumh8jeu3a564fx79rjr5lxffqmrljuu
|
recipient: age120whqj96g26lsgy4udvgsn8dc9lumh8jeu3a564fx79rjr5lxffqmrljuu
|
||||||
- enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2MEZxZTI3Y0xWSDdzanRC
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvbnJCWU9UMXJXMG92b3hm
|
||||||
RytNV2x0UkY0SnRzVnR0eFNvaDRJeUtpZjBjCko2T09qSTJsQm51aW9pR3JUbkRm
|
WWNBTFpQamVWQThITmt5QUVwR3h2OHI2SlVRCjcxOEJTVkFjN0NhamFZQ1plK29w
|
||||||
NWRXODg0L08xdGNLdCtORXA1VWRteTAKLS0tIC90bkZEd2pOYWFCeGg3UW54TWxH
|
dk5XYkYxOXQ0YkVzcVc3VnhCQWlsV0UKLS0tIHZ5cWtFZUhDKzZkOE1BK2Y5TStR
|
||||||
Y1liQWVWS0t4WXBROENGNnpDVUpxRVUK57qCQ0l5Gw1ZqM50XSBNwVlXkue3QOT/
|
SFlDRjE4ZHpiVEJOQk5TUGNEN1B4amMKUCJ8CL8QpmRpFs83HD9TUn7NrPguuP8S
|
||||||
BPmPFkMNpVIcUDENj+mJAS5GHuEm1MkZcMi/wN9Hp2KE05ZywPR2Zg==
|
JQH/bzPorXTXJuyOKuKAZq1hK8BmiMUFksaZ03yN6YaFVIOeelEEMg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
recipient: age1xjst4frdh0th6q8m7p7u9g5af7ty5jqeum0p6z8a52a9q7st7ewqw8yl9j
|
recipient: age1xjst4frdh0th6q8m7p7u9g5af7ty5jqeum0p6z8a52a9q7st7ewqw8yl9j
|
||||||
- enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCaGhvbE9EU1dMZitXYlZk
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxWHV0S2dxeHZEMDNRSkhO
|
||||||
NVBEYWxHbzAwQ1FUU2JidjJPZDZLVDZLODI4CjYxaG9OSlVOeGQ5SmlGNWEzNG1n
|
ZFJHaiszd0tzYzlzd3gvbXNSQmlMVlJUNGcwCnVrM29MdFZCR1NBYnpkQ1k5VFZQ
|
||||||
WU9PRVpVRk9rd1BvWDNnMFcrZFNJZDAKLS0tIEUzV0E3OGE5cWM4MjBwTk9Mb3Np
|
b3Z2Q3ZGekVQZkZKWGlka3NDOHJ0R1EKLS0tIDlXTmNzUk0wVXo0UWhkd0ZvK3FI
|
||||||
cDJQZGNqNXlySjRLcWF4R0V0ZS9DVFEKS0CmXOfJ1qpUF24EJT6F5/6xzR7h593O
|
UzJxU3RkdWs4aTZYVVkrS056bTN1ek0KgKJNz8GvynX5pK33aW9x3v6yr2Ox0LCT
|
||||||
mLiMdOCeFYWlTtBwrD1dkweAzStiHlzTVEq3w9BFvAsr5x/NbQJUJA==
|
GGrt+ddbKLcwpBpYjfWkFhffO330EKui73S+c/qMf8N9j6wzalOTpQ==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
recipient: age10at8862478urh0eeuwh8hzln6ck78jgwtztgxatwqlzwagg77y5snm4xzg
|
recipient: age10at8862478urh0eeuwh8hzln6ck78jgwtztgxatwqlzwagg77y5snm4xzg
|
||||||
- enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzOUxYa2xOYlVYd1lWclR1
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3ZWJlbDl1TEkvWEpBMC9Y
|
||||||
bDJFTEg1SjBhNjhFT0IwY0ZxZFRFTzRUVlhZCmpwL1VBMElZNzV3UkFCV0FWb3Jr
|
ejY1MnFMUExBeDhITndxNy9YUi9hU0tXM1JJCjcwNEppcDdxYzlCSVMrMExWa3A4
|
||||||
SlN2dTNmOFRXeGJVTFgwdjA4SmdSQXMKLS0tIHZOdjFzalRkL2N5eU1iSDkzbHYw
|
cmFPSjV3LzkyMXZCUDU2QmtHRmpHRmsKLS0tIFFTdXBOaDJJTERseXlGbmdrQzhD
|
||||||
NEU3VWxBNE9NZy9hVFJwM0VnQ2RIbjQKc62J04UtVjqiU7p7GueMicdCDRTvM9zY
|
TWtnRFdIRXpsNkY0U1BiczNsdUk1V1kKGpndKmT8kj/oIxQuxQALfzscw+CsVmnj
|
||||||
IPOJSwTCatRMWeuBJIsRNkbyOLeSAesQdfAXL5GoAE8mBtBdhJKaLA==
|
cyPC3bF+tG6LcqqoKLjPSJfcIgzhnX7cAr/wwESavemLn8L/zQMe4w==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
recipient: age1ezk9x53zt8kcnscdm80jcyf0xq97vndv7jsn3rl8cc0cwm2jmpmq372dzs
|
recipient: age1ezk9x53zt8kcnscdm80jcyf0xq97vndv7jsn3rl8cc0cwm2jmpmq372dzs
|
||||||
- enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1NkppWjNXRDlsVXZ6b1ND
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpQVVyY3UrSHFWSmpDdmRE
|
||||||
OExFRXVOcXVSWmZXemNoK09EcXBHZ0JMYnd3CjlsaEM4MjdJT1lBZGFCZTNISHZV
|
dlh5akFqVFdDVFNMSHE1eVJnZzR6YzFHSVFRCko4UE9EdXNxZzF2MW5PTTN6dEdU
|
||||||
MjBRTUlTVkZBQitFc1I3ekRLN0RtYzQKLS0tIC9oelk1TTRzeFNpbWQzL0pObXJq
|
ZHM1MGowcVB2Y1ZlOTVHdnNtY3diM2cKLS0tIDBYSmh5dVVPaTM3d0ErcC8wMDNB
|
||||||
eWxHbkF3anVPRFgyQ0kvSlJqQTdLQW8KLfZ+UIzas43ROGO51KKOmy58R2tl3jwh
|
eUpHWnZlYnJsbHZuS3pwbG15UGtwN2MKVPQA1MpjIfYAsNacoAbpvZNuAIkvx7ER
|
||||||
M+1WXB8vIXA7DLQ6vMbzMPMhBnJ4kbZgRtxELg7fzSLQStLvkY+O9A==
|
CvWBKEHUVm6m8905BXzv8MdGTAk0EyCIP3aMmYqTIYfv2k9pP0T08A==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
recipient: age1fxxzpnfse8nd9wz78ht3m0plrmraacf4cpga0pe8fm2tdnqcgy8q7qsyvp
|
recipient: age1fxxzpnfse8nd9wz78ht3m0plrmraacf4cpga0pe8fm2tdnqcgy8q7qsyvp
|
||||||
- enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWVm5uZU43cFFLcC8wVkZO
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0MTQ4MzN0bDJNL1l1bmpX
|
||||||
NmlOMmlocGpwbXFkUFlCMEl5Tkx1QWJIdVRjCnFBTzdWT3NaM0N2eGlCbDV2NUU3
|
ekxDUXRNa0JHWWltZFNGTVltTFdSSE82SVZvCmdUWUdja3JIajMzY09IMUE5elox
|
||||||
Z3FnYktsZ3NHT3Y0b2hOd0xmRjlLMjgKLS0tIFBQVXVDeHkyeVNNek5CSldSMFlY
|
MDdEakFJTmtkRWF2R1BGNkQ4U3grNWsKLS0tIE9hZUhkVGI1ZEpzdDhRU21EZm91
|
||||||
a0Q5bHVlRHUxMWduMCtLRjdnMUw5RG8KDJX8I21+bUpkJ5wnX+kGhsSa8mKqsR25
|
VnJNb1kyQ05MM0RJa1lLUEtjWWxkSTAKHVAKcGcWl6LncJALRBU9RKP7ot6C6GSE
|
||||||
iNdOtVtb2WRxIAgyjR72yp1tLMrop9NkZN68MGvd/LsNM+6simyosQ==
|
1iZtj1SNX6wzEWrhOEnV37aQ8bKZj6u+Y/q6/vJ4qiBs78y/drdIzA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
recipient: age190htw7prp4vln076dxjx3gxxaq06h0zl0te7cqgpx79vl3lhkaes8suy05
|
recipient: age190htw7prp4vln076dxjx3gxxaq06h0zl0te7cqgpx79vl3lhkaes8suy05
|
||||||
- enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHcmIxZVc4aGRmNEhSY3Iv
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNUEVWY2NpVEU4OTZPZTZR
|
||||||
YXhzLzduWFdzVVlKZTR6WExzcDNtSWQza0FjCmlsZDVJUWpqY0pRSktQeGk2SlAy
|
UzdKcktpUGJOcnJ0aEhkQlhvUWdUYkV2SkdzCm51eGJVeHJMcVRRRld0dFRCYUxr
|
||||||
cjZXL0JCVFRtQXpHNkNIejhyTnVtWjgKLS0tIHBSZ1JyMFBJVWZ3VWFLOUt5OHd6
|
TTN2WEhOVjRqV0FtQXowZWNTbkJneEUKLS0tIFpUazZpTUNWZUZBSFE0VDZZbkJu
|
||||||
YUI4ODhHc1I2UHVRQ0diSnhsSUhDelEK3hBBX1+Uwe/MusLqmt4oAy7Z6jOU96Cd
|
SFVlUVhySnNqUENYOG9qUm5ZMDc1ZW8Kv0lY5dhnCEheM0sttfr4p7IL+EVog16T
|
||||||
7zTe8YyLc0/DUDsyuFZ7a68riO9/My+zrFllwtKe/JPmPHLY0+3Pnw==
|
OapUdbuXL2l7t7URzHnvfG/nbOtJIjH8a0XFsWyJChtNXpF2d/vf2g==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
recipient: age1ukpqxzl44mnjpy5r96sfuc5sqzm47u4k8ujjh5qdgy6jvl9uqgpspymqfk
|
recipient: age1ukpqxzl44mnjpy5r96sfuc5sqzm47u4k8ujjh5qdgy6jvl9uqgpspymqfk
|
||||||
- enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZSE53Yy9FOHNpeVFtTmF1
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvTzc2aExGTHFrc0ZtL3pq
|
||||||
UUExbzJlZktxMGR3Ukora3RpbVN2THlyVjNJCnFpWVEyK2U1N1diOWZQWm5Bb1lD
|
YlZLbzd3MVZHZTB0VUQvSXZ4NGVsZk42c2hBCk1vbTk0Tnl3b01vbVZaMkJ5aldE
|
||||||
djV0U3QxamRJY3BRN3hBTmZ4SVRiV0UKLS0tIFU2Q0o1UTRjaEFaSTRVTm5MVW5T
|
WmZBMGFjb2pjQXpYcnBxWmp0UUsrdXcKLS0tIG16SG9JbFdkbmVidCsxUnpBR3V3
|
||||||
QXdEbVB0Y3YwM3R5dGp5d1oxeUxHNGcKRj5hNLlXtZoT3IwXHTxaReJLu8k133n/
|
RzNOY3hIRWk4UXh6N3NrcjNSU3ZwWTgKaExY4U2s8E6ojljJ+4TU+YJhcLXyuVA1
|
||||||
ZoCtv470LPL1M/kTjdPc/nWMgYOHDKkO90gr3WILfHg8idkVuCLT2Q==
|
ROB70jQCjFvQOeo6thjQohSSUoPKhxSl1/nr4ZiGBO3/VskzihckKg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
recipient: age15kh7akxlx7zn00tey79rq2g8lgs4j5y77rcnyfxrxap8ckfu0a9sqvtdhh
|
recipient: age15kh7akxlx7zn00tey79rq2g8lgs4j5y77rcnyfxrxap8ckfu0a9sqvtdhh
|
||||||
- enc: |
|
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQazFqSWpaVGtkNFhPU3A5
|
|
||||||
cE1oVXEvSU5RNnY5NU9mbFhGOFQycVFuL1U4CnNwRnVkYmxCNFZzNVZvM2N5eVN0
|
|
||||||
clNjUWY4RHZxbVNsMFlFSnZKcmI3dm8KLS0tIFBDZTRuRlprdzdVT1ZHejNsNDBJ
|
|
||||||
OUVjclpPSjl0RGtLVlZRSkpFeG02KzAKt0rcJunZppojjijrjbXsztLwRD9pgWRb
|
|
||||||
jYsn7dCvWGFZGVgeyxqfL0jfYNz6dW0yecRE/gbP5hIooxbcPcxfag==
|
|
||||||
-----END AGE ENCRYPTED FILE-----
|
|
||||||
recipient: age1ehkswwz2pqaz4svzh7ela5tdnssl8kn6d4vwwxd6zwg8exfpd43syyrrjp
|
|
||||||
lastmodified: "2026-07-19T02:30:40Z"
|
lastmodified: "2026-07-19T02:30:40Z"
|
||||||
mac: ENC[AES256_GCM,data:UiL3VMDF6rq4Nr87KspcDx434q3tfNXeb5pwH2O+4ssNQ6xzcYDdzXBnhAY3zLBsqPMKrvHBd4Ot/gEMcq3FMIVe7Q6p9yWKpep66KZ/yWEhAlwIVhD79Oj8VS+1CHKjf25zpRdhZorp04oeFQQd9VfjJB4EE/Q1aVbwTGlpIic=,iv:i/0conaFgFia+wzNTdUL6tlSTw35HTK3Ap1Sr5RGHf8=,tag:ULbz5FllShA/JjlSRdxA0g==,type:str]
|
mac: ENC[AES256_GCM,data:UiL3VMDF6rq4Nr87KspcDx434q3tfNXeb5pwH2O+4ssNQ6xzcYDdzXBnhAY3zLBsqPMKrvHBd4Ot/gEMcq3FMIVe7Q6p9yWKpep66KZ/yWEhAlwIVhD79Oj8VS+1CHKjf25zpRdhZorp04oeFQQd9VfjJB4EE/Q1aVbwTGlpIic=,iv:i/0conaFgFia+wzNTdUL6tlSTw35HTK3Ap1Sr5RGHf8=,tag:ULbz5FllShA/JjlSRdxA0g==,type:str]
|
||||||
unencrypted_suffix: _unencrypted
|
unencrypted_suffix: _unencrypted
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
wifi-password: ENC[AES256_GCM,data:SZQPtU6PYHbf9o83wq3KTupx,iv:FxO68Pn/+N58r/OPLfkAMYPFpP8TYxszMniFd/01E38=,tag:jwxaY6zDEcO5r9OWSfvUyw==,type:str]
|
|
||||||
sops:
|
|
||||||
age:
|
|
||||||
- enc: |
|
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrWFpRSURBR0gvRzVIMElk
|
|
||||||
ZlJGZlJvc3lFOVJIZ29YbFp3akFCNG42TWkwCjhNM0J6cjIwRXBzc3VWNVpZSGdL
|
|
||||||
Q1M1bm90OE1tTG9GbUxvS1dvRkZ0UGsKLS0tIE43L1dnbWhQOUhhYjg5bEIvZkVD
|
|
||||||
Zkh1NDhvZjlDc2c0cUZUYVRLdEozS2sKU/r6JnEnUs2WPj/J724B+lgiV84iteZa
|
|
||||||
uMlhgnwYJFLkH7ZyydQqjYcHL7xEInr0taYJN+M0nZIsZTvzAEi7iw==
|
|
||||||
-----END AGE ENCRYPTED FILE-----
|
|
||||||
recipient: age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad
|
|
||||||
- enc: |
|
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXZmYrVTZxWW1QKzhLWThX
|
|
||||||
UFAwSEhId24yMitmVk1kT215OGdqbGp4akFjCjRzN2dkTURXS0wzbzhYL0YxRW41
|
|
||||||
TWJ3UExXaHNhSUxaYkVsMFNCekZHZDgKLS0tIGQrSWNzalhCbGJZdkxvT1N5ZWlo
|
|
||||||
a0l0Nk9DY1BKYU9ReWovaHJ0Z2NyWGcKFoIYS1M4EbR6H6QG3Wjv2ZdX3r2W8zKp
|
|
||||||
S9f578O5ZLh2OWaawcSb0oecZJykT9pgudVcuKRunzZN7NQvFxBRnQ==
|
|
||||||
-----END AGE ENCRYPTED FILE-----
|
|
||||||
recipient: age190htw7prp4vln076dxjx3gxxaq06h0zl0te7cqgpx79vl3lhkaes8suy05
|
|
||||||
- enc: |
|
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlWnpPUk10YW82TVRzbk80
|
|
||||||
WHJqbXVVSUFrSTVjMEdqUTdSQWwvdG1TcW1nClQ5T0E0akFtSlFGdC9WaU1LbGkw
|
|
||||||
allOSjJ2SjQ0MkVMcVNtdzN3cW1hVzgKLS0tIERlQ2l3NitPQndERTYrNVA3ajF0
|
|
||||||
bCtnYzhHcnZkalRNK1BHbjdIS2JLNmcK5RwFzeaK1KafO4cAtdFh5Tnz1lpZbj1Q
|
|
||||||
aeMmTGMkxJYVGzXKS6SRspVT3MvY4Fvay8B0MezjG5Y5HKNIv22Vog==
|
|
||||||
-----END AGE ENCRYPTED FILE-----
|
|
||||||
recipient: age1ehkswwz2pqaz4svzh7ela5tdnssl8kn6d4vwwxd6zwg8exfpd43syyrrjp
|
|
||||||
lastmodified: "2026-07-22T01:15:21Z"
|
|
||||||
mac: ENC[AES256_GCM,data:dC/oIqMUHkOh3AocOwP7Gc6XGH3L+nTqJfhFNts1DNbRXsopNIxVBtIz2pEhwnWSQrqPisDLmPHFBwRpGVn01u8w8IU1FKbAKC0J2nJXF8ozpInbjzDOmehqPWZG7yaKoq8cwAnp5XOk+IVO4l6tPxLxkExU5fT2ALuMq+sgOko=,iv:jaVyArpf6zMCFa6J9X1aQMGrmFq+W2CPZdWO6vVW68c=,tag:S+qF8/FkgHc4uW0e4ICmSQ==,type:str]
|
|
||||||
unencrypted_suffix: _unencrypted
|
|
||||||
version: 3.13.2
|
|
||||||
@@ -45,20 +45,6 @@
|
|||||||
# the installer image's nixos/root users.
|
# the installer image's nixos/root users.
|
||||||
adminSshKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCq/Q5LvIXlZwO2kdeAN5nLGZ59nZB7JHYMEszHxmNtGMzv1lM31jiPNsr0z2EKVZhE7OOfa2IF9rhWYD7JUA9G0yzdZ4WTXFNGVVOJoOVH6vAF3XCxoVilOEwTc7h2Wiy+rzd0B28/3spffzQQWJhY6GRQVa8j+6xAGF60Fcvl1vLosYT9Bn2ZbK4TCWOwAn2jqXIieGpZdn/UNZbGOeKRiCvhktDfMAzuQzN/9jMu/oF4pkPn2X1UrsQdNlvp0Ci8md612MozIpncQJyAF1ADhunr3sMx0isUXiqD29R5DS4TftpekqLNLak+zcxFa8N7DcRNp3DcKfJvyTkwQrR4r+b7lFLYOLHLagSso9CzeW/paAS2q9I5SBm/2DtE1diLLg2jZikYcstsu/G5RgvbzbKqjiaMwTdXC3AMvDxQrs7U5pDRZFzoofG3cpODbTm+uy3m0kP70z0M1K45UbDG0p+itnTu9x40JbQEgefbx38AItNvAIx1A8HO4I1VX28= wayne@stream";
|
adminSshKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCq/Q5LvIXlZwO2kdeAN5nLGZ59nZB7JHYMEszHxmNtGMzv1lM31jiPNsr0z2EKVZhE7OOfa2IF9rhWYD7JUA9G0yzdZ4WTXFNGVVOJoOVH6vAF3XCxoVilOEwTc7h2Wiy+rzd0B28/3spffzQQWJhY6GRQVa8j+6xAGF60Fcvl1vLosYT9Bn2ZbK4TCWOwAn2jqXIieGpZdn/UNZbGOeKRiCvhktDfMAzuQzN/9jMu/oF4pkPn2X1UrsQdNlvp0Ci8md612MozIpncQJyAF1ADhunr3sMx0isUXiqD29R5DS4TftpekqLNLak+zcxFa8N7DcRNp3DcKfJvyTkwQrR4r+b7lFLYOLHLagSso9CzeW/paAS2q9I5SBm/2DtE1diLLg2jZikYcstsu/G5RgvbzbKqjiaMwTdXC3AMvDxQrs7U5pDRZFzoofG3cpODbTm+uy3m0kP70z0M1K45UbDG0p+itnTu9x40JbQEgefbx38AItNvAIx1A8HO4I1VX28= wayne@stream";
|
||||||
|
|
||||||
# Prestaged wifi SSID for the gui host's NetworkManager profile
|
|
||||||
# (modules/networking/wifi.nix). The password is not here -- it's
|
|
||||||
# sops-encrypted in secrets/gui.yaml (wifi-password) instead, since this
|
|
||||||
# file isn't a secret store.
|
|
||||||
wifiSsid = "nbn-fttp-net-5G";
|
|
||||||
|
|
||||||
# Bare-metal gui host's two disks for a ZFS RAID0 (striped) root pool
|
|
||||||
# (modules/disko/baremetal.nix). Only used transiently at disko-format
|
|
||||||
# time (partitioning); the resulting fileSystems/zpool import reference
|
|
||||||
# by-partlabel/by-id paths afterward regardless, same as
|
|
||||||
# modules/disko/proxmox.nix's own plain "/dev/sda".
|
|
||||||
guiRootDisk1 = "/dev/sda";
|
|
||||||
guiRootDisk2 = "/dev/sdb";
|
|
||||||
|
|
||||||
# System
|
# System
|
||||||
timeZone = "Australia/Brisbane";
|
timeZone = "Australia/Brisbane";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user