Skip to main content
RunBook Academy

Proxmox VEIII · Installation & BaselineAutomated installation

Automated installation with answer files

Advanced⏱ ~32 min🧪 Lab requiredproxmox-auto-install-assistantxorriso

What you'll learn

  • Write and validate an answer.toml covering global, network and disk-setup
  • Choose between the iso, partition and http fetch modes for your environment
  • Select disks and NICs by UDEV property so an answer file survives a hardware change
  • Attach a first-boot hook so a node arrives configured rather than merely installed

Prerequisites

Verified against Proxmox VE 9.2.4 · Proxmox Backup Server 4.2.5 · Ceph Squid / Tentacle · Debian 13 (Trixie) · Linux kernel 7.0 (PVE 9.2 default) · 2026-08-12

Not yet marked complete on this device.

The interactive installer produces a node. It does not produce a procedure. Every choice made in front of it — the ZFS layout, the ashift, the swap size, the management VLAN — exists only in the memory of whoever clicked through it, and the next node is built from that memory.

Two situations turn that into a real problem. Building a fleet, where nodes must be identical and are not. And rebuilding a failed node at 03:00, where the person at the keyboard is not the person who built it and every answer they guess is a divergence they will discover weeks later.

The answer file is the fix. It is a TOML document describing every choice the installer would ask about, and the installer applies it without interaction.

The tool

Configuration changeinstall the assistant
apt update
apt install proxmox-auto-install-assistant xorriso

proxmox-auto-install-assistant has six subcommands, and you will use all of them:

SubcommandWhat it does
validate-answerChecks an answer file for syntax and schema errors
device-infoPrints UDEV properties for disks or network interfaces
device-matchTests a filter expression against the hardware present
prepare-isoProduces a bootable ISO or PXE tree with the fetch mode baked in
inspect-isoReports what a prepared ISO was configured to do
system-infoPrints the machine identification an HTTP answer server receives

The answer file

Four sections. [global], [network] and [disk-setup] are required; the rest are optional.

[global]
keyboard = "en-us"
country = "gb"
fqdn = "pve01.example.com"
mailto = "platform-alerts@example.com"
timezone = "Europe/London"
root-password-hashed = "REPLACE_ME-with-mkpasswd-output"
root-ssh-keys = [
  "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAREPLACEME platform-team",
]
reboot-on-error = false

[network]
source = "from-dhcp"

[disk-setup]
filesystem = "zfs"
disk-list = ["sda", "sdb"]

[disk-setup.zfs]
raid = "raid1"
ashift = 12
compress = "lz4"
arc-max = 16384

That is a complete, working answer file. Everything below is about making it correct for a fleet rather than for one machine.

[global]

keyboard, country, timezone and mailto are what the interactive installer asks. fqdn needs more thought than it looks.

root-password versus root-password-hashed. Set one, never both. The plaintext form puts a working root password in a file that will be copied into a repository, a PXE tree, and probably a ticket. Generate a hash instead:

Read-only / Safegenerate a password hash for the answer file
mkpasswd --method=yescrypt

A hash in an answer file is still a credential — it is offline-crackable — so it belongs in the same store as any other secret and not in a public repository. What it is not is a password that works if someone reads the file.

root-ssh-keys is what makes the node usable without the password at all. Populate it and the root password becomes a console-only fallback.

reboot-on-error = false is the setting to keep at its default while you are developing the answer file. On a failure the installer stays on screen with the error visible; with it set to true, a failing node reboots into the installer again and loops, showing you nothing.

A per-node FQDN is the one field that stops an answer file being reusable — which is what fqdn.source exists for:

[global]
fqdn.source = "from-dhcp"
fqdn.domain = "example.com"

The installer takes the hostname from DHCP option 12 and the domain from option 15, falling back to fqdn.domain when the DHCP server does not supply one. Now the same answer file builds every node in the rack, and identity comes from the DHCP reservation, which is already where your IPAM lives.

subscription-key is available and implies the proxmox-first-boot package. Putting a subscription key in a file that gets copied around is a decision to make deliberately.

[network]

Two modes. source = "from-dhcp" takes everything from DHCP. source = "from-answer" requires cidr, gateway and dns, and a filter to pick the interface.

[network]
source = "from-answer"
cidr = "192.0.2.11/24"
gateway = "192.0.2.1"
dns = "192.0.2.53"
filter.ID_NET_NAME_MAC = "*e43d1afa379a"

DHCP with a reservation is generally the better answer for a fleet: the address still comes from a controlled place, and the answer file stays identical across nodes.

Interface name pinning, available since PVE 9.1, is the section that prevents a class of incident that predates automation entirely:

[network.interface-name-pinning]
enabled = true

[network.interface-name-pinning.mapping]
"24:8a:07:1e:05:bc" = "lan0"
"24:8a:07:1e:05:bd" = "lan1"
"b4:2e:99:ac:ad:b4" = "mgmt"

Without pinning, the installer names interfaces nic0, nic1, nic2. With it, a MAC gets a stable name you chose. That matters because /etc/network/interfaces refers to interfaces by name, and a name that moves between physical ports after a firmware update, a card swap, or a BIOS change produces a node whose management traffic is on the wrong wire. Pinning ties the name to the MAC, so the mapping survives.

[disk-setup]

Choose the filesystem, then the disks, then the per-filesystem options.

[disk-setup]
filesystem = "zfs"
filter.ID_MODEL = "SAMSUNG_MZ*"
filter.ID_SERIAL = "*"
filter-match = "all"

[disk-setup.zfs]
raid = "raid10"
ashift = 12
compress = "zstd"
checksum = "sha256"
arc-max = 16384
hdsize = 400

filesystem takes ext4, xfs, zfs or btrfs — the last being a technology preview and not a production choice.

Disks are selected by disk-list or by filter, never both. filter-match takes any (the default) or all, deciding whether the filters combine with OR or AND.

For ZFS, raid accepts raid0, raid1, raid10, raidz-1, raidz-2 and raidz-3. ashift = 12 is 4K sectors and is correct for essentially every current drive. arc-max deserves a value on every hypervisor, in MiB: unbounded ARC competes with guest memory, and the answer file is the earliest and cheapest place to bound it.

For ext4 and xfs the corresponding section is [disk-setup.lvm] with hdsize, swapsize, maxroot, maxvz and minfree — the same knobs the interactive installer exposes under Advanced Options.

Filters that survive a hardware change

A disk-list of ["sda", "sdb"] is a bet that kernel enumeration order is stable. It is not: add an HBA, change a backplane, or upgrade the kernel, and sda can become the data disk you did not want to overwrite.

Filters match UDEV properties instead. Find out what your hardware actually reports:

Read-only / Safewhat does this hardware report?
proxmox-auto-install-assistant device-info -t disk
proxmox-auto-install-assistant device-info -t network

Useful disk properties are DEVNAME, ID_SERIAL_SHORT, ID_WWN, ID_MODEL and ID_SERIAL. For network interfaces: ID_NET_NAME, ID_NET_NAME_MAC, ID_VENDOR_FROM_DATABASE and ID_MODEL_FROM_DATABASE.

Filters support glob wildcards: ? for one character, * for any run, [abc] and [0-9] for classes, [!a] for negation.

Then test the expression before you trust it:

Read-only / Safedoes the filter select what you think?
proxmox-auto-install-assistant device-match disk ID_MODEL='SAMSUNG_MZ*'

Getting the answer file to the installer

Three fetch modes, baked into the ISO at preparation time.

--fetch-from iso

The answer file is embedded in the ISO. Simplest, and correct when the answer file is genuinely per-node or per-model.

Configuration changeembed the answer file in the ISO
proxmox-auto-install-assistant validate-answer answer.toml

proxmox-auto-install-assistant prepare-iso \
proxmox-ve_9.2-1.iso \
--fetch-from iso \
--answer-file answer.toml \
--output pve-auto-rack1.iso

The cost is that the credentials are inside the ISO. Treat a prepared ISO as a secret-bearing artefact — inspect-iso will show what it carries, and --show-sensitive will show the values.

--fetch-from partition

The ISO is generic; the answer file lives on a separate FAT partition labelled PROXMOX-AIS. One ISO, many answer files, no server.

Configuration changeprepare a generic ISO that looks for a labelled partition
proxmox-auto-install-assistant prepare-iso \
proxmox-ve_9.2-1.iso \
--fetch-from partition \
--output pve-auto-generic.iso
Destructiveprepare the answer USB stick
lsblk -o NAME,SIZE,MODEL,SERIAL

USB_PART=/dev/sdz1

mkfs.vfat "$USB_PART"
fatlabel "$USB_PART" 'PROXMOX-AIS'
mkdir -p /mnt/ais
mount "$USB_PART" /mnt/ais
cp answer.toml /mnt/ais/answer.toml
sync
umount /mnt/ais

The file must be named exactly answer.toml. A custom partition label is possible with --partition-label since PVE 8.3-1.

--fetch-from http

The installer asks a server for its answer file, and the server decides what to send based on who is asking. This is the fleet mode.

Configuration changeprepare an ISO that fetches over HTTPS
proxmox-auto-install-assistant prepare-iso \
proxmox-ve_9.2-1.iso \
--fetch-from http \
--url 'https://provisioning.example.com/answer' \
--cert-fingerprint '04:42:97:27:F6:29:2F:9F:3D:7F:13:11:C8:E2:F5:5F:84:03:95:D9:F5:14:72:7C:9E:90:47:03:D2:96:2B:EC' \
--answer-auth-token 'REPLACE_ME-provisioning-token' \
--output pve-auto-http.iso

The URL and fingerprint do not have to come from the ISO. The installer will also take them from DHCP option 250 (URL) and option 251 (fingerprint), or from DNS TXT records at proxmox-auto-installer.{search-domain} and proxmox-auto-installer-cert-fingerprint.{search-domain}. That makes one truly generic ISO possible, with the environment supplying the rest.

The installer POSTs machine identification to the server — the same data system-info prints — so the server can return an answer file matched to this specific machine. Run system-info on a node you already have to see the shape of what your server will receive.

For PXE, --pxe and --pxe-loader ipxe write a boot tree to --output instead of an ISO.

First-boot hooks

An installed node is not a configured node. [first-boot] runs an executable the first time the system comes up.

[first-boot]
source = "from-iso"
ordering = "fully-up"
Configuration changeembed a first-boot script
proxmox-auto-install-assistant prepare-iso \
proxmox-ve_9.2-1.iso \
--fetch-from iso \
--answer-file answer.toml \
--on-first-boot first-boot.sh \
--output pve-auto-rack1.iso

ordering decides when it runs: before-network, network-online, or fully-up (the default). Anything that needs to reach a repository or a configuration-management server needs at least network-online.

Keep the hook small. Its job is to make the node reachable by your real configuration management — install the agent, register with it, join it to the right group — not to be your configuration management. A five-hundred-line first-boot script is a second, undebuggable provisioning system that only runs once and logs nowhere convenient.

Common mistakes

  • disk-list with kernel names. sda is not stable across an HBA change or a kernel upgrade. Filter on ID_WWN or ID_SERIAL.
  • A filter never tested with device-match. The installer consumes every disk it selects, without a confirmation step.
  • root-password in plaintext. Use root-password-hashed, and treat even that as a secret.
  • reboot-on-error = true while developing. The node loops and shows you nothing.
  • A prepared ISO stored like an ordinary artefact. It contains credentials; inspect-iso --show-sensitive proves it.
  • HTTP mode without --cert-fingerprint. The installer will take its entire configuration, including the root credential, from whatever answers.
  • A first-boot script that is the provisioning system. It runs once, logs awkwardly, and cannot be re-run. Bootstrap into real configuration management instead.
  • No post-install verification. Reproducible is not the same as correct.

Key takeaways

  • The answer file is the procedure. Without it the build lives in somebody’s memory and the rebuild diverges.
  • [global], [network], [disk-setup] are required; fqdn.source = "from-dhcp" is what makes one file build a whole rack.
  • Select disks and NICs by UDEV property, and prove the filter with device-match before it touches hardware.
  • Three fetch modes: iso for per-node, partition for one generic ISO plus a labelled stick, http for a fleet with a server that answers.
  • HTTP mode needs the certificate fingerprint, from the ISO, DHCP options 250/251, or DNS TXT records.
  • Interface name pinning ties a name to a MAC, so management traffic stays on the wire you chose.
  • First-boot hooks bootstrap into configuration management; they are not configuration management.
  • Verify after the build. Automation makes divergence faster, not impossible.

Knowledge check

Knowledge check · 4 questions

  1. Q1. You are building twelve identical nodes and want one answer file for all of them. Which setting removes the need for a per-node file?

  2. Q2. Why is filter.ID_WWN preferable to disk-list = ["sda", "sdb"] in an answer file? Select all that apply.

  3. Q3. Running proxmox-auto-install-assistant validate-answer successfully means the answer file will produce the node you intended.

  4. Q4. An ISO is prepared with --fetch-from http and a URL, but without --cert-fingerprint. What is the consequence?

Passing score: 75%. Answers are checked in this browser.