Skip to content

Distribute images as a bundle, and stop paying for a boot nobody watches - #11

Merged
jacob-carlborg merged 9 commits into
masterfrom
claude/netbsd-11-microvm-kernel-wsrfrg
Aug 16, 2026
Merged

Distribute images as a bundle, and stop paying for a boot nobody watches#11
jacob-carlborg merged 9 commits into
masterfrom
claude/netbsd-11-microvm-kernel-wsrfrg

Conversation

@jacob-carlborg

@jacob-carlborg jacob-carlborg commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Cuts the time a consumer spends before its first command runs, and changes how images are distributed to make that possible.

Distribution. One artifact per image, netbsd-<version>-<architecture>.tar.zst, holding a RAW disk.img and, where the release publishes one, a kernel for QEMU's microvm machine type. Members are named generically so a consumer needs one code path per platform. qcow2 goes: 11.0 x86-64 drops from 497 MiB to 268 MiB.

Boot path. DHCP, ntpdate, and duplicate address detection all sat in front of sshd and none of them did anything useful behind user mode networking. The per-commit messages have the reasoning.

Measured on 11.0 x86-64, against master:

master this branch
time to a reachable sshd ~20.5s 3.2s
total setup 34.4s 18.7s
image download 497 MiB 265 MiB

The consumer-side half is action#162.

Merging

  1. This PR.
  2. Cut a release.
  3. Bump netbsd: in the action's src/version.ts to that tag.
  4. action#162.

The last commit pins Test Image to the action's branch, since action@master builds a .qcow2 URL and cannot read a bundle. Revert that to @master once action#162 has merged.

claude added 9 commits August 15, 2026 08:55
rcorder puts both in front of sshd and neither does anything useful
here. The lease never changes under user mode networking, so freeze the
one this build was given into the static configuration. ntpdate corrects
an offset that is already ~0, since the emulated RTC is seeded from the
host clock; ntpd stays, with -g to step a large offset if one appears.

Also let the runner user log in without a credential on every
architecture, as the VAX image already does, and add a boot_timestamps
variable that stamps each rc.d script as it starts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M9LHkNMNNfDf2Fa1ieGT8g
/etc/rc.d/network was 6 of the 7 seconds the rc sequence took. It ends
with `ifconfig $ifconfig_wait_dad_flags`, whose default `-W 5` waits for
the `detached` flag to clear -- which nothing can satisfy, since the
consumer runs the guest with IPv6 off and no router is ever advertised.

This removes work from the boot without yet making a job start sooner:
something after rc absorbs whatever rc gives back.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M9LHkNMNNfDf2Fa1ieGT8g
A stamp per script start leaves the last script's duration unaccounted
for, so the end of the sequence is only an inference. Stamp the end of
the loop as well.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M9LHkNMNNfDf2Fa1ieGT8g
Removing the wait moved the same six seconds from rc to after rc, rather
than saving them. An address is unusable while the kernel probes for
another host holding it: sshd was listening at +4s but dropped every
connection until +11s, which the client reports as a timeout during the
banner exchange.

Set the probe count to zero instead. The address comes from the
hypervisor's user mode network, a private segment with one guest on it,
so there is no second host that could hold it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M9LHkNMNNfDf2Fa1ieGT8g
One artifact per image, netbsd-<version>-<architecture>.tar.zst, holding
a RAW `disk.img` and, where the release publishes one, a `kernel` for
QEMU's `microvm` machine type. The members are named generically, so a
consumer needs one code path per platform. The kernel is why this is a
tar: that machine type cannot boot from a disk, so the consumer hands
the kernel to QEMU itself and it cannot live inside the image.

qcow2 goes with it. Its compression has to keep the image writable, so
it loses to a solid stream, and the consumer pays that on every job: the
11.0 x86-64 image goes from 497 MiB to 268 MiB.

The image's zero ranges are dug into holes first. Without that the
archive carries all 12 GB and the consumer writes all 12 GB back out,
which measured 47 seconds against the 4 the qcow2 conversion took.

Ref: cross-platform-actions/action#151

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M9LHkNMNNfDf2Fa1ieGT8g
The rc.local hook mounted the last attached disk as FAT and installed
the SSH key it found there as the runner's authorized key. The login
needs no credential now, so there is no disk to find, and the hook was
mounting whatever happened to be last. The resolver restore stays.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M9LHkNMNNfDf2Fa1ieGT8g
packer keeps the ISO in `packer_cache`, inside the workspace the action
copies into the guest: half a gigabyte with no use there. Delete it once
the build is done. The guest now reports the size it received, so the
next time this grows there is something to look at.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M9LHkNMNNfDf2Fa1ieGT8g
The bundle is unreadable to action@master, which builds a .qcow2 URL, so
the image test cannot pass until the action side ships. Point it at the
branch that reads bundles, so this repository's CI is green on its own.

Revert to master once action#162 has merged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M9LHkNMNNfDf2Fa1ieGT8g
The image test asserts an HTTPS fetch to prove the static network
configuration works. certctl(8), which populates the system CA store,
arrived in NetBSD 10, so on 9.2, 9.3 and 9.4 that fetch fails on trust
rather than on connectivity:

    curl: (60) unable to get local issuer certificate

Attempt HTTPS everywhere still, and fall back to plain HTTP only where
the release explains it. A broken CA store on 10 or later keeps failing
the test rather than quietly downgrading.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M9LHkNMNNfDf2Fa1ieGT8g
@jacob-carlborg
jacob-carlborg force-pushed the claude/netbsd-11-microvm-kernel-wsrfrg branch from e5f4582 to 27ab7ef Compare August 15, 2026 08:56
@jacob-carlborg
jacob-carlborg merged commit 9dd30ab into master Aug 16, 2026
22 checks passed
@jacob-carlborg
jacob-carlborg deleted the claude/netbsd-11-microvm-kernel-wsrfrg branch August 16, 2026 06:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants