FreeBSD on a Hetzner Cloud VPS
Hetzner has no FreeBSD image and their ISO library stops at 15.0. You can still get any release on there, unattended, in about an hour.
The box runs AthletOS, a training app I am building. Predefined programs, session logging and tracking.
— I picked FreeBSD for ZFS boot environments and jails, so a bad deploy is a rollback instead of an incident.
This is the whole procedure, in order, with the things that went wrong and what fixed them. I used FreeBSD’s own ISO rather than a prebuilt image, so the checksum can be verified against the project’s manifest.
1. Create the server
A CX23 gives 2 vCPU and 4 GB, which is plenty. Firewall first, then the box.
for; do
done
The image is a throwaway. It only exists so the server boots at all.
2. Boot into rescue
3. Fetch and verify the ISO
Take bootonly, not disc1. The rescue root is a small RAM overlay and a 1.3 GB ISO plus its extraction plus the rebuild will not fit.
B=https://download.freebsd.org/releases/amd64/amd64/ISO-IMAGES/15.1
I=FreeBSD-15.1-RELEASE-amd64-bootonly.iso
E=
A=
[ && ||
4. Remaster the ISO
Three edits: a serial console so we can watch it, the prompt that would otherwise block forever, and the network the scripted install never configures for itself.
&& &&
# append, never replace: the original carries vfs.root.mountfrom
# the serial path asks for a terminal type with an unconditional read
# a scripted install never configures the network
What went wrong
I replaced loader.conf instead of appending, which threw away vfs.root.mountfrom and left it at a mountroot prompt.
Then it hung at Console type [vt100]: because that read runs before the script is ever looked for.
Then distfetch died on DNS, because the interactive path exports BSDINSTALL_CONFIGCURRENT and the scripted path does not.
5. Write the unattended install script
PARTITIONS=DEFAULT
DISTRIBUTIONS="kernel.txz base.txz"
#!/bin/sh
# ARC takes half of RAM by default. On a 4GB box that is 2GB it does not
# need, and it turns up later looking like a database problem.
&&
10.0.2.2 is QEMU’s host address. Serving the distribution sets from rescue skips DNS entirely and is quicker than fetching them twice.
&&
B=https://download.freebsd.org/releases/amd64/15.1-RELEASE
for; do ; done
while ; do
[ || continue
[ &&
done
&
Tip: leave that server running. The installer fetches from it in step 7, not now.
6. Build the ISO and wipe the disk
Delete the source ISO before building, or you run out of space in the overlay.
# zfs labels sit at both ends of the device, so wipefs alone is not enough
SECTORS=
MIB=
What went wrong
An earlier attempt got far enough to create a zroot pool before failing.
The next run found it and stopped to ask for a different pool name, which is another dialog nobody is there to answer. Wipe both ends between attempts.
7. Run the installer under QEMU
Hetzner Cloud has no nested virtualisation, so there is no /dev/kvm and this runs under TCG emulation. It takes roughly half an hour. Nothing to do but watch the log.
|
# zfsboot mount distfetch checksum distextract bootconfig config
# entropy umount script
Kill QEMU once the script step finishes, or it reboots straight back into the installer.
8. Boot it, and fix fstab
# the installer ran under qemu virtio (vtbd0), hetzner presents da0.
# the pool is fine because zfs scans devices. swap is not.
What went wrong
The generated fstab pointed at /dev/vtbd0p2, which does not exist on the real machine.
It booted with no swap and said nothing about it. Refer to it by GPT label and the device name stops mattering.
Result
Result
15.1 on root-on-ZFS, a release the ISO library does not carry, from media you can verify.
The same steps work for any release. Change the version in step 3 and the rest follows.
Every command above ran against a real CX23 in hel1.