NetBSD’s superpower is portability. The heart of that is the cross-build framework (build.sh): you can build full releases for other architectures without chroots, containers, or VMs. In this guide you’ll go from zero to installable images for amd64, i386, and evbarm (armv7hf) using a single host machine.
TL;DR: clone
src, run./build.sh tools, then./build.sh -m <arch> releasewith a separate object directory. Repeat for each architecture.
Host prerequisites
You can build from NetBSD, FreeBSD, or Linux.
- Multi-core CPU
- ~32–80 GB free disk (for three arches sharing one
objdir) - 8–16 GB RAM (more
-jjobs helps)
Useful packages by host OS
FreeBSD:
sudo pkg install git gmake bison flex pkgconf xz
NetBSD (via pkgin):
sudo pkgin install git gmake bison flex pkgconf xz
Linux (Debian/Ubuntu-like):
sudo apt update && sudo apt install -y git gcc g++ make bison flex pkg-config xz-utils
Speed tip: use an NVMe SSD and point -O (objdir) to a fast filesystem (ZFS dataset, tmpfs, btrfs) to accelerate repeated builds.
Get the source tree
Git has become the simplest route (CVS remains available). We’ll place src under /usr/src.
sudo mkdir -p /usr/src && sudo chown "$USER" /usr/src
cd /usr/src
# Git example:
git clone https://github.com/NetBSD/src.git .
# (Or use your preferred mirror/branch.)
Unprivileged builds: we’ll use -U so everything happens as your normal user; no touching the live system.
First pass: build the toolchains
build.sh creates a cross toolchain in -T and separates objects in -O.
cd /usr/src
JOBS=$(sysctl -n hw.ncpu 2>/dev/null || nproc)
./build.sh -U -u \
-O /usr/obj \
-T /usr/tools \
-j "$JOBS" tools
-U: unprivileged-u: update/incremental-O: object/artifacts directory-T: tools directory-j: parallel jobs = CPU cores
The first run takes longer. After that, new architecture releases reuse the tools.
Build releases per architecture
The magic is switching -m (MACHINE) and, when needed, -a (MACHINE_ARCH).
amd64 (x86_64)
cd /usr/src
./build.sh -U -u -O /usr/obj -T /usr/tools \
-m amd64 -j "$JOBS" release
Artifacts: .../releasedir/amd64/binary/sets/ and kernels under .../binary/kernel/.
i386 (32-bit x86)
./build.sh -U -u -O /usr/obj -T /usr/tools \
-m i386 -j "$JOBS" release
Great for older hardware and tiny VMs.
evbarm (ARMv7 hard-float)
./build.sh -U -u -O /usr/obj -T /usr/tools \
-m evbarm -a earmv7hf -j "$JOBS" release
Look for a ready-to-write image around:
/usr/obj/releasedir/evbarm/binary/gzimg/armv7.img.gz
Note: exact subpaths vary by board; sets are also under .../binary/sets/ if you prefer installing via sysinst.
Installing the images
Write the ARM image to an SD card
Careful: pick the correct device! On NetBSD something like /dev/rsd0d; on Linux usually /dev/sdX.
# NetBSD/FreeBSD (gzcat available):
sudo gzcat /usr/obj/releasedir/evbarm/binary/gzimg/armv7.img.gz \
| sudo dd of=/dev/rsd0d bs=1m conv=sync
# Linux:
gzip -dc /usr/obj/releasedir/evbarm/binary/gzimg/armv7.img.gz \
| sudo dd of=/dev/sdX bs=1M conv=fsync
sync
Insert the SD into your ARM board and boot. Initial setup is quick; then adjust rc.conf, networking, sshd, etc.
Install amd64 / i386 using sets
Use sysinst with the sets in .../releasedir/<arch>/binary/sets/. In virtualized environments, mounting the install ISO/USB is often fastest.
Quick post-install (pkgsrc/pkgin)
For prebuilt binaries via pkgin:
# Example: adjust the URL for your exact version/arch
sudo mkdir -p /usr/pkg/etc
echo "https://cdn.NetBSD.org/pub/pkgsrc/packages/NetBSD/<arch>/<version>/All" \
| sudo tee /usr/pkg/etc/pkgin/repositories.conf
sudo pkg_add pkgin
sudo pkgin -y update
sudo pkgin -y install vim git htop sshrc
Replace <arch> (e.g., amd64, i386, evbarm) and <version> (e.g., 10.0). If you prefer building everything, pkgsrc bootstrap is an option — but pkgin is a great accelerator to start.
Cleanups, rebuilds, and gotchas
./build.sh cleandirif you want a clean slate.- Keep
-Oand-Tstable; moving them invalidates caches. - Snapshot your
-Ofilesystem (ZFS/Btrfs) before large releases for easy rollbacks. - Switching branches? Do
cleandir+toolsagain.
Minimal CI pipeline (cron + Makefile)
Automate nightly releases with a tiny Makefile and cron.
Makefile (save as /usr/src/ci/Makefile):
JOBS?=$(shell sysctl -n hw.ncpu 2>/dev/null || nproc)
OBJ?=/usr/obj
TOOL?=/usr/tools
ARCHS=amd64 i386 evbarmv7
all: $(ARCHS:%=build-%)
build-amd64:
\tcd /usr/src && ./build.sh -U -u -O $(OBJ) -T $(TOOL) -m amd64 -j$(JOBS) release
build-i386:
\tcd /usr/src && ./build.sh -U -u -O $(OBJ) -T $(TOOL) -m i386 -j$(JOBS) release
build-evbarmv7:
\tcd /usr/src && ./build.sh -U -u -O $(OBJ) -T $(TOOL) -m evbarm -a earmv7hf -j$(JOBS) release
Nightly cron (e.g., 02:30):
crontab -e
# add:
30 2 * * * make -C /usr/src/ci all >> /var/log/netbsd-build.log 2>&1
Tip: sync /usr/obj/releasedir/ to an internal web server and publish artifacts automatically.
Pocket checklists
Fast build (after the first time):
cd /usr/srcgit pull(or update CVS)./build.sh -U -u -O /usr/obj -T /usr/tools -m amd64 -j "$JOBS" release- Repeat for
i386andevbarm -a earmv7hf
ARM SD card:
- Locate the image under
.../binary/gzimg/ gzcat armv7.img.gz | dd of=<YOUR_DEVICE>syncand boot
Conclusion
With build.sh, NetBSD portability turns from a slogan into a daily workflow. In a few hours you can prepare toolchains and generate releases for different generations of x86 and also ARM boards — all from one box. From here, it’s easy to expand to evbmips, riscv, and beyond; the process barely changes, and watching three worlds boot from your single build is always satisfying.
Credits & notes
- Releasedir paths and filenames can vary by version/board; adjust as needed.
- This guide focuses on unprivileged builds; no host install (
destdir=/) required.
Optional cover/thumbnail
- Title: “NetBSD Cross-Build — amd64 • i386 • evbarm”
- Subtitle: “Portability in Practice with build.sh”
- Suggested size: 2048×1152
- Palette: NetBSD orange (#FF6600), charcoal (#222), white (#FAFAFA)
- Icon: stylized daemon head + gears
Leave a Reply