Containerfile
⎇
Raw
1# The build VM image. Hearthforge starts one container of it per build_image
2# step. The container runs QEMU, and the VM inside runs buildah. See CI.md.
3# Hearthforge embeds this directory and builds the image on first use with
4# the engine's classic build API, so it must not need BuildKit.
5
6ARG ALPINE=docker.io/library/alpine:3.24
7
8# The system that boots inside the VM.
9FROM ${ALPINE} AS guest
10RUN apk add --no-cache buildah crun netavark e2fsprogs tar ca-certificates
11COPY guest/ /
12# The embedded copy loses file modes.
13RUN chmod 755 /init
14
15# --no-scripts skips the mkinitfs trigger. Its initramfs is not used.
16FROM ${ALPINE} AS kernel
17RUN apk add --no-cache --no-scripts linux-virt kmod
18# Modules are stored uncompressed and numbered in load order, so the guest
19# init needs only busybox insmod.
20RUN set -eu; \
21 kver=$(ls /lib/modules); \
22 mkdir -p /out/modules; \
23 for m in virtio_blk virtio_net ext4 overlay virtio_rng; do \
24 modprobe -S "$kver" --show-depends "$m"; \
25 done | awk '$1 == "insmod" && !seen[$2]++ { print $2 }' > /tmp/modules; \
26 i=0; \
27 while read -r ko; do \
28 i=$((i + 1)); \
29 gunzip -c "$ko" > "/out/modules/$(printf %02d $i)-$(basename "$ko" .gz)"; \
30 done < /tmp/modules; \
31 cp /boot/vmlinuz-virt /out/vmlinuz
32
33FROM ${ALPINE} AS initramfs
34RUN apk add --no-cache cpio
35COPY --from=guest / /rootfs/
36COPY --from=kernel /out/modules/ /rootfs/lib/hf-modules/
37RUN set -eu; \
38 cd /rootfs; \
39 mkdir -p proc sys dev tmp run var/lib/containers; \
40 find . -print0 | cpio --null --quiet -o -H newc | gzip -1 > /initramfs.gz
41
42FROM ${ALPINE}
43RUN apk add --no-cache "qemu-system-$(apk --print-arch)" tar
44COPY --from=kernel /out/vmlinuz /vm/vmlinuz
45COPY --from=initramfs /initramfs.gz /vm/initramfs.gz
46COPY run-vm /usr/local/bin/run-vm
47RUN chmod 755 /usr/local/bin/run-vm && mkdir -p /in/context /out /work
48ENTRYPOINT ["/usr/local/bin/run-vm"]
49