Wheelhouse docs

wheelhouse-seed.py#

Builds the small disk that installs a machine for you: an ISO 9660 image labelled CIDATA — cloud-init's NoCloud label — holding a user-data cloud-config and whatever it refers to. Attach it to the machine (a second disk on a virtual machine, a USB stick on hardware), boot the Wheelhouse image, and the installer finds it.

bash
# from a cloud-config you wrote
tools/wheelhouse-seed.py --user-data seed.yaml --out seed.iso

# from the OPNsense router you are replacing, configuration and all
tools/wheelhouse-seed.py --opnsense config.xml --map lan=eth0,wan=eth1 \
    --admin-password-file admin.pw --console-password-file console.pw \
    --out seed.iso

This tool does not ship on the router. It runs on your workstation.

Options#

Exactly one of --user-data or --opnsense is required.

OptionDefaultMeaning
--user-data FILEA cloud-config you wrote yourself. Copied verbatim.
--opnsense FILEAn OPNsense config.xml. The importer runs, and the seed is written around its output.
--out FILEseed.isoThe image to write.
--out-dir DIRWrite the seed files into a directory instead of building an image.
--label NAMECIDATAThe filesystem label. WHEELHOUSE also works.
--mapInterface mapping for the import: lan=eth0,wan=eth1.
--diskautoThe disk to install to. auto means the one disk that is not the boot medium.
--hostnamefrom the importHost name for the new machine.
--timezoneUTCOverridden by the import when the source configuration names one.
--admin-passwordThe web UI password. Prefer the file form.
--admin-password-file
--console-passwordThe console password. Prefer the file form.
--console-password-file
--ssh-key FILEA public key file for the console accounts. Repeatable.
--include-sourceoffPut the config.xml on the seed as opnsense-config.xml.
--no-rebootoffStop after installing instead of rebooting.
--on-errorhalthalt, poweroff, menu or shell.

With --opnsense and no password given any way, and a terminal attached, it prompts twice for each and refuses if the two entries differ. With no terminal, it refuses outright rather than building a seed with an empty password.

What lands on the seed#

FileAlwaysWhat
user-datayesThe cloud-config. Either yours, verbatim, or generated from the import.
meta-datayesinstance-id: wheelhouse-<pid>. cloud-init wants the file to exist beside user-data, even empty.
config.commandswith --opnsenseThe set lines the import produced.
import-report.mdwith --opnsenseThe importer's report, so the record of what did not come across travels with the machine.
authorized_keyswith --ssh-keyEvery key, comments and blank lines stripped. Also appended to user-data as ssh_authorized_keys.
opnsense-config.xmlwith --include-sourceThe original file, so the installed machine can check itself against it months later.

With --user-data, three files beside your cloud-config are picked up automatically if they exist and copied onto the seed: config.commands, config.boot and authorized_keys.

What the generated cloud-config looks like#

yaml
#cloud-config
# Wheelhouse unattended install. Written by wheelhouse-seed.py.
wheelhouse:
  disk: auto
  hostname: edge
  timezone: Europe/Berlin
  wan:
    interface: eth1
    addressing: dhcp
  lan:
    interface: eth0
    address: 192.0.2.1/24
  firewall: false
  passwords:
    admin: 'the web UI password'
    console: 'the console password'
  config:
    commands: config.commands
  reboot: true
  on_error: halt

The host name and time zone are taken from the imported configuration's own set system host-name and set system time-zone lines unless you override them, so the new box keeps the old box's identity.

An image, or a directory#

FormNeedsUse it when
--out seed.isoxorrisofs, genisoimage or mkisofs — whichever is on the machineYou are attaching a virtual disk, or writing a stick with dd.
--out-dir /media/sticknothingYou formatted and labelled a stick yourself. Copy the files to its root and label it CIDATA or WHEELHOUSE.
bash
dd if=seed.iso of=/dev/sdX bs=4M status=progress && sync

The tool prints which image tool it used and the resulting size.

The seed is a credential#

Public keys are the way to keep a password from living on a stick longer than it has to: --ssh-key ~/.ssh/id_ed25519.pub puts one on both console accounts, and the machine is reachable the moment it boots.

Checking a seed before you use it#

bash
python3 packaging/seed-to-answers.py user-data /tmp/work   # what it converts to
sudo wheelhouse-install --answers /tmp/work/answers.conf --commands   # what it would run

See also#

Checked against#

tools/wheelhouse-seed.py (main, TEMPLATE, read_password, run_importer, make_image), tools/tests/test_seed.py, packaging/seed-to-answers.py, packaging/wheelhouse-autoinstall, docs/unattended-install.md.

Updated 2026-09-02 tools seed unattended installer