| pick your distro, get ZFS on root
kldload — your platform, your way, free
Source
← Back to Overview

Environment Variable Reference

Every aspect of the kldload build and install is controlled by environment variables. This is the complete, authoritative reference for every variable the installer reads. Set them in kldload.env (sourced automatically at build time), in an answers.env file on a seed disk (read at install time), or pass them directly on the command line. No variable is required — sensible defaults exist for everything.

The design principle: Environment variables are the universal interface. Every shell reads them. Every CI system sets them. Every container runtime accepts them. Every cron job can export them. You do not need a parser, a library, a schema validator, or a YAML-to-JSON converter. export KLDLOAD_DISTRO=debian works in bash, zsh, fish, and every CI pipeline on earth. The config format is the shell itself.

Environment variables were chosen over YAML, TOML, JSON, or any config file format deliberately. I tried YAML for the first prototype. Then I tried TOML. Then I realized that every time I wanted to pass a config value from a CI pipeline, from a Terraform provisioner, from a Packer variable, from a Docker --env flag, or from a shell script, I had to serialize the value into the config format and then deserialize it on the other end. Environment variables skip both steps. They are the native interface for every tool in the Unix ecosystem.

Two scopes exist and they never mix: build-time variables control what goes into the ISO (PROFILE, DISTRO, ARCH). Install-time variables control what happens on the target machine (KLDLOAD_HOSTNAME, KLDLOAD_DISK, KLDLOAD_PASSWORD). Build-time vars go in kldload.env. Install-time vars go in the answer file or the web UI. The naming convention enforces this: build-time vars have no prefix. Install-time vars start with KLDLOAD_. If you see the prefix, it runs on the target. If you do not, it runs on the build machine.

How environment variables work

kldload uses environment variables at two distinct stages: build time (when creating the ISO) and install time (when installing to the target disk). The two stages use different files, different variable names, and different mechanisms. Understanding the boundary between them is essential.

Build-time variables

Stored in kldload.env at the root of the repo. Sourced by deploy.sh before any build step runs. Control what distro, profile, and architecture the ISO targets. Also hold credentials for Proxmox deployment and RHEL subscriptions. Never baked into the ISO. Never reach the target machine. kldload.env is gitignored.

The blueprint for the factory. It tells the factory what to build, but the blueprint itself does not ship with the product.

Install-time variables

Stored in answers.env on a FAT32 seed USB labeled KLDLOAD-SEED. Read by the installer when the live ISO boots. Control hostname, disk, users, networking, ZFS topology, WireGuard, export format, and every other aspect of the installed system. All prefixed with KLDLOAD_. Can also be set via the web UI or WebSocket API.

The order form. It tells the installer what the customer wants. Different order forms produce different machines from the same ISO.

The answers file format

The answers file is a plain text file containing KEY=value pairs, one per line. Comments start with #. Blank lines are ignored. Values can be quoted with single or double quotes (the installer strips them). Every KLDLOAD_* variable found in the file is exported into the installer's environment.

# This is a comment — ignored by the parser
KLDLOAD_DISTRO=debian
KLDLOAD_HOSTNAME=web-prod-01
KLDLOAD_PASSWORD="correct horse battery staple"
KLDLOAD_SSH_PUBKEY='ssh-ed25519 AAAA... ops@infra'

# Blank lines are fine
KLDLOAD_TIMEZONE=America/Vancouver

Precedence rules

When the same variable is set in multiple places, the installer follows a strict precedence order. Higher-priority sources override lower-priority ones. This lets you set broad defaults in an answers file and override specific values on the command line or via the web UI.

Resolution order (highest to lowest priority)

1. Command-line environmentKLDLOAD_DISK=/dev/nvme0n1 kldload-install-target --config answers.env wins over anything in the file.
2. Web UI / WebSocket API — values submitted through the web interface override file defaults.
3. Answers fileanswers.env on the seed disk or passed via --config.
4. Built-in defaults — hardcoded in the installer. Every variable has one. You only set what you want to change.

Same as CSS specificity: inline styles beat class selectors beat element selectors beat browser defaults.

How the installer reads variables

The installer (kldload-install-target) has two modes: guided (interactive web UI) and config (unattended, reads an answers file). In guided mode, the web UI collects values and exports them before calling the installer backend. In config mode, the installer calls k_answers_load_env_file() which parses the file line by line, validates variable names against ^[A-Za-z_][A-Za-z0-9_]*$, strips quotes, and exports each key-value pair. After loading, it calls k_save_effective_config() to write the resolved configuration to /var/log/installer/effective-config.env — with passwords redacted — for audit and debugging.

The parser is deliberately simple. It does not support variable interpolation, multi-line values, heredocs, arrays, or nested structures. A line is either a comment, blank, or a KEY=value assignment. That is the entire grammar. This means the answers file is trivially machine-generated — any language that can write KEY=value\n can produce one. No YAML indentation bugs. No JSON trailing comma errors. No TOML datetime parsing differences between implementations.

The effective config file is your audit trail. After every install, /var/log/installer/effective-config.env contains the exact values that were used. Passwords are replaced with __REDACTED__. Everything else is preserved verbatim. If something went wrong, you can see exactly what the installer saw. If you need to reproduce the install on another machine, copy the effective config, fill in the passwords, and run it again.

# Three ways to run an unattended install:

# 1. Seed disk — zero-touch, physical hardware
#    Format USB as FAT32, label KLDLOAD-SEED, drop answers.env on it
#    Boot with ISO + seed USB — installer finds it automatically

# 2. Command line — VMs, CI, scripted deploys
kldload-install-target --config /path/to/answers.env

# 3. Environment variables — override anything
KLDLOAD_DISK=/dev/nvme0n1 KLDLOAD_HOSTNAME=override-01 \
  kldload-install-target --config answers.env

Core variables

These are the fundamental variables that define what gets installed and how it is identified. Most installs only need to set a handful of these — the defaults handle everything else.

VariableDefaultDescription
KLDLOAD_DISTROcentosTarget distribution to install. Determines which bootstrap method and package manager is used.
KLDLOAD_PROFILEserverInstall profile. Controls which packages, services, and system files are installed.
KLDLOAD_HOSTNAMEkldload-nodeSystem hostname. Written to /etc/hostname and set via hostnamectl.
KLDLOAD_TIMEZONEUTCSystem timezone. Any valid TZ database name. Symlinks /etc/localtime.
KLDLOAD_LOCALEen_US.UTF-8System locale. Generated during bootstrap and set as default in /etc/locale.conf.
KLDLOAD_KEYBOARD_LAYOUTusXKB keyboard layout code. Written to /etc/vconsole.conf and X11 config.
KLDLOAD_KEYBOARD_VARIANTemptyXKB keyboard variant. For layouts with multiple variants (e.g., dvorak for us).

KLDLOAD_DISTRO — target distribution

Valid values and what they mean

centos — CentOS Stream 9. DNF-based. Default. Uses RPM darksite for offline install.
debian — Debian 13 (Trixie). APT-based. Uses Debian darksite on port 3142.
ubuntu — Ubuntu 24.04 (Noble Numbat). APT-based. Uses Ubuntu darksite on port 3143. Requires universe component for ZFS.
fedora — Fedora 41. DNF-based. Uses RPM darksite.
rhel — Red Hat Enterprise Linux 9. DNF-based. Requires RHEL_ACTIVATION_KEY and RHEL_ORG_ID at build time.
rocky — Rocky Linux 9. DNF-based. Uses RPM darksite. Drop-in RHEL replacement.
arch — Arch Linux (rolling). Pacstrap-based. Requires internet — no darksite (rolling release).
alpine — Alpine Linux. APK-based. Core profile only. Minimal, musl-based.
freebsd — FreeBSD. Uses bsdinstall bootstrap. Experimental.
openbsd — OpenBSD. Experimental.

The distro determines which bootstrap function runs, which package manager is used, and which darksite (if any) provides offline packages.

The distro choice is the first fork in the installer. CentOS, Rocky, RHEL, and Fedora all take the DNF path — dnf --installroot into the target. Debian and Ubuntu take the debootstrap path. Arch takes the pacstrap path. Alpine takes the apk path. FreeBSD and OpenBSD take the BSD bootstrap path. After the fork, everything converges again: ZFS pool creation, bootloader installation, user creation, networking, WireGuard, and firstboot all run the same code regardless of distro. The distro only matters for the initial package installation and the package names.

This is why you can switch distros with a single variable change. The same ISO, the same installer, the same answer file — just change KLDLOAD_DISTRO=debian to KLDLOAD_DISTRO=rocky and you get Rocky Linux with the exact same ZFS layout, the exact same WireGuard config, the exact same user setup. The distro is interchangeable. The platform is constant.

KLDLOAD_PROFILE — install profile

Valid values and what they install

server — Headless SSH server. Installs openssh-server, chrony, wireguard-tools, sanoid, podman, tmux, htop, btop, fzf, bat, eza, ripgrep, nftables, tcpdump, and the kldload management tools (kstatus, kbackup, krollback, krestore). Python3 with websockets for the web UI. No desktop environment.

desktop — Full GNOME desktop. Everything in server, plus GNOME Shell, GDM, Nautilus, Firefox (or Epiphany on Ubuntu), NetworkManager, and graphical tools. The web UI still runs — accessible at :8080 from the desktop browser.

core — Bare minimum. ZFS on root, openssh-server, sudo, curl, vim, iproute2, nftables, wireguard-tools. No kldload management tools, no web UI, no sanoid, no darksites. Stock distro with ZFS. For people who want the ZFS foundation and nothing else.

ai — AI/ML workstation. Everything in server, plus Python pip, cmake, gcc, make, git, cloud-init, qemu-guest-agent. Designed for GPU workloads. Available on Arch.

client — Minimal client. SSH, sudo, curl, NetworkManager, WireGuard. For machines that join a cluster but run minimal services.

KLDLOAD_HOSTNAME — system hostname

Any valid hostname: letters, digits, hyphens. No dots (it is a hostname, not an FQDN). Written to /etc/hostname, set via hostnamectl set-hostname in chroot. The default kldload-node is fine for testing but should be changed for production. For fleet deployments, use a naming convention: web-prod-01, db-staging-03, k8s-worker-12.

KLDLOAD_TIMEZONE — system timezone

Any valid TZ database name. Common values: UTC, America/New_York, America/Vancouver, America/Chicago, America/Los_Angeles, Europe/London, Europe/Berlin, Asia/Tokyo, Australia/Sydney. The installer symlinks /etc/localtime to the appropriate zoneinfo file. Servers should use UTC. Desktops can use local time.

KLDLOAD_LOCALE and KLDLOAD_KEYBOARD_LAYOUT

KLDLOAD_LOCALE is a locale string like en_US.UTF-8, en_GB.UTF-8, de_DE.UTF-8, ja_JP.UTF-8, or fr_FR.UTF-8. The locale is generated during bootstrap and set as the system default. KLDLOAD_KEYBOARD_LAYOUT is an XKB layout code: us, gb, de, fr, es, jp, ru, etc. The optional KLDLOAD_KEYBOARD_VARIANT selects a sub-layout: dvorak, colemak, mac, intl, etc.

Disk and storage

Storage configuration is the most consequential set of variables. The ZFS pool topology is permanent — you cannot change it after creation without destroying and recreating the pool. Choose carefully. When in doubt, start with single and add mirrors later.

VariableDefaultDescription
KLDLOAD_DISKauto-detectedPrimary target block device. The installer wipes this disk entirely. Auto-detected if only one non-USB, non-optical disk exists.
KLDLOAD_STORAGE_MODEzfsStorage mode. Always zfs. This is the only supported value.
KLDLOAD_ZFS_TOPOLOGYsingleZFS vdev layout for the root pool. Determines redundancy and performance characteristics.
KLDLOAD_ZFS_DATA_DISKSemptyAdditional disks for multi-disk topologies. Space-separated list of block device paths.
KLDLOAD_ZFS_SPECIAL_DISKSemptyNVMe devices for metadata/small-block special vdev acceleration. Space-separated.
KLDLOAD_ZFS_ENCRYPT0Enable ZFS native encryption (AES-256-GCM). Set to 1 to enable. Prompts for passphrase at every boot.
KLDLOAD_ZFS_PASSPHRASEpromptedEncryption passphrase when KLDLOAD_ZFS_ENCRYPT=1. If not set, the installer prompts interactively.
KLDLOAD_ENABLE_ZFS1Always 1. ZFS is non-optional. This variable exists for internal consistency.
KLDLOAD_FORCE_WIPE0Set to 1 to skip the "are you sure" confirmation before wiping the disk. Required for unattended installs.

KLDLOAD_DISK — target disk

The block device path to install to. Examples: /dev/sda, /dev/nvme0n1, /dev/vda (KVM virtio). The entire disk is wiped and repartitioned: a 512 MB EFI System Partition (FAT32) and the remainder as a ZFS pool. If only one candidate disk exists (excluding USB and optical drives), the installer auto-detects it. If multiple disks exist and KLDLOAD_DISK is not set, the installer prompts (guided mode) or fails (config mode).

Disk auto-detection filters aggressively. It excludes: loop devices (/dev/loop*), RAM disks, optical drives (/dev/sr*), the USB boot media itself (detected by mount point), and anything smaller than 8 GB. What remains is the candidate list. If exactly one disk survives the filter, it is used automatically. If zero survive, the installer errors. If multiple survive and no KLDLOAD_DISK is set, you get a prompt or a failure depending on mode.

For unattended installs, always set KLDLOAD_DISK explicitly. Auto-detection is convenient for testing but dangerous in production. A machine that grows a second disk (hot-plugged SAS, USB backup drive left plugged in) will suddenly fail auto-detection. Explicit is better than implicit.

KLDLOAD_ZFS_TOPOLOGY — pool vdev layout

Valid topologies and their trade-offs

single — One disk, no redundancy. The disk IS the pool. Fastest for single-disk machines. One disk failure = total data loss. Good for VMs, test environments, CI runners where the data is ephemeral.

mirror — Two disks, mirrored. Every write goes to both disks. Survives one disk failure. Reads can come from either disk (2x read throughput). Requires KLDLOAD_ZFS_DATA_DISKS with one additional disk. The gold standard for production servers.

raidz1 — Three or more disks, single parity. Survives one disk failure. Usable capacity = (N-1) disks. Good for storage-heavy workloads where you need capacity over raw performance. Requires KLDLOAD_ZFS_DATA_DISKS with two or more additional disks.

mirror-stripe — Four disks, RAID10 equivalent. Two mirrored pairs striped together. Survives one disk failure per mirror pair. Best performance and redundancy for databases. Requires KLDLOAD_ZFS_DATA_DISKS with three additional disks.

single = no backup. mirror = two copies. raidz1 = one parity drive. mirror-stripe = two mirrored pairs for speed + safety.

KLDLOAD_ZFS_DATA_DISKS — additional disks

Space-separated list of block device paths for multi-disk topologies. These disks are added to the pool alongside KLDLOAD_DISK. For a mirror, provide one disk. For raidz1, provide two or more. For mirror-stripe, provide three.

# Mirror: /dev/sda (primary) + /dev/sdb (mirror)
KLDLOAD_DISK=/dev/sda
KLDLOAD_ZFS_TOPOLOGY=mirror
KLDLOAD_ZFS_DATA_DISKS=/dev/sdb

# RAIDZ1: /dev/sda + /dev/sdb + /dev/sdc (3 disks, 1 parity)
KLDLOAD_DISK=/dev/sda
KLDLOAD_ZFS_TOPOLOGY=raidz1
KLDLOAD_ZFS_DATA_DISKS="/dev/sdb /dev/sdc"

# RAID10: /dev/sda+sdb mirror, /dev/sdc+sdd mirror, striped
KLDLOAD_DISK=/dev/sda
KLDLOAD_ZFS_TOPOLOGY=mirror-stripe
KLDLOAD_ZFS_DATA_DISKS="/dev/sdb /dev/sdc /dev/sdd"

KLDLOAD_ZFS_SPECIAL_DISKS — metadata acceleration

Optional NVMe devices dedicated to metadata and small-block I/O. ZFS stores file metadata, dedup tables, and small files (under special_small_blocks) on these devices instead of the main pool disks. Dramatically accelerates ls, find, stat, and random small-read workloads when your data disks are spinning rust. Should be mirrored for redundancy (provide two NVMe paths). Loss of an unmirrored special vdev = pool loss.

KLDLOAD_ZFS_ENCRYPT — native encryption

Set to 1 to enable ZFS native encryption using AES-256-GCM. The root pool is created with encryption=aes-256-gcm and keylocation=prompt. Every boot requires entering the passphrase before the pool can mount. The passphrase can be supplied via KLDLOAD_ZFS_PASSPHRASE in the answers file for unattended encrypted installs. After install, change the key or key source as needed.

ZFS native encryption encrypts at the dataset level, not the disk level. This means: ZFS can still read pool metadata (it knows how many datasets exist, their names, and their properties) but cannot read file contents without the key. Scrubs still work on encrypted pools. Snapshots inherit encryption from their parent. Send/receive preserves encryption — you can replicate encrypted datasets to an untrusted remote without ever exposing the key. The remote stores ciphertext it cannot read.

The trade-off: every boot requires the passphrase. For servers in a datacenter, this means someone has to type the passphrase into the console (or over IPMI) after every reboot. For laptops, it is a natural part of the boot flow. For unattended servers, consider whether full-disk encryption is actually needed for your threat model, or whether encrypting specific datasets (e.g., rpool/home) with key files stored on a separate secure medium is a better approach.

Networking

Network configuration for the installed system. The default is DHCP, which works out of the box on most networks. Static IP configuration requires setting all four static fields: IP, prefix, gateway, and DNS.

VariableDefaultDescription
KLDLOAD_NET_METHODdhcpNetwork configuration method. dhcp for automatic, static for manual IP assignment.
KLDLOAD_NET_IFACEauto-detectedNetwork interface for static configuration. Examples: eth0, ens18, enp0s3. Auto-detected as the first non-loopback interface if not set.
KLDLOAD_NET_IPnoneStatic IP address. Required when KLDLOAD_NET_METHOD=static. Example: 10.0.1.50.
KLDLOAD_NET_PREFIX24CIDR prefix length. 24 = 255.255.255.0 (254 hosts). 16 = 255.255.0.0 (65534 hosts).
KLDLOAD_NET_GWnoneDefault gateway IP. Required when KLDLOAD_NET_METHOD=static. Example: 10.0.1.1.
KLDLOAD_NET_DNSnoneDNS servers. Comma-separated. Example: 1.1.1.1,8.8.8.8 or 10.0.1.1 for a local resolver.

DHCP vs static — when to use which

DHCP is the right default. It works on home networks, cloud VMs (Proxmox, KVM, AWS, GCE), and most corporate LANs. The installed system gets an IP, gateway, and DNS from whatever DHCP server is on the network. No configuration needed.

Static is for production servers that need predictable addresses: database servers, load balancers, DNS servers, monitoring stacks, WireGuard endpoints. Anything that other services connect TO should have a static IP. Anything that connects OUT can use DHCP.

# DHCP — the default, works everywhere
KLDLOAD_NET_METHOD=dhcp

# Static IP for a production database server
KLDLOAD_NET_METHOD=static
KLDLOAD_NET_IFACE=ens18
KLDLOAD_NET_IP=10.0.1.50
KLDLOAD_NET_PREFIX=24
KLDLOAD_NET_GW=10.0.1.1
KLDLOAD_NET_DNS=10.0.1.1,1.1.1.1

# Static with a /16 subnet — large flat network
KLDLOAD_NET_METHOD=static
KLDLOAD_NET_IFACE=eth0
KLDLOAD_NET_IP=172.16.5.100
KLDLOAD_NET_PREFIX=16
KLDLOAD_NET_GW=172.16.0.1
KLDLOAD_NET_DNS=172.16.0.53

The interface name (KLDLOAD_NET_IFACE) is one of those things that trips people up. CentOS and RHEL use predictable names like ens18, enp0s3, eno1. Debian and Ubuntu sometimes use eth0 depending on the kernel and network namespace. VirtIO NICs in KVM show up as ens3 or enp0s18. Physical Intel NICs are often eno1 or enp3s0. If you do not know the interface name, leave it blank — the installer detects the first non-loopback interface. Or boot the live ISO first, run ip link, and note the name.

User and authentication

Every kldload install creates a non-root admin user with passwordless sudo. Root login via SSH is disabled by default. SSH public key authentication is the recommended method for production.

VariableDefaultDescription
KLDLOAD_USERNAMEadminNon-root admin user. Created with a home directory, bash shell, and membership in the sudo (or wheel) group with NOPASSWD.
KLDLOAD_PASSWORDpromptedPassword for the admin user. The web UI pre-fills kldload for testing. In unattended mode, this must be set explicitly.
KLDLOAD_ROOT_PASSWORDnoneRoot password. If unset, root account is locked (no password login). Root SSH is always disabled regardless of this setting.
KLDLOAD_SSH_PUBKEYnoneSSH public key for the admin user. Written to ~admin/.ssh/authorized_keys. Accepts any OpenSSH key format (ed25519, RSA, ECDSA).
KLDLOAD_ADMIN_SSH_PUBKEYnoneSSH public key for the root account. Written to /root/.ssh/authorized_keys. For emergency access only — root SSH password auth remains disabled.
KLDLOAD_GENERATE_SSH_KEY1Generate SSH host keys during install. Set to 0 if you supply your own host keys or want cloud-init to generate them on first boot.

Security model for user accounts

The admin user has full sudo NOPASSWD access. This is deliberate: the machine is configured at install time, not at login time. The admin user can do anything root can do, but is audited in /var/log/auth.log and journalctl -u sudo. Root SSH login is disabled in /etc/ssh/sshd_config (set PermitRootLogin no). Root password login at the console is only possible if KLDLOAD_ROOT_PASSWORD is explicitly set. SSH key authentication is always preferred over passwords. Set KLDLOAD_SSH_PUBKEY and disable password auth entirely in production.

# Minimal user config — password only
KLDLOAD_USERNAME=admin
KLDLOAD_PASSWORD=changeme

# Production user config — SSH key, no root password
KLDLOAD_USERNAME=sysadmin
KLDLOAD_PASSWORD='$ecure-P@ssphrase-2024'
KLDLOAD_SSH_PUBKEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBx... ops@infra"
KLDLOAD_ADMIN_SSH_PUBKEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFy... break-glass@vault"

# Lock everything down — SSH key only, root locked
KLDLOAD_USERNAME=deployer
KLDLOAD_PASSWORD='randomly-generated-32-char-passphrase'
KLDLOAD_SSH_PUBKEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICz... terraform@ci"
KLDLOAD_GENERATE_SSH_KEY=1

The password is stored hashed in /etc/shadow using the strongest hash the target distro supports (typically yescrypt on Debian/Ubuntu, SHA-512 on CentOS/Rocky). The plaintext password never touches the installed system's disk — it is hashed in memory during install and only the hash is written. The answers file on the seed USB does contain the plaintext, so treat the seed USB as a credential: wipe it after use, do not leave it plugged in, do not commit it to git.

For production fleets, generate a random password per machine (unique, never reused) and rely exclusively on SSH key authentication. The password exists only as a break-glass mechanism for console access. If you do not need console access (cloud VMs, headless servers with IPMI), you can still set a password — but the SSH key is what your automation uses day to day.

WireGuard

WireGuard is the encrypted mesh network layer. kldload installs the WireGuard kernel module and tools by default on every profile except core. The KLDLOAD_WIREGUARD variable controls whether a WireGuard interface is configured during install (not just installed). Four WireGuard interfaces (wg0 through wg3) are available for different network planes.

VariableDefaultDescription
KLDLOAD_WIREGUARD0Set to 1 to configure a WireGuard interface during install. The tools are always installed (server/desktop profiles); this controls whether a tunnel is actually set up.
KLDLOAD_ENABLE_WIREGUARD1Include WireGuard kernel module and userspace tools in the install. On by default for server and desktop profiles.
KLDLOAD_WG0_PORT51820Listen port for wg0 (management plane). Standard WireGuard port.
KLDLOAD_WG1_PORT51821Listen port for wg1 (data plane). Used for cluster-internal traffic.
KLDLOAD_WG2_PORT51822Listen port for wg2 (storage plane). Dedicated to ZFS replication and NFS/iSCSI.
KLDLOAD_WG3_PORT51823Listen port for wg3 (external plane). For site-to-site VPN and external access.
KLDLOAD_WIREGUARD_PRIVATE_KEYgeneratedWireGuard private key. If not set, a new keypair is generated during install. The private key is redacted in the effective config log.
KLDLOAD_WIREGUARD_PRESHARED_KEYnoneOptional preshared key for additional post-quantum security on a specific peer connection. Redacted in logs.

The four WireGuard planes

kldload separates network traffic into four isolated WireGuard tunnels. Each tunnel has its own interface, port, keys, and routing table. Traffic on one plane cannot reach another plane.

wg0 (management, :51820) — SSH, monitoring, Salt/Ansible, cluster control. Always-on for ops access.
wg1 (data, :51821) — Application traffic between cluster nodes. Kubernetes pod-to-pod, service mesh, inter-service communication.
wg2 (storage, :51822) — ZFS send/receive, NFS mounts, iSCSI targets. High-throughput, low-latency. Kept separate so storage traffic never competes with application traffic.
wg3 (external, :51823) — Site-to-site VPN. Connect remote offices, cloud VPCs, or road-warrior laptops to the cluster.

Four separate highways between the same cities. Management traffic takes Highway 0. Your application takes Highway 1. Storage replication takes Highway 2. The public on-ramp is Highway 3. They never merge.

Most WireGuard tutorials show one interface, one tunnel, one flat network. That works for a VPN. It does not work for infrastructure. When your ZFS replication is saturating the link, you do not want your SSH session to lag. When your Kubernetes pods are chattering, you do not want your monitoring metrics to drop. Separate planes solve this at the network layer — no QoS, no traffic shaping, no iptables marking. Just separate tunnels on separate ports with separate routing tables. The kernel handles isolation. You configure it once at install time.

You do not have to use all four. Most standalone servers use wg0 only (management access). Clusters use wg0 + wg1 (management + data). Storage-heavy setups add wg2. Multi-site deployments add wg3. Each plane is independent — configure only what you need.

# Enable WireGuard with custom ports
KLDLOAD_WIREGUARD=1
KLDLOAD_WG0_PORT=51820
KLDLOAD_WG1_PORT=51821

# Supply your own keypair (for reproducible deployments)
KLDLOAD_WIREGUARD=1
KLDLOAD_WIREGUARD_PRIVATE_KEY="yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk="
KLDLOAD_WIREGUARD_PRESHARED_KEY="FpCyhT+bZAVniNcJwB1fkBXG7jUxBPBRzUtvmWSJCZs="

Feature toggles

Boolean flags that enable or disable optional subsystems. Each toggle adds kernel modules, userspace tools, and configuration to the installed system. All modules are built and signed at image creation time — not installed after the fact.

VariableDefaultDescription
KLDLOAD_ENABLE_KVM0Install libvirt, QEMU/KVM, and virt-manager. Creates rpool/vms/images with tuned recordsize. Caps ARC at 50% RAM to leave room for VM memory.
KLDLOAD_ENABLE_EBPF0Install eBPF toolchain: bpftool, bpftrace, bcc-tools. Enables kernel-level tracing, performance analysis, and custom eBPF programs.
KLDLOAD_ENABLE_AI0Install AI/ML stack: Python pip, cmake, gcc, make, git. For GPU workloads and model training/inference.
KLDLOAD_NVIDIA_DRIVERS0Install NVIDIA proprietary GPU drivers. Required for CUDA workloads, GPU passthrough, and AI inference. Adds significant size to the install.

WireGuard is on by default because every server should have encrypted management access. ZFS is always on because that is the entire point of kldload. Everything else is opt-in because it adds size, complexity, and attack surface. NVIDIA drivers add hundreds of megabytes. eBPF tools add kernel headers. KVM installs an entire virtualization stack. AI installs build toolchains. Each toggle is an explicit decision: "I need this capability on this machine." The defaults produce a lean, secure, fast-booting server with ZFS and WireGuard. Add what you need. Leave off what you do not.

Packages and darksites

Package management variables control what extra software is installed beyond the profile defaults, and how the package mirrors are configured on the installed system.

VariableDefaultDescription
KLDLOAD_EXTRA_PACKAGESemptyAdditional packages to install beyond the profile defaults. Space or comma separated. Must exist in the darksite (for offline installs) or in the distro repos (for online installs).
KLDLOAD_KEEP_DARKSITE0Set to 1 to copy the darksite mirror to the installed system. Allows the installed machine to install additional packages offline, or serve as a local mirror for other machines.
KLDLOAD_CUSTOM_MIRROR_URLnoneCustom APT/DNF mirror URL for the installed system's package configuration. Useful for air-gapped environments with an internal mirror.

How darksites work

Darksites are offline package mirrors baked into the ISO at build time. The ISO contains every RPM, DEB, or APK needed to install the target system without internet access. During install, the live ISO serves these packages to the installer via local HTTP (Debian on :3142, Ubuntu on :3143) or direct filesystem access (RPM distros via file:///root/darksite/). After install, the target system's repos are reconfigured to point at the upstream internet mirrors by default. Set KLDLOAD_KEEP_DARKSITE=1 to preserve the local mirror on the installed system.

The darksite is like shipping a vending machine pre-stocked with everything you need. You do not need to call the supplier (internet) — everything is already inside.
# Install extra packages (available in darksite or online repos)
KLDLOAD_EXTRA_PACKAGES="nginx postgresql-server redis"

# Air-gapped server — keep the darksite for future package installs
KLDLOAD_KEEP_DARKSITE=1

# Point at your internal Artifactory mirror post-install
KLDLOAD_CUSTOM_MIRROR_URL=https://artifactory.internal.corp/debian
KLDLOAD_KEEP_DARKSITE=0

The darksite is the secret weapon for air-gapped and compliance-constrained environments. Government networks, classified facilities, industrial control systems, ships at sea, remote mining sites — anywhere the machine cannot reach the internet. The ISO contains everything. No apt-get update that fails because the mirror is unreachable. No dnf install that times out because the satellite link is 9600 baud. The packages are right there on the USB stick, pre-resolved, pre-downloaded, checksummed. It just works.

The KLDLOAD_EXTRA_PACKAGES variable is additive. It does not replace the profile packages — it adds to them. If your profile installs nginx and you set KLDLOAD_EXTRA_PACKAGES=redis, you get both nginx and redis. The package names must match the target distro's package naming: nginx on Debian, nginx on CentOS (same in this case), but postgresql on Debian vs postgresql-server on CentOS. Know your distro's package names.

Export — golden image workflow

The export workflow converts a freshly installed system into a cloud-ready golden template. The installer does the OS install, seals the image for cloning (clears machine-id, removes SSH host keys, enables cloud-init), then uses qemu-img convert to produce the image in the requested format. Optionally uploads it via SCP to a remote host.

VariableDefaultDescription
KLDLOAD_EXPORT_FORMATnoneImage export format. none = normal install (no export). Other values produce a golden image file.
KLDLOAD_EXPORT_SCP_HOSTnoneRemote hostname or IP for SCP upload. If set, the image is uploaded after creation.
KLDLOAD_EXPORT_SCP_USERrootSSH user for SCP upload.
KLDLOAD_EXPORT_SCP_PATH/root/Remote destination path for SCP upload.
KLDLOAD_EXPORT_SCP_KEYnonePath to SSH private key for SCP authentication. If set, key-based auth is used.
KLDLOAD_EXPORT_SCP_PASSnoneSSH password for SCP (if no key). Uses sshpass if available. Redacted in logs.

KLDLOAD_EXPORT_FORMAT — output formats

Valid export formats and their targets

none — Normal install. No image export. The system is installed to the disk and boots normally.

qcow2 — QEMU Copy-On-Write v2. For KVM, Proxmox, OpenStack. Supports thin provisioning, snapshots, compression. The most common format for Linux hypervisors.

vmdk — VMware Virtual Machine Disk. For ESXi, vSphere, Workstation, Fusion. Widely supported.

vhd — Virtual Hard Disk. For Hyper-V and Azure. Microsoft ecosystem standard.

ova — Open Virtualization Archive. Portable format containing OVF metadata + VMDK. Import into VirtualBox, vSphere, or any OVF-compatible hypervisor.

raw — Raw disk image. Byte-for-byte copy. For dd to physical disks, Firecracker microVMs, or custom toolchains.

The same cake, in different boxes. qcow2 is the KVM box. vmdk is the VMware box. vhd is the Hyper-V box. raw is no box at all.

Image sealing process

Before export, k_seal_image_for_clone() prepares the system for cloning:

What gets cleared

/etc/machine-id — truncated (systemd regenerates on first boot).
/var/lib/dbus/machine-id — removed.
/etc/ssh/ssh_host_* — all host keys removed (sshd-keygen regenerates).
DHCP leases — cleared from NetworkManager, dhclient, systemd-networkd.
Cloud-init instance data — removed so cloud-init re-runs on next boot.
/root/.bash_history — deleted.
wtmp, btmp, lastlog — truncated.
Install manifest — removed (contains build-time passwords).

What gets enabled

Cloud-init services — cloud-init.service, cloud-init-local.service, cloud-config.service, cloud-final.service all enabled.
Datasource config — accepts NoCloud, ConfigDrive, OpenStack, Azure, GCE, EC2.
The image boots, cloud-init runs, and the machine personalizes itself from whatever datasource is available.

# Export a qcow2 golden image and upload to the Proxmox host
KLDLOAD_EXPORT_FORMAT=qcow2
KLDLOAD_EXPORT_SCP_HOST=proxmox.lab.local
KLDLOAD_EXPORT_SCP_USER=root
KLDLOAD_EXPORT_SCP_PATH=/var/lib/vz/template/images/
KLDLOAD_EXPORT_SCP_KEY=/root/.ssh/id_ed25519

# Export a vmdk for VMware — no upload, retrieve manually
KLDLOAD_EXPORT_FORMAT=vmdk

# Export raw for dd to physical disks or Firecracker
KLDLOAD_EXPORT_FORMAT=raw

The golden image workflow replaces Packer for the base image. Packer is great at layering application packages onto an existing base image. But Packer cannot build the base image itself — it cannot set up ZFS on root, configure boot environments, build DKMS modules, or create a darksite. kldload does all of that, then exports the result as a qcow2/vmdk/vhd that Packer can consume as a source image. The pipeline is: kldload builds the OS layer, exports a golden image, Packer layers the application on top, Terraform deploys the result. Each tool does what it is best at.

The SCP upload is optional but extremely convenient for CI pipelines. Build the ISO, boot a VM, run the unattended install with KLDLOAD_EXPORT_FORMAT=qcow2 and KLDLOAD_EXPORT_SCP_HOST=artifact-server, and the golden image lands on your artifact server automatically. No manual retrieval. No additional scripting. The installer does the install, exports the image, uploads it, and powers off. Your CI pipeline picks up the artifact from the server and feeds it to Packer or Terraform.

Cluster and infrastructure mode

Infrastructure mode transforms a standalone machine into a node in a managed cluster. A cluster manager (control plane) coordinates multiple minion nodes via WireGuard mesh networking, Salt configuration management, and automated blue/green deployment.

VariableDefaultDescription
KLDLOAD_INFRA_MODEstandaloneDeployment intent. standalone = single machine. control-plane = cluster manager (hub). join = join an existing cluster as a minion.
KLDLOAD_CLUSTER_CIDRnoneWireGuard cluster CIDR block. A /20 is split into 16 x /24 subnets (8 blue, 8 green). Example: 10.78.0.0/20.
KLDLOAD_CLUSTER_DOMAINinfra.localInternal DNS domain for the cluster. Used for service discovery and inter-node resolution.
KLDLOAD_CLUSTER_SIZE16Maximum number of nodes in the cluster. Determines the subnet allocation table.
KLDLOAD_HUB_LANnoneLAN IP of the cluster manager. Used by join-mode nodes to locate and connect to the hub at first boot.
KLDLOAD_NODE_ROLE_NminionRole for node N in the cluster. Values: minion, k8s-control, k8s-worker, k8s-lb, storage, prometheus, grafana, custom. Node 0 is always master/hub.

Infrastructure mode is the difference between "I installed one server" and "I deployed a cluster." A standalone install is one machine with ZFS, WireGuard, and whatever profile you selected. A control-plane install is the same machine, plus Salt master, plus cluster subnet allocation, plus blue/green deployment zones, plus a node role table. A join install is a minion that auto-discovers the hub on the LAN, exchanges WireGuard keys, and registers itself into the cluster. Boot the hub first, then boot the minions. They find each other automatically.

Distro-specific and mirror configuration

Variables that only apply to specific distributions or control package mirror behavior for the installed system.

VariableDefaultDescription
KLDLOAD_RELEASE9Release version for CentOS/RHEL/Rocky (e.g., 9). Determines which repo metalink is used.
KLDLOAD_DEBIAN_RELEASEtrixieDebian suite codename. trixie (Debian 13) or bookworm (Debian 12).
KLDLOAD_DEBIAN_SUITEtrixieAlias for KLDLOAD_DEBIAN_RELEASE. Either can be used.
KLDLOAD_DEBIAN_MIRRORhttps://mirror.it.ubc.ca/debianUpstream Debian APT mirror. Used when building the darksite and for post-install repo configuration.
KLDLOAD_MIRRORauto-detectedAPT mirror URL. Auto-detects the local darksite if running from the live ISO. Falls back to KLDLOAD_DEBIAN_MIRROR.
KLDLOAD_SUITEtrixieAPT suite for debootstrap. Matches KLDLOAD_DEBIAN_RELEASE.
KLDLOAD_BOOTLOADER_IDKLDloadEFI boot entry name. Shown in the firmware boot menu and efibootmgr output.

Build-time variables

These variables are set in kldload.env and control the ISO build process. They run on the build machine (your laptop, CI runner, or build server) and never reach the target system.

VariableDefaultDescription
PROFILEdesktopBuild profile. desktop includes GNOME, Firefox, webui. server is minimal with SSH and ops tools. core is ZFS only, stock distro.
DISTROcentosTarget distro for the ISO. Same values as KLDLOAD_DISTRO.
ARCHx86_64Target architecture. Currently only x86_64 is fully supported.
RELEASE9Distro release version (e.g., 9 for CentOS Stream 9).
EDITIONfreeBuild edition. Always free.
OUTPUT_DIRlive-build/outputWhere the built ISO is written.
LOG_DIRlive-build/logsWhere build logs are written.
BUILDER_IMAGEkldload-live-builder:latestDocker/Podman image used for the build container.

RHEL subscription (RHEL builds only)

VariableDefaultDescription
RHEL_ACTIVATION_KEYnoneRed Hat activation key name. Required for RHEL builds. Used at build time only — never baked into the ISO.
RHEL_ORG_IDnoneRed Hat organization ID. Found at console.redhat.com under your account settings.

Proxmox deployment

VariableDefaultDescription
PROXMOX_HOSTnoneProxmox host IP or hostname for API access.
PROXMOX_NODEnoneProxmox node name (as shown in the Proxmox web UI).
PROXMOX_TOKEN_IDnoneProxmox API token ID. Format: user@realm!tokenname (e.g., root@pam!kldload).
PROXMOX_TOKEN_SECRETnoneProxmox API token secret. UUID format.
VMID902VM ID in Proxmox. Must be unique on the cluster.
VM_NAMEkldload-freeVM display name in Proxmox.
VM_MEMORY4096VM RAM in megabytes.
VM_CORES4Number of CPU cores assigned to the VM.
VM_DISK_GB40VM disk size in gigabytes.
VM_BRIDGEvmbr0Proxmox network bridge for the VM NIC.
VM_SECURE_BOOTnoEnable UEFI Secure Boot on the VM. Requires MOK enrollment on first boot.
VM_TPMyesEnable TPM 2.0 emulation on the VM.

USB & physical deploy

VariableDefaultDescription
USB_DEVICEnoneUSB block device for ISO burning. Must be set explicitly. Never auto-detected (safety).
USB_BURN_ON_DEPLOYnoSet to yes to auto-burn the ISO to USB_DEVICE after build.
kldload.env is gitignored. Credentials (RHEL keys, Proxmox tokens, API secrets) live in kldload.env which is excluded from git. Never committed. Never in the ISO. Build-time only.

The security boundary is clear. Build-time credentials (kldload.env) never enter the ISO. The ISO contains packages, scripts, and darksites — not your Proxmox tokens or RHEL activation keys. Install-time credentials (passwords, SSH keys) go into the answer file on the seed disk — which you control physically. At no point does any credential touch a network, a cloud API, or a third-party service. The build machine has your build creds. The seed disk has your install creds. The ISO has neither.

Advanced and internal variables

Variables that control internal installer behavior, logging, and advanced workflows. Most users never need to set these. They exist for CI pipelines, custom integrations, and debugging.

VariableDefaultDescription
KLDLOAD_TARGET_MNT/targetMount point for the target filesystem during install. All chroot operations happen under this path.
KLDLOAD_LOG_DIR/var/log/installerDirectory for all installer log files. Contains kldload-installer.log, bootstrap.log, storage.log, zfs.log, security.log, network.log, bootloader.log, and effective-config.env.
KLDLOAD_INSTALLER_VERSION1.0.0Installer version. Written to the install manifest for audit.
KLDLOAD_TEMPLATEemptyNamed template identifier. Written to the install manifest for tracking which template was used.
KLDLOAD_SECURE_BOOTauto-detectedSet to 1 if Secure Boot is enabled. Auto-detected via mokutil --sb-state. Affects ZFS module loading strategy.
KLDLOAD_TPM_PRESENTauto-detectedSet to 1 if a TPM 2.0 device is detected at /dev/tpm0 or /dev/tpmrm0.

Cloud provider variables (cluster mode)

When running in control-plane infrastructure mode, these optional variables enable cloud provider integration for hybrid cluster deployments.

VariableDefaultDescription
KLDLOAD_AWS_ACCESS_KEY_IDnoneAWS access key for hybrid cloud deployments.
KLDLOAD_AWS_REGIONnoneAWS region (e.g., us-west-2).
KLDLOAD_AZURE_SUBSCRIPTION_IDnoneAzure subscription ID for hybrid deployments.
KLDLOAD_GCP_PROJECT_IDnoneGoogle Cloud project ID for hybrid deployments.

Complete examples

Full answers.env files for real-world scenarios. Copy, modify, deploy. Each example shows every relevant variable for that use case — but remember, you only need to set what differs from defaults.

Example 1: Minimal headless server (Debian, DHCP, single disk)

The simplest production install

Three lines define the essentials. Everything else uses sane defaults: DHCP networking, admin user, UTC timezone, server profile, single-disk ZFS.

# answers.env — Minimal Debian server
KLDLOAD_DISTRO=debian
KLDLOAD_DISK=/dev/sda
KLDLOAD_HOSTNAME=web-01
KLDLOAD_USERNAME=admin
KLDLOAD_PASSWORD=changeme
KLDLOAD_SSH_PUBKEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBx... ops@infra"
KLDLOAD_FORCE_WIPE=1

Example 2: Encrypted desktop workstation (Ubuntu, GNOME, NVMe)

Developer workstation with full-disk encryption

Ubuntu desktop with GNOME, ZFS native encryption, local timezone, and NVIDIA GPU drivers for CUDA development. The encryption passphrase is prompted at every boot.

# answers.env — Encrypted Ubuntu desktop
KLDLOAD_DISTRO=ubuntu
KLDLOAD_PROFILE=desktop
KLDLOAD_DISK=/dev/nvme0n1
KLDLOAD_HOSTNAME=dev-workstation
KLDLOAD_USERNAME=developer
KLDLOAD_PASSWORD='$trong-Desktop-P@ss!'
KLDLOAD_TIMEZONE=America/Vancouver
KLDLOAD_LOCALE=en_US.UTF-8
KLDLOAD_KEYBOARD_LAYOUT=us

# ZFS encryption — prompted for passphrase at boot
KLDLOAD_ZFS_ENCRYPT=1
KLDLOAD_ZFS_PASSPHRASE='my-encryption-passphrase-change-me'

# GPU + containers for development
KLDLOAD_NVIDIA_DRIVERS=1
KLDLOAD_ENABLE_EBPF=1
KLDLOAD_EXTRA_PACKAGES="git build-essential python3-venv nodejs npm"

KLDLOAD_NET_METHOD=dhcp
KLDLOAD_FORCE_WIPE=1

Example 3: KVM hypervisor with mirrored ZFS (Rocky, static IP)

Production hypervisor with redundancy

Rocky Linux server with KVM, mirrored ZFS pool across two NVMe drives, static IP, eBPF monitoring, and WireGuard management tunnel. The KLDLOAD_ENABLE_KVM=1 flag creates the rpool/vms/images dataset with tuned recordsize and caps ARC at 50% RAM.

# answers.env — KVM hypervisor on Rocky Linux
KLDLOAD_DISTRO=rocky
KLDLOAD_PROFILE=server
KLDLOAD_DISK=/dev/nvme0n1
KLDLOAD_HOSTNAME=kvm-host-01
KLDLOAD_USERNAME=sysadmin
KLDLOAD_PASSWORD='Hyp3rv1sor-Adm!n-2024'
KLDLOAD_TIMEZONE=UTC

# Mirrored ZFS across two NVMe drives
KLDLOAD_ZFS_TOPOLOGY=mirror
KLDLOAD_ZFS_DATA_DISKS=/dev/nvme1n1

# Static IP on the management network
KLDLOAD_NET_METHOD=static
KLDLOAD_NET_IFACE=eno1
KLDLOAD_NET_IP=10.0.1.10
KLDLOAD_NET_PREFIX=24
KLDLOAD_NET_GW=10.0.1.1
KLDLOAD_NET_DNS=10.0.1.1,1.1.1.1

# Enable KVM + eBPF + WireGuard
KLDLOAD_ENABLE_KVM=1
KLDLOAD_ENABLE_EBPF=1
KLDLOAD_WIREGUARD=1

# SSH key for automation
KLDLOAD_SSH_PUBKEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICz... terraform@ci"
KLDLOAD_ADMIN_SSH_PUBKEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFy... break-glass@vault"
KLDLOAD_FORCE_WIPE=1

Example 4: Golden image export for Packer pipeline (CentOS, qcow2)

Automated golden image factory

CentOS server installed to a VM disk, sealed for cloning (machine-id cleared, SSH keys removed, cloud-init enabled), exported as qcow2, and uploaded to the artifact server via SCP. The Packer pipeline picks up the artifact and layers application packages on top.

# answers.env — Golden image for CI pipeline
KLDLOAD_DISTRO=centos
KLDLOAD_PROFILE=server
KLDLOAD_DISK=/dev/vda
KLDLOAD_HOSTNAME=golden-centos-base
KLDLOAD_USERNAME=packer
KLDLOAD_PASSWORD='packer-build-temporary'
KLDLOAD_TIMEZONE=UTC

# Export as qcow2 and upload to artifact server
KLDLOAD_EXPORT_FORMAT=qcow2
KLDLOAD_EXPORT_SCP_HOST=artifacts.internal.corp
KLDLOAD_EXPORT_SCP_USER=deploy
KLDLOAD_EXPORT_SCP_PATH=/var/artifacts/images/
KLDLOAD_EXPORT_SCP_KEY=/root/.ssh/deploy_key

# DHCP — the VM gets a temporary IP during build
KLDLOAD_NET_METHOD=dhcp
KLDLOAD_FORCE_WIPE=1

# The image is sealed automatically:
#   - machine-id cleared
#   - SSH host keys removed
#   - cloud-init enabled (NoCloud, ConfigDrive, OpenStack, Azure, GCE, EC2)
#   - install manifest removed (no passwords on disk)

Example 5: Air-gapped CI runner fleet (Debian, RAIDZ1, darksite)

Classified network CI runner

Debian server on a classified network with no internet access. Three-disk RAIDZ1 for data protection. Darksite preserved on the installed system so the CI runner can install additional packages offline. Custom internal mirror for future updates when the mirror is available on the secure network.

# answers.env — Air-gapped CI runner
KLDLOAD_DISTRO=debian
KLDLOAD_PROFILE=server
KLDLOAD_DISK=/dev/sda
KLDLOAD_HOSTNAME=ci-runner-01
KLDLOAD_USERNAME=ci
KLDLOAD_PASSWORD='c1-Runn3r-@ir-g@pped'
KLDLOAD_TIMEZONE=UTC

# RAIDZ1 across three disks — one parity drive
KLDLOAD_ZFS_TOPOLOGY=raidz1
KLDLOAD_ZFS_DATA_DISKS="/dev/sdb /dev/sdc"

# Static IP on the isolated network
KLDLOAD_NET_METHOD=static
KLDLOAD_NET_IFACE=ens18
KLDLOAD_NET_IP=192.168.100.50
KLDLOAD_NET_PREFIX=24
KLDLOAD_NET_GW=192.168.100.1
KLDLOAD_NET_DNS=192.168.100.1

# Keep the darksite — no internet, need offline packages
KLDLOAD_KEEP_DARKSITE=1
KLDLOAD_CUSTOM_MIRROR_URL=http://mirror.secure.internal/debian

# Extra CI tooling
KLDLOAD_EXTRA_PACKAGES="git make gcc podman buildah skopeo jq"

# SSH key for the CI orchestrator
KLDLOAD_SSH_PUBKEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDz... jenkins@ci-master"
KLDLOAD_FORCE_WIPE=1

Example 6: Cluster manager with blue/green deployment zones

Infrastructure control plane

CentOS control-plane node managing a 16-node cluster over WireGuard mesh. Four network planes (management, data, storage, external). Blue/green deployment zones for zero-downtime upgrades. The cluster CIDR is a /20 split into 16 x /24 subnets.

# answers.env — Cluster manager (hub)
KLDLOAD_DISTRO=centos
KLDLOAD_PROFILE=server
KLDLOAD_DISK=/dev/nvme0n1
KLDLOAD_HOSTNAME=hub-01
KLDLOAD_USERNAME=ops
KLDLOAD_PASSWORD='Hub-M@ster-Key-2024'
KLDLOAD_TIMEZONE=UTC

# Infrastructure mode — this is the control plane
KLDLOAD_INFRA_MODE=control-plane
KLDLOAD_CLUSTER_CIDR=10.78.0.0/20
KLDLOAD_CLUSTER_DOMAIN=prod.infra.local
KLDLOAD_CLUSTER_SIZE=16

# Mirrored NVMe for the hub
KLDLOAD_ZFS_TOPOLOGY=mirror
KLDLOAD_ZFS_DATA_DISKS=/dev/nvme1n1

# Static IP — the hub must be reachable
KLDLOAD_NET_METHOD=static
KLDLOAD_NET_IFACE=eno1
KLDLOAD_NET_IP=10.0.1.5
KLDLOAD_NET_PREFIX=24
KLDLOAD_NET_GW=10.0.1.1
KLDLOAD_NET_DNS=10.0.1.1

# All four WireGuard planes
KLDLOAD_WIREGUARD=1
KLDLOAD_WG0_PORT=51820
KLDLOAD_WG1_PORT=51821
KLDLOAD_WG2_PORT=51822
KLDLOAD_WG3_PORT=51823

# Enable KVM + eBPF for the hub
KLDLOAD_ENABLE_KVM=1
KLDLOAD_ENABLE_EBPF=1
KLDLOAD_FORCE_WIPE=1

Example 7: Fedora desktop with Arch-style minimalism

# answers.env — Fedora desktop, single NVMe, encrypted
KLDLOAD_DISTRO=fedora
KLDLOAD_PROFILE=desktop
KLDLOAD_DISK=/dev/nvme0n1
KLDLOAD_HOSTNAME=fedora-desktop
KLDLOAD_USERNAME=todd
KLDLOAD_PASSWORD='f3d0r@-D3skt0p'
KLDLOAD_TIMEZONE=America/Vancouver
KLDLOAD_LOCALE=en_US.UTF-8
KLDLOAD_KEYBOARD_LAYOUT=us

KLDLOAD_ZFS_ENCRYPT=1
KLDLOAD_ZFS_PASSPHRASE='boot-passphrase-for-nvme'
KLDLOAD_NET_METHOD=dhcp
KLDLOAD_FORCE_WIPE=1

These examples are not theoretical. They are the actual answers files from real deployments. The minimal server is three production web servers behind a load balancer. The encrypted desktop is my daily-driver laptop. The KVM hypervisor runs 40 VMs on mirrored NVMe. The golden image pipeline produces weekly base images consumed by Terraform. The air-gapped CI runner lives on a network that has never seen the internet. The cluster manager coordinates 16 nodes across two racks.

The pattern is always the same: decide what you need, set the variables, plug in the USB, boot, walk away. The machine configures itself. Whether it is one machine or fifty, the process is identical. The answer file is the only thing that changes.

Complete variable reference table

Every install-time variable in one table. Sorted by category. This is the authoritative reference — if a variable exists, it is listed here.

VariableTypeDefaultDescription
Core
KLDLOAD_DISTROenumcentosTarget distro: centos, debian, ubuntu, fedora, rhel, rocky, arch, alpine, freebsd, openbsd
KLDLOAD_PROFILEenumserverInstall profile: server, desktop, core, ai, client
KLDLOAD_HOSTNAMEstringkldload-nodeSystem hostname (letters, digits, hyphens)
KLDLOAD_TIMEZONEstringUTCTZ database timezone name
KLDLOAD_LOCALEstringen_US.UTF-8System locale
KLDLOAD_KEYBOARD_LAYOUTstringusXKB keyboard layout
KLDLOAD_KEYBOARD_VARIANTstringemptyXKB keyboard variant (optional)
Disk & Storage
KLDLOAD_DISKpathautoTarget block device (e.g., /dev/sda, /dev/nvme0n1)
KLDLOAD_STORAGE_MODEenumzfsStorage mode (always zfs)
KLDLOAD_ZFS_TOPOLOGYenumsinglePool layout: single, mirror, raidz1, mirror-stripe
KLDLOAD_ZFS_DATA_DISKSstringemptyAdditional disks (space-separated paths)
KLDLOAD_ZFS_SPECIAL_DISKSstringemptyNVMe metadata vdev disks (space-separated)
KLDLOAD_ZFS_ENCRYPTbool0Enable ZFS native encryption (AES-256-GCM)
KLDLOAD_ZFS_PASSPHRASEstringpromptedEncryption passphrase (redacted in logs)
KLDLOAD_ENABLE_ZFSbool1Always 1 (ZFS is non-optional)
KLDLOAD_FORCE_WIPEbool0Skip disk wipe confirmation
Networking
KLDLOAD_NET_METHODenumdhcpNetwork method: dhcp or static
KLDLOAD_NET_IFACEstringautoNetwork interface name
KLDLOAD_NET_IPstringnoneStatic IP address
KLDLOAD_NET_PREFIXint24CIDR prefix length
KLDLOAD_NET_GWstringnoneDefault gateway
KLDLOAD_NET_DNSstringnoneDNS servers (comma-separated)
User & Auth
KLDLOAD_USERNAMEstringadminNon-root admin user
KLDLOAD_PASSWORDstringpromptedAdmin password (redacted in logs)
KLDLOAD_ROOT_PASSWORDstringnoneRoot password (if unset, root is locked)
KLDLOAD_SSH_PUBKEYstringnoneSSH public key for admin user
KLDLOAD_ADMIN_SSH_PUBKEYstringnoneSSH public key for root (emergency access)
KLDLOAD_GENERATE_SSH_KEYbool1Generate SSH host keys during install
WireGuard
KLDLOAD_WIREGUARDbool0Configure WireGuard interface
KLDLOAD_ENABLE_WIREGUARDbool1Include WireGuard module and tools
KLDLOAD_WG0_PORTint51820Management plane listen port
KLDLOAD_WG1_PORTint51821Data plane listen port
KLDLOAD_WG2_PORTint51822Storage plane listen port
KLDLOAD_WG3_PORTint51823External plane listen port
KLDLOAD_WIREGUARD_PRIVATE_KEYstringgeneratedWireGuard private key (redacted in logs)
KLDLOAD_WIREGUARD_PRESHARED_KEYstringnonePreshared key for post-quantum security (redacted)
Feature Toggles
KLDLOAD_ENABLE_KVMbool0Install KVM/QEMU/libvirt
KLDLOAD_ENABLE_EBPFbool0Install eBPF toolchain
KLDLOAD_ENABLE_AIbool0Install AI/ML stack
KLDLOAD_NVIDIA_DRIVERSbool0Install NVIDIA GPU drivers
Packages & Mirrors
KLDLOAD_EXTRA_PACKAGESstringemptyAdditional packages (space/comma separated)
KLDLOAD_KEEP_DARKSITEbool0Preserve darksite on installed system
KLDLOAD_CUSTOM_MIRROR_URLstringnoneCustom package mirror URL
Export (Golden Image)
KLDLOAD_EXPORT_FORMATenumnoneExport format: none, qcow2, vmdk, vhd, ova, raw
KLDLOAD_EXPORT_SCP_HOSTstringnoneRemote host for SCP upload
KLDLOAD_EXPORT_SCP_USERstringrootSCP user
KLDLOAD_EXPORT_SCP_PATHstring/root/SCP destination path
KLDLOAD_EXPORT_SCP_KEYpathnoneSSH private key for SCP
KLDLOAD_EXPORT_SCP_PASSstringnoneSSH password for SCP (redacted)
Cluster & Infrastructure
KLDLOAD_INFRA_MODEenumstandaloneDeploy intent: standalone, control-plane, join
KLDLOAD_CLUSTER_CIDRstringnoneCluster WireGuard CIDR (e.g., 10.78.0.0/20)
KLDLOAD_CLUSTER_DOMAINstringinfra.localCluster DNS domain
KLDLOAD_CLUSTER_SIZEint16Maximum cluster nodes
KLDLOAD_HUB_LANstringnoneHub LAN IP (for join mode)
Distro-Specific
KLDLOAD_RELEASEstring9CentOS/RHEL/Rocky release version
KLDLOAD_DEBIAN_RELEASEstringtrixieDebian suite codename
KLDLOAD_DEBIAN_SUITEstringtrixieAlias for KLDLOAD_DEBIAN_RELEASE
KLDLOAD_DEBIAN_MIRRORstringhttps://mirror.it.ubc.ca/debianDebian APT mirror
KLDLOAD_MIRRORstringautoAPT mirror (auto-detects darksite)
KLDLOAD_SUITEstringtrixieAPT suite for debootstrap
KLDLOAD_BOOTLOADER_IDstringKLDloadEFI boot entry name
Advanced & Internal
KLDLOAD_TARGET_MNTpath/targetTarget filesystem mount point
KLDLOAD_LOG_DIRpath/var/log/installerInstaller log directory
KLDLOAD_INSTALLER_VERSIONstring1.0.0Installer version (audit)
KLDLOAD_TEMPLATEstringemptyTemplate identifier (audit)
KLDLOAD_SECURE_BOOTboolautoSecure Boot detected
KLDLOAD_TPM_PRESENTboolautoTPM 2.0 detected

That is the entire API surface for kldload installation. Roughly 70 variables across 10 categories. Most installs use fewer than 15. The minimal install uses 3 (distro, disk, hostname). Everything else has a sane default. The table above is the single source of truth — if it is not in this table, the installer does not read it. If it is in this table, it does exactly what the description says. No hidden behavior. No undocumented side effects. No magic.

The variable naming convention is deliberate and consistent. KLDLOAD_ prefix for all install-time variables. KLDLOAD_NET_ for networking. KLDLOAD_ZFS_ for ZFS. KLDLOAD_EXPORT_ for golden image export. KLDLOAD_WGN_ for WireGuard planes. KLDLOAD_ENABLE_ for feature toggles. KLDLOAD_CLUSTER_ for infrastructure mode. The prefix tells you the category. The suffix tells you the specific setting. You can grep for KLDLOAD_NET_ and find every networking variable. You can grep for KLDLOAD_EXPORT_ and find every export variable. Discoverability by convention.