Provision a host
The host tool, nix run .#host, turns a rented server in rescue mode into a NixOS host that installs from a reviewed plan and then updates itself. It never trusts a new SSH key automatically and never formats a disk you haven't approved.
- Commands
discover·plan·install·enroll·verify- Inventory
fleet/hosts/NAME/host.json, plus an optionalconfiguration.nixfor roles
1. Discover
Get the rescue system's Ed25519 host key through a trusted channel and put it in its own known-hosts file. Then record what the machine has:
nix run .#host -- discover --platform hetzner --name example \ --target root@HOST --known-hosts /private/rescue-known-hosts \ --identity /private/operator-key --output /private/example.candidate.json
Use --platform ec2 for EC2 bare metal. Discovery won't overwrite an existing file. It suggests a network configuration (DHCP on EC2, interface addresses on Hetzner) for you to review.
2. Review and declare
Copy the candidate to fleet/hosts/NAME/host.json and finish it by hand:
- Set
reviewedto true once you've checked the hardware, firmware and networking. - Choose
storage.systemby disk ID: two physical disks on Hetzner, or the existing root EBS volume on EC2. On EC2, list the local NVMe IDs instorage.guests. - Give
identity.hostIda persistent, nonzero eight-digit hex value. Leaveidentity.sshKeysempty for now. - Set
budgets.guestCpuCores,guestMemoryMiBandarcMaxMiB, and on Hetzner a reviewedstorage.quotaBytes. - Add your Git server's known-host lines to
git.knownHosts. Private keys never go in the declaration.
If the host will run Lilly, declare that role now (see enroll hosts), then commit and push to main. Planning only accepts a commit that has been pushed to main.
3. Plan
nix run .#host -- plan --host example --revision FULL_COMMIT \ --target root@HOST --known-hosts /private/rescue-known-hosts \ --identity /private/operator-key --output /private/example.plan.json
The plan pins the declaration, the authenticated target key, the firmware, the revision, the exact system, disk-layout and kexec paths, and the list of disks it will erase. Read it. Everything is built before anything destructive happens.
4. Install
nix run .#host -- install --plan /private/example.plan.json --erase PLAN_SHA256 \ --target root@HOST --known-hosts /private/rescue-known-hosts \ --identity /private/operator-key
The installer kexecs into a pinned image, compares the disk inventory again and formats under a lock. It refuses if a device is mounted or a pool is imported. The new host key is generated on the host; only its public half comes back, recorded in PLAN.installed.json. Keep that file. An interrupted install never resumes formatting silently.
5. Enroll and verify
Record the installed public key in identity.sshKeys and in a new known-hosts file. Add the Git credential reference secrets/NAME-git.enc.json to credentials, then prepare the encrypted Git bundle. Use your own account name from nix/users.nix in place of YOU:
nix run .#host -- enroll --host example --deploy-key /private/shared-git-key \ --root . --target YOU@HOST --known-hosts /private/installed-known-hosts \ --identity /private/operator-key
Push the updated declaration and ciphertext, then activate that exact commit and check it:
nix run .#host -- enroll --host example --revision ENROLLED_FULL_COMMIT \ --install-receipt /private/example.plan.json.installed.json \ --target YOU@HOST --known-hosts /private/installed-known-hosts \ --identity /private/operator-key nix run .#host -- verify --target YOU@HOST \ --known-hosts /private/installed-known-hosts --identity /private/operator-key
From here the host polls main every ten seconds, with jitter. Changes to the platform, firmware, disks or identity are rejected by ordinary updates; those need deliberate maintenance.
Full reference: docs/host-provisioning.md