Archived
Fix LXC deployment path and clean up remaining eval warnings
LXC hosts (device busy fix): modules/platforms/lxc.nix now imports nixpkgs' own virtualisation/proxmox-lxc.nix, giving every lxc-* host a real config.system.build.tarball output — a directly `pct restore`-able Proxmox container image. This is the actual bug fix behind the "cannot remove real root directory: device busy or in use" error: lxc-* targets were only reachable through nixos-install, which bind-mounts / onto /mnt for containers (no raw disk to partition) and then correctly refuses to modify the filesystem it's currently running on. auto-install.sh's menu now excludes lxc-* targets entirely (they deploy via nix build + pct restore instead, see docs/auto-installer.md) — and, on the same reasoning, also excludes `installer`/`proxmox-lxc`, which are the installer image's own flake targets, not deployable hosts. manageHostName = true keeps host.nix's declared hostnames (upstream's default would let Proxmox's ambient container config win instead); privileged = false matches how these containers are actually created. Eval warnings, now zero across all 19 nixosConfigurations + 4 packages: - Multiple password options (root/nixos in the installer): nixpkgs' own installer profile sets initialHashedPassword = "" for passwordless login, conflicting with our explicit hashedPassword. Force-nulled the upstream option rather than adopting passwordless login, since this image now also boots over LAN PXE with PasswordAuthentication enabled. - boot.zfs.forceImportRoot default value: set explicitly to false (matching the two places that already did) in modules/common/configuration.nix and modules/installer/common.nix, covering every host and the installer alike. - Deprecated pkgs.system in modules/build-types/gui.nix: switched to pkgs.stdenv.hostPlatform.system. All confirmed non-behavioral where it matters: unrelated hosts' drvPaths are byte-identical to their pre-existing baselines throughout. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01La55Nsss8jZ7ZuzUV9mfot
This commit is contained in:
@@ -12,6 +12,38 @@ discovers available hosts from this same flake, lets the operator choose a
|
|||||||
target, applies that host's Disko storage configuration, installs NixOS, and
|
target, applies that host's Disko storage configuration, installs NixOS, and
|
||||||
reboots.
|
reboots.
|
||||||
|
|
||||||
|
**This applies to every `nixosConfigurations` target except `lxc-*` hosts —
|
||||||
|
see "LXC hosts" immediately below for why those are different.**
|
||||||
|
|
||||||
|
## LXC hosts
|
||||||
|
|
||||||
|
`lxc-*` targets (`lxc-minimal`, `lxc-nix-cache`, `lxc-server`, `lxc-docker`,
|
||||||
|
`lxc-gui`, `lxc-pxe-boot`) are **not** installed via `auto-install.sh` — the
|
||||||
|
interactive menu deliberately excludes them. Don't try to select one there;
|
||||||
|
`nixos-install` would bind-mount `/` onto `/mnt` (LXC containers have no raw
|
||||||
|
disk to partition) and then refuse to touch the filesystem it's currently
|
||||||
|
running on — it's designed to protect exactly this case, so it just fails.
|
||||||
|
|
||||||
|
`modules/platforms/lxc.nix` imports nixpkgs' own
|
||||||
|
`virtualisation/proxmox-lxc.nix` module, which gives every `lxc-*` host a
|
||||||
|
`config.system.build.tarball` output — a complete, directly Proxmox-importable
|
||||||
|
container image, no install step at all:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
nix build .#nixosConfigurations.lxc-minimal.config.system.build.tarball
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, on the Proxmox host, `pct restore` (or the GUI's "Create CT" → upload
|
||||||
|
template flow) that tarball directly as a new container. First boot runs
|
||||||
|
`boot.postBootCommands` (registers the Nix store DB and system profile) —
|
||||||
|
there's no separate activation step to run yourself.
|
||||||
|
|
||||||
|
Host keys still need pre-seeding the same way as any other host (see "Host
|
||||||
|
keys" below) — the sops-nix activation-vs-first-boot race is identical
|
||||||
|
regardless of how the image reaches the machine. `NIXOS_HOST_KEYS_DIR=...
|
||||||
|
nix build ... --impure` bakes the matching key into the tarball the same way
|
||||||
|
it does for the ISO/PXE installer images.
|
||||||
|
|
||||||
## Layout
|
## Layout
|
||||||
|
|
||||||
- `modules/installer/common.nix` — shared by every installer target: SSH
|
- `modules/installer/common.nix` — shared by every installer target: SSH
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
inputs.nixos-conf-editor.packages.${pkgs.system}.nixos-conf-editor
|
inputs.nixos-conf-editor.packages.${pkgs.stdenv.hostPlatform.system}.nixos-conf-editor
|
||||||
nodejs
|
nodejs
|
||||||
appimage-run
|
appimage-run
|
||||||
seahorse
|
seahorse
|
||||||
|
|||||||
@@ -13,6 +13,12 @@
|
|||||||
|
|
||||||
networking.networkmanager.enable = true; # Easiest to use and most distros use this by default.
|
networking.networkmanager.enable = true; # Easiest to use and most distros use this by default.
|
||||||
|
|
||||||
|
# Recommended over the true default (bypasses ZFS's own import safeguards)
|
||||||
|
# per the option's own docs; matches hosts/docker/host.nix and
|
||||||
|
# modules/services/zfs/enable-service.nix, which already set this
|
||||||
|
# explicitly. Harmless no-op on hosts that don't use ZFS at all.
|
||||||
|
boot.zfs.forceImportRoot = false;
|
||||||
|
|
||||||
# Set your time zone.
|
# Set your time zone.
|
||||||
time.timeZone = vars.timeZone;
|
time.timeZone = vars.timeZone;
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,12 @@
|
|||||||
|
|
||||||
networking.useDHCP = lib.mkDefault true;
|
networking.useDHCP = lib.mkDefault true;
|
||||||
|
|
||||||
|
# Recommended over the true default (bypasses ZFS's own import safeguards)
|
||||||
|
# per the option's own docs. This installer environment has no ZFS pools
|
||||||
|
# of its own to import, so this is a no-op here — just silences the
|
||||||
|
# eval-time warning, matching modules/common/configuration.nix.
|
||||||
|
boot.zfs.forceImportRoot = false;
|
||||||
|
|
||||||
time.timeZone = vars.timeZone;
|
time.timeZone = vars.timeZone;
|
||||||
|
|
||||||
# Without this, the installer only ever sees cache.nixos.org, which
|
# Without this, the installer only ever sees cache.nixos.org, which
|
||||||
@@ -50,11 +56,26 @@
|
|||||||
export FLAKE_BASE_URL="git+https://${vars.lanDomain}/beatzaplenty/nixos.git"
|
export FLAKE_BASE_URL="git+https://${vars.lanDomain}/beatzaplenty/nixos.git"
|
||||||
|
|
||||||
echo "Fetching available NixOS hosts from flake..."
|
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/proxmox-lxc — these *are* the installer image's own
|
||||||
|
# flake targets, not deployable hosts;
|
||||||
|
# "installing" one means nixos-install-ing
|
||||||
|
# a copy of the installer into itself.
|
||||||
mapfile -t options < <(
|
mapfile -t options < <(
|
||||||
nix eval --json --no-use-registries --no-accept-flake-config --extra-experimental-features "flakes nix-command" \
|
nix eval --json --no-use-registries --no-accept-flake-config --extra-experimental-features "flakes nix-command" \
|
||||||
"''${FLAKE_BASE_URL}#nixosConfigurations" \
|
"''${FLAKE_BASE_URL}#nixosConfigurations" \
|
||||||
--apply builtins.attrNames \
|
--apply builtins.attrNames \
|
||||||
| jq -r '.[]'
|
| jq -r '.[]
|
||||||
|
| select(startswith("lxc-") | not)
|
||||||
|
| select(. != "installer")
|
||||||
|
| select(. != "proxmox-lxc")'
|
||||||
)
|
)
|
||||||
|
|
||||||
if [[ ''${#options[@]} -eq 0 ]]; then
|
if [[ ''${#options[@]} -eq 0 ]]; then
|
||||||
@@ -62,6 +83,10 @@
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
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:"
|
echo "Choose the flake profile to install:"
|
||||||
select choice in "''${options[@]}"; do
|
select choice in "''${options[@]}"; do
|
||||||
if [[ -n "$choice" ]]; then
|
if [[ -n "$choice" ]]; then
|
||||||
@@ -190,9 +215,18 @@
|
|||||||
PasswordAuthentication = true;
|
PasswordAuthentication = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# nixpkgs' own installer profile (profiles/installation-device.nix, pulled
|
||||||
|
# in via installation-cd-minimal.nix) sets initialHashedPassword = "" for
|
||||||
|
# both users — its own passwordless-login convention for install media.
|
||||||
|
# That's a second, non-null password option alongside our hashedPassword
|
||||||
|
# below, which NixOS warns about as ambiguous precedence. Force it null
|
||||||
|
# rather than adopting passwordless login: this image now also boots over
|
||||||
|
# LAN PXE with PasswordAuthentication enabled, so passwordless root SSH
|
||||||
|
# would be reachable by anyone on the LAN, not just local console.
|
||||||
users.users.root = {
|
users.users.root = {
|
||||||
hashedPassword =
|
hashedPassword =
|
||||||
"$6$Kwv9KAyvcurAViQF$H4.u3feqGE7lVoNgkFXhE3n2Pmo//9JYDTCz8ifrVHBxPjwa1xMby7tEZ8Bpt5MXs9Rkx6/YbZWxs5CpH0s/70";
|
"$6$Kwv9KAyvcurAViQF$H4.u3feqGE7lVoNgkFXhE3n2Pmo//9JYDTCz8ifrVHBxPjwa1xMby7tEZ8Bpt5MXs9Rkx6/YbZWxs5CpH0s/70";
|
||||||
|
initialHashedPassword = lib.mkForce null;
|
||||||
};
|
};
|
||||||
|
|
||||||
users.users.${vars.primaryUser} = {
|
users.users.${vars.primaryUser} = {
|
||||||
@@ -206,6 +240,7 @@
|
|||||||
|
|
||||||
hashedPassword =
|
hashedPassword =
|
||||||
"$6$Kwv9KAyvcurAViQF$H4.u3feqGE7lVoNgkFXhE3n2Pmo//9JYDTCz8ifrVHBxPjwa1xMby7tEZ8Bpt5MXs9Rkx6/YbZWxs5CpH0s/70";
|
"$6$Kwv9KAyvcurAViQF$H4.u3feqGE7lVoNgkFXhE3n2Pmo//9JYDTCz8ifrVHBxPjwa1xMby7tEZ8Bpt5MXs9Rkx6/YbZWxs5CpH0s/70";
|
||||||
|
initialHashedPassword = lib.mkForce null;
|
||||||
|
|
||||||
openssh.authorizedKeys.keys = [
|
openssh.authorizedKeys.keys = [
|
||||||
vars.adminSshKey
|
vars.adminSshKey
|
||||||
|
|||||||
+30
-28
@@ -1,36 +1,38 @@
|
|||||||
{ lib, ... }:
|
{ lib, modulesPath, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
boot = {
|
# LXC containers share the host kernel — Proxmox starts them by exec'ing
|
||||||
isContainer = true;
|
# /sbin/init directly, no bootloader/initrd involved — and Proxmox has its
|
||||||
|
# own container hostname/network provisioning outside Nix. nixpkgs' own
|
||||||
|
# virtualisation/proxmox-lxc.nix module already handles all of this
|
||||||
|
# correctly (boot.isContainer, loader.initScript, systemd-networkd) and,
|
||||||
|
# critically, provides config.system.build.tarball — a directly
|
||||||
|
# `pct restore`-able container image, no nixos-install/bind-mount needed
|
||||||
|
# (nixos-install refuses to touch the filesystem it's currently running
|
||||||
|
# on, which is exactly what bind-mounting / onto /mnt for an installer
|
||||||
|
# LXC container does).
|
||||||
|
imports = [
|
||||||
|
(modulesPath + "/virtualisation/proxmox-lxc.nix")
|
||||||
|
];
|
||||||
|
|
||||||
loader = {
|
proxmoxLXC = {
|
||||||
|
# host.nix declares each host's real hostname (networking.hostName);
|
||||||
|
# keep that instead of letting Proxmox's ambient container config win.
|
||||||
|
manageHostName = true;
|
||||||
|
# Unprivileged matches how these containers are actually created.
|
||||||
|
privileged = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
boot.loader = {
|
||||||
grub.enable = false;
|
grub.enable = false;
|
||||||
systemd-boot.enable = false;
|
systemd-boot.enable = false;
|
||||||
|
|
||||||
# LXC containers share the host kernel — Proxmox starts them by exec'ing
|
|
||||||
# /sbin/init directly, no bootloader/initrd involved. Without this, that
|
|
||||||
# file doesn't correctly launch the current generation, so even a
|
|
||||||
# correctly-installed system can fail to come up after reboot.
|
|
||||||
initScript.enable = true;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
networking = {
|
# NetworkManager depends on a running udevd to enumerate/classify devices,
|
||||||
# boot.isContainer disables services.udev (see nixpkgs'
|
# which boot.isContainer disables (see nixpkgs' container-config.nix) —
|
||||||
# virtualisation/container-config.nix), and NetworkManager depends on a
|
# that's what broke DHCP-hostname registration in Pi-hole. The imported
|
||||||
# running udevd to enumerate/classify devices. That leaves NM unable to
|
# proxmox-lxc.nix module already switches networking to systemd-networkd
|
||||||
# reliably manage the container's veth interface, which is what broke
|
# for the same reason; it just doesn't disable NetworkManager itself,
|
||||||
# DHCP-hostname registration in Pi-hole. systemd-networkd talks to the
|
# which modules/common/configuration.nix enables for every host.
|
||||||
# kernel over rtnetlink directly and doesn't have that dependency.
|
networking.networkmanager.enable = lib.mkForce false;
|
||||||
networkmanager.enable = lib.mkForce false;
|
|
||||||
useNetworkd = true;
|
|
||||||
|
|
||||||
# container-config.nix also defaults this to true, which assumes a
|
|
||||||
# systemd-nspawn-style host bind-mount of /etc/resolv.conf. Real Proxmox
|
|
||||||
# LXC doesn't do that (nixpkgs' own virtualisation/proxmox-lxc.nix module
|
|
||||||
# forces this false for the same reason), so leaving it true silently
|
|
||||||
# breaks DNS instead of falling back to networkd/DHCP-provided servers.
|
|
||||||
useHostResolvConf = lib.mkForce false;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user