Wheelhouse docs

Answer-file keys#

wheelhouse-install --answers FILE installs a machine without a keyboard. The file is KEY=VALUE lines; anything that is not one of the keys below is ignored, and so is any line that does not match ^[A-Z_]+=. It is also the format a cloud-config seed is converted into before the installer ever sees it, so this table is the ground truth for both.

Re-derive the accepted list:

bash
grep '^ANSWER_KEYS=' packaging/wheelhouse-install
answers.conf
DISK=sda
HOSTNAME=edge
TIMEZONE=Europe/Berlin
WAN_IFACE=eth0
WAN_ADDRESSING=dhcp
LAN_IFACE=eth1
LAN_ADDRESS=192.0.2.1/24
LAN_DHCP=yes
FIREWALL=yes
NAT=yes
ADMIN_PASSWORD=…
CONSOLE_PASSWORD=…
REBOOT=yes
bash
sudo wheelhouse-install --answers answers.conf

Every key#

KeyDefaultMeaning
DISK(none)Required. The disk to install to, named without /dev/sda, nvme0n1. auto means the one disk that is not the boot medium; with none or more than one it refuses rather than erasing the wrong machine.
OVERWRITEnoyes allows a reinstall over a disk that already holds a system. Without it, a seeded install that finds one leaves the disk alone and says so.
HOSTNAMEwheelhouseset system host-name.
TIMEZONEthe live system's, else UTCset system time-zone.
WAN_IFACE(empty)The uplink interface. Empty skips the WAN entirely — no addressing, no NAT, no firewall.
WAN_ADDRESSINGdhcpdhcp or static.
WAN_ADDRESS(empty)Static only: the address with its prefix length.
WAN_GATEWAY(empty)Static only: the next hop for the default route.
WAN_DNS(empty)Static only: resolvers, comma separated. Each becomes a set system name-server. Empty on a DHCP WAN leaves resolvers to the lease.
LAN_IFACE(empty)The inside interface. Empty skips the LAN entirely.
LAN_ADDRESS192.168.1.1/24The router's address on the LAN, with its prefix length. The network and the router's own address are derived from it.
LAN_DHCPyesHand out addresses on the LAN and answer DNS for it.
FIREWALLyesWrite the default-deny ruleset. It follows the WAN, not the LAN: a box with a WAN and no LAN is the one that most needs a closed front door.
NATyesSource-NAT the LAN out of the WAN.
MGMT_SOURCE(empty)An address or prefix admitted through the input filter to the router itself — the web UI on 8443, and SSH. On a box with no LAN it is the difference between an install you can reach afterwards and one you can only reach at the console.
ADMIN_PASSWORD(none)Required. The web UI admin password, set through the agent.
CONSOLE_PASSWORD(none)Required. The password for both console accounts.
VYOS_PASSWORD(none)The former name of CONSOLE_PASSWORD, still accepted so seeds written against it keep working. CONSOLE_PASSWORD wins if a file carries both.
REBOOTyesReboot after a successful install.
POWEROFFnoPower off instead. Checked before REBOOT.
KEEP_PREVIOUSnoPassed to the base installer as WH_KEEP_PREVIOUS: keep the previous installation on the disk.
CONFIG_COMMANDS(empty)A file of set lines to apply — what the OPNsense importer writes. A path, or an https:// URL. Refused unless it holds at least one set or delete line.
CONFIG(empty)A whole configuration file for the router to load and validate itself. A path, or an https:// URL.
SSH_KEYS(empty)A file of public keys, one per line, installed on both console accounts. A path, or an https:// URL.
ON_ERRORsee belowWhat an unattended install does when it cannot finish: halt, poweroff, menu (or interactive), shell, or exit.

DISK is required, and ADMIN_PASSWORD and CONSOLE_PASSWORD are required together. Missing any of them is a one-line refusal on stderr and exit 1.

The two console accounts#

The installer sets up two, with the same password:

AccountRole
wheelhouseThe one to log in with. It is the product's own account, and it is what every screen and every banner names.
vyosThe base system's own account, whose login and sudo handling the platform depends on. It is kept, given the same password, and documented as the recovery way in rather than as the way in.

One password for both, so an operator has one thing to remember and cannot be locked out of the recovery account.

ON_ERROR#

ValueEffect
haltStop the machine. The default for a seeded install — a machine with nobody watching should stop rather than loop.
poweroffPower it off.
menu or interactiveFall back to the interactive installer.
shellDrop to a login shell, printing how to run the installer again.
exitExit 1. The default for a hand-run --answers install — a person at a keyboard should get their shell back, not a halted box.

Combined with the once-per-boot rule, this is what stops a failing seed from erasing the same disk on every restart. See Where the installer looks for a seed.

Payloads: file, or HTTPS URL#

CONFIG_COMMANDS, CONFIG and SSH_KEYS each take a local path or a URL. Plain http:// and ftp:// are refused unless the machine was booted with wheelhouse.insecure=1 on the kernel command line:

refusing to fetch config.commands over plain http: use https, or boot with
wheelhouse.insecure=1 if this network is trusted

That switch is deliberately not an answer-file key. A seed that arrived over the network must not be able to declare that the network it arrived over is trustworthy; only the person at the keyboard, on the kernel command line, can say that.

CONFIG_COMMANDS and CONFIG are applied to the live configuration after the passwords are set, so a restored firewall can be as closed as you like without locking the installer out of the agent. A loaded whole configuration replaces everything, including the two console accounts — so the installer writes them again afterwards, because a box whose login screens name an account the configuration does not have is the worst kind of wrong.

Seeing the commands before running them#

--commands prints the set lines a set of answers produces and stops. It changes nothing, needs no root, and does not need to be running from a live image:

bash
wheelhouse-install --answers answers.conf --commands

That is how to check a seed before booting a machine with it.

What the installer does with a WAN and no firewall#

Unchecking the firewall with a WAN present is a deliberate answer, and the summary screen says what it costs in those words: REACHABLE - port 8443 answers the internet. With the firewall on, the same line reads blocked, or blocked, except from <MGMT_SOURCE>.

Where it logs#

/var/log/wheelhouse-install.log, on the live system. Under --answers every log line also goes to stderr, so a seeded install narrates itself on the console.

See also#

Checked against#

packaging/wheelhouse-install (ANSWER_KEYS, read_answers, resolve_disk, disk_has_install, fetch_payload, apply_config, build_commands, wan_ui_summary), packaging/seed-to-answers.py, packaging/README.md, docs/unattended-install.md.

Updated 2026-09-02 installer unattended answers