Skip to main content

max / alloy

6.5 KB · 133 lines History Blame Raw
1 # alloy-nvidia — Alloy plus the open NVIDIA kernel module, aarch64.
2 #
3 # Derived, not a build ARG on the shipped Containerfile. Max chose option (c)
4 # on alloy `2a382a75` (2026-08-30) and the reason is structural rather than
5 # stylistic: the kernel and the module come from the same digest by
6 # construction, so the kernel-lock failure the ARG option had is impossible
7 # here. There is no build-time assertion to write and no way for
8 # build/refresh-base-digests.sh to ship a machine that boots with no GPU and no
9 # signal, because moving the base digest cannot reach an image built from the
10 # old one.
11 #
12 # It also keeps the shipped image out of it. An aarch64 NVIDIA card is not a
13 # role almost all Alloy users have, and wiki `alloy-selection-discipline` says
14 # the shipped image should not serve one by default.
15 #
16 # WHAT ASTRA NEEDS THIS FOR: an RTX 5070 Ti, Blackwell GB203, which the
17 # proprietary module does not drive. `akmod-nvidia` on aarch64 IS the open
18 # build -- `modinfo` reports `license: Dual MIT/GPL`, where the proprietary one
19 # reports `NVIDIA`. There is no separate `akmod-nvidia-open` for this arch, so
20 # do not go looking for one.
21 #
22 # Usage: build/build-nvidia.sh. It resolves the digest, so this file names none.
23
24 ARG ALLOY_DIGEST
25 FROM localhost/alloy@${ALLOY_DIGEST}
26
27 # RPM Fusion nonfree, which is where the akmod lives. `--nogpgcheck` on the
28 # release package alone and nothing else: it is the package that installs the
29 # key every later transaction is then checked against, which is the one
30 # bootstrap that cannot itself be checked.
31 #
32 # `$releasever` resolves to 43 without help, because the base image already
33 # pins it in /etc/dnf/vars/releasever -- Alloy re-brands os-release, so dnf
34 # would otherwise expand it to Alloy's own VERSION_ID and ask for a nonexistent
35 # Fedora.
36 RUN dnf install -y --nogpgcheck \
37 "https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm" \
38 "https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm" \
39 && dnf clean all
40
41 # The akmod source and the userspace it pairs with.
42 #
43 # NOT `kmod-nvidia`, which exists prebuilt in the repo and is built against RPM
44 # Fusion's own kernel rather than this image's. Installing it would put a
45 # module in the image whose vermagic does not match the kernel beside it, which
46 # is the failure this whole derived shape exists to make impossible -- and it
47 # would look installed.
48 #
49 # Weak deps off, as everywhere else in this project.
50 RUN dnf install -y --setopt=install_weak_deps=False \
51 akmod-nvidia \
52 xorg-x11-drv-nvidia-cuda \
53 && dnf clean all
54
55 # Build the module against THIS image's kernel, now, at build time.
56 #
57 # akmods normally runs on the machine after a kernel update, which a bootc
58 # image does not do: the filesystem is immutable and the kernel only changes by
59 # taking a new image. So the build happens here, against the one kernel this
60 # image has, and the result ships inside it.
61 #
62 # The kernel is read out of /usr/lib/modules rather than passed in. There is
63 # exactly one, it came with the digest above, and a number written here by hand
64 # is the drift this file exists to avoid.
65 RUN set -eux; \
66 kernel="$(ls /usr/lib/modules | head -1)"; \
67 akmods --force --kernels "$kernel"; \
68 # The check, and it is the done condition. A build that produced nothing,
69 # or produced a module for a different kernel, fails here rather than at
70 # boot on a machine with no display.
71 module="/usr/lib/modules/$kernel/extra/nvidia/nvidia.ko.xz"; \
72 [ -f "$module" ] || module="/usr/lib/modules/$kernel/extra/nvidia/nvidia.ko"; \
73 test -f "$module"; \
74 vermagic="$(modinfo -F vermagic "$module" | awk '{print $1}')"; \
75 test "$vermagic" = "$kernel" \
76 || { echo "vermagic $vermagic is not this image's kernel $kernel" >&2; exit 1; }; \
77 # Open, not proprietary. Blackwell GB203 needs the open module, and the two
78 # are told apart by their licence: the open one is Dual MIT/GPL.
79 licence="$(modinfo -F license "$module")"; \
80 case "$licence" in \
81 *MIT*) : ;; \
82 *) echo "nvidia.ko reports licence '$licence'; expected the open Dual MIT/GPL build" >&2; exit 1 ;; \
83 esac; \
84 echo "built $module: vermagic $vermagic, licence $licence"
85
86 # The initramfs has to carry it, or the module is present and not loaded early
87 # enough to drive the console.
88 RUN set -eux; \
89 kernel="$(ls /usr/lib/modules | head -1)"; \
90 depmod -a "$kernel"; \
91 dracut --force --no-hostonly \
92 --kver "$kernel" \
93 "/usr/lib/modules/$kernel/initramfs.img"
94
95 # `nouveau` and the nvidia module cannot both drive the card, and nouveau wins
96 # by loading first. Blacklisted here rather than as a kernel argument so the
97 # fact travels with the image that needs it.
98 RUN printf 'blacklist nouveau\noptions nouveau modeset=0\n' \
99 > /etc/modprobe.d/alloy-nvidia-blacklist.conf
100
101 # The build host's leavings, on the same rule the shipped image applies to
102 # itself. This step is why: the derived image builds a kernel module, which
103 # means dnf, akmods and dracut all run here, and every one of them leaves
104 # something behind.
105 #
106 # It matters more here than it looks. astra installs and boots THIS image, not
107 # the Alloy underneath it, so a bar the shipped image enforces and this one does
108 # not is a bar that does not apply to the only machine that runs it. Measured
109 # 2026-09-04 on the first build that reached this far: /run/akmods, akmods and
110 # dnf logs, the libdnf5 and akmods caches, and dnf's countme files, all of them
111 # payload on a machine that will never run akmods again -- the module is built
112 # here precisely because the installed system cannot rebuild it.
113 #
114 # Everything the shipped image sweeps, plus the two akmods adds. authselect is
115 # not touched: this image runs no authselect, so its checksum is whatever the
116 # base already validated.
117 RUN set -eux; \
118 find /run /tmp -mindepth 1 -maxdepth 1 -exec rm -rf {} + 2>/dev/null || true; \
119 rm -f /var/log/dnf5.log*; \
120 rm -rf /var/lib/dnf /var/cache/libdnf5; \
121 rm -rf /var/cache/akmods /var/log/akmods; \
122 rm -f /var/cache/ldconfig/aux-cache; \
123 echo "sweep: build-host caches, logs and /run content removed"
124
125 # The same gate the shipped image ends on, and for the same reason.
126 #
127 # `bootc container lint` alone stood here and reported three warnings that
128 # nothing read, which is the exact failure the shipped Containerfile records
129 # fixing on 2026-08-26: a detector whose output reaches no one is not a control.
130 # A derived image is not a reason to keep a weaker gate -- it is the image that
131 # boots.
132 RUN bootc container lint --fatal-warnings --no-truncate
133