Archived
Add Proxmox VM disk-image building; fix disko confirmation bypass
modules/disko/proxmox.nix gains imageSize (20G default) and a per-host imageName (networking.hostName, so every proxmox-* host produces a distinctly named image instead of an identical main.raw). This is the same disko.devices config already used to format a real disk on install, so it's available for every proxmox-* target with no per-host changes needed: nix build .#nixosConfigurations.<host>.config.system.build.diskoImagesScript sudo ./result --build-memory 2048 docs/proxmox-images.md covers building, host-key pre-seeding via disko's --pre-format-files (same host-keys/ workflow as the installer and LXC tarball paths), and the qm import/attach sequence for deploying the result to Proxmox. Also fixes a real bug in auto-install.sh found while testing: the disko confirmation bypass used --yes, which disko's CLI doesn't recognize at all (the actual flag is --yes-wipe-all-disks) — so the "skip confirmation" flag was silently a no-op and the interactive prompt kept appearing regardless. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01La55Nsss8jZ7ZuzUV9mfot
This commit is contained in:
@@ -0,0 +1,107 @@
|
|||||||
|
# Proxmox VM disk images
|
||||||
|
|
||||||
|
`proxmox-*` hosts (VM platform, not `lxc-*`) can be built as standalone,
|
||||||
|
ready-to-attach `.raw` disk images via disko's own image-builder — no
|
||||||
|
`nixos-install`, no live installer boot. This uses the same `disko.devices`
|
||||||
|
config (`modules/disko/proxmox.nix`) already used to format a real disk on
|
||||||
|
install, so there's nothing host-specific to write; it's available for every
|
||||||
|
`proxmox-*` target automatically.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
```sh
|
||||||
|
nix build .#nixosConfigurations.proxmox-server.config.system.build.diskoImagesScript
|
||||||
|
sudo ./result --build-memory 2048
|
||||||
|
```
|
||||||
|
|
||||||
|
This produces `<hostname>.raw` in the current directory (e.g. `server.raw`
|
||||||
|
for `proxmox-server`, matching `networking.hostName`, not the flake attribute
|
||||||
|
name — every `proxmox-*` host gets a distinctly named image instead of all
|
||||||
|
of them producing an identical `main.raw`). The script builds inside a
|
||||||
|
temporary QEMU VM and moves the finished image out to the working directory
|
||||||
|
when done; `--build-memory` controls how much RAM that build VM gets.
|
||||||
|
|
||||||
|
`disko.devices.disk.main.imageSize` (currently `20G`, in
|
||||||
|
`modules/disko/proxmox.nix`) sets the image's total size — disko doesn't
|
||||||
|
support auto-resizing, so this needs to comfortably fit ESP + swap + root at
|
||||||
|
build time. Grow the virtual disk (and resize the filesystem) in Proxmox
|
||||||
|
after attaching if a host needs more than that; this is the normal way to
|
||||||
|
size these images, not a one-time decision to get exactly right up front.
|
||||||
|
|
||||||
|
## Host keys
|
||||||
|
|
||||||
|
The disko image script runs a real activation pass inside its temporary
|
||||||
|
build VM while constructing the image — the same sops-nix
|
||||||
|
activation-before-first-boot problem the installer and LXC tarball workflows
|
||||||
|
have (see `docs/auto-installer.md`) applies here too, unmodified. Disko has
|
||||||
|
a native mechanism for it:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo ./result \
|
||||||
|
--pre-format-files host-keys/server_ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key \
|
||||||
|
--pre-format-files host-keys/server_ssh_host_ed25519_key.pub /etc/ssh/ssh_host_ed25519_key.pub \
|
||||||
|
--build-memory 2048
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate the key first with `scripts/prepare-host-key.sh <hostname>`, same
|
||||||
|
as any other host — see `docs/auto-installer.md` for the full sops.yaml
|
||||||
|
registration walkthrough.
|
||||||
|
|
||||||
|
## Deploying to Proxmox
|
||||||
|
|
||||||
|
The image needs **UEFI (OVMF)**, not Proxmox's default SeaBIOS —
|
||||||
|
`modules/boot/efi.nix` uses `systemd-boot`, which only works with UEFI
|
||||||
|
firmware. `virtio-scsi` is safe to use as the disk bus:
|
||||||
|
`hardware-configuration/vm/proxmox.nix` already includes `virtio_scsi` in
|
||||||
|
its initrd kernel modules.
|
||||||
|
|
||||||
|
1. Copy the image to the Proxmox host:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scp server.raw root@<proxmox-host>:/var/lib/vz/import/
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Create an empty VM shell (no disk yet) — replace `<vmid>` with a free ID
|
||||||
|
and `<storage>` with your storage pool's name (`pvesm status` or
|
||||||
|
Datacenter → Storage in the web UI):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
qm create <vmid> --name proxmox-server --memory 2048 --cores 2 \
|
||||||
|
--net0 virtio,bridge=vmbr0 \
|
||||||
|
--bios ovmf --machine q35 \
|
||||||
|
--scsihw virtio-scsi-pci \
|
||||||
|
--efidisk0 <storage>:1,efitype=4m,pre-enrolled-keys=0
|
||||||
|
```
|
||||||
|
|
||||||
|
(`--efidisk0` is required for UEFI — it's where OVMF persists boot-entry
|
||||||
|
NVRAM; without it, systemd-boot's boot entry may not survive a reboot.)
|
||||||
|
|
||||||
|
3. Import the raw disk into storage:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
qm importdisk <vmid> /var/lib/vz/import/server.raw <storage>
|
||||||
|
```
|
||||||
|
|
||||||
|
This prints the resulting disk identifier (e.g. `vm-<vmid>-disk-1`).
|
||||||
|
|
||||||
|
4. Attach it and set it as the boot disk:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
qm set <vmid> --scsi0 <storage>:vm-<vmid>-disk-1
|
||||||
|
qm set <vmid> --boot order=scsi0
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Boot it:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
qm start <vmid>
|
||||||
|
```
|
||||||
|
|
||||||
|
No install step — it boots straight into the already-activated system.
|
||||||
|
|
||||||
|
## Why not `nix build .#nixosConfigurations.<host>.config.system.build.vm`?
|
||||||
|
|
||||||
|
That's a different, unrelated feature — `system.build.vm` (`nixos-rebuild
|
||||||
|
build-vm`) produces an ephemeral QEMU script for locally testing a
|
||||||
|
configuration, not a distributable disk image. It's not part of this
|
||||||
|
workflow.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
_:
|
{ config, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
disko.devices = {
|
disko.devices = {
|
||||||
@@ -6,6 +6,16 @@ _:
|
|||||||
type = "disk";
|
type = "disk";
|
||||||
device = "/dev/sda";
|
device = "/dev/sda";
|
||||||
|
|
||||||
|
# Only used when building a standalone disk image directly (`nix build
|
||||||
|
# .#nixosConfigurations.<host>.config.system.build.diskoImagesScript`)
|
||||||
|
# rather than formatting a real device — see docs/proxmox-images.md.
|
||||||
|
# imageSize sets the .raw file's total size (root's "100%" below fills
|
||||||
|
# whatever's left after ESP + swap within it); imageName keeps each
|
||||||
|
# host's image distinctly named instead of every proxmox-* host
|
||||||
|
# producing an identical "main.raw".
|
||||||
|
imageSize = "20G";
|
||||||
|
imageName = config.networking.hostName;
|
||||||
|
|
||||||
content = {
|
content = {
|
||||||
type = "gpt";
|
type = "gpt";
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,7 @@
|
|||||||
|
|
||||||
if nix eval --refresh --json --extra-experimental-features "flakes nix-command" "''${nix_extra_opts[@]}" "''${FLAKE_BASE_URL}#nixosConfigurations.''${choice}.config.disko.devices.disk.main.device" >/dev/null 2>&1; then
|
if nix eval --refresh --json --extra-experimental-features "flakes nix-command" "''${nix_extra_opts[@]}" "''${FLAKE_BASE_URL}#nixosConfigurations.''${choice}.config.disko.devices.disk.main.device" >/dev/null 2>&1; then
|
||||||
disko --mode destroy,format,mount \
|
disko --mode destroy,format,mount \
|
||||||
--flake "''${FLAKE_BASE_URL}#''${choice}" "''${nix_extra_opts[@]}" --yes
|
--flake "''${FLAKE_BASE_URL}#''${choice}" "''${nix_extra_opts[@]}" --yes-wipe-all-disks
|
||||||
else
|
else
|
||||||
# No raw disk to partition — true for every LXC container, which has
|
# No raw disk to partition — true for every LXC container, which has
|
||||||
# no block device visible from inside it, only its already-mounted
|
# no block device visible from inside it, only its already-mounted
|
||||||
|
|||||||
Reference in New Issue
Block a user