Skip to content

Commit 4076c9c

Browse files
committed
Add dedicated bootc planner for container image builds
bootc container images are built without a running systemd and with a restricted filesystem layout: /var, /home, and /root do not exist during the build. The ostree planner assumes all of these are available, so it cannot be used for bootc image builds. Introduce a bootc planner that understands the full bootc lifecycle: Nix is installed into /nix at build time, moved to /usr/lib/nix for the image layer, and on first boot systemd-tmpfiles seeds /var/lib/nix while a bind-mount exposes it at /nix. sysusers.d ensures build users survive across boots since /etc/passwd entries from the build may not persist. Auto-detection checks for /usr/bin/bootc before the ostree check since bootc images are also ostree-based and would otherwise match the wrong planner. Closes: #155
1 parent 6f760b1 commit 4076c9c

File tree

7 files changed

+478
-57
lines changed

7 files changed

+478
-57
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ jobs:
313313
distro:
314314
- ubuntu-v22_04
315315
- ubuntu-v24_04
316+
- fedora-v42-bootc
316317
steps:
317318
- name: Checkout repository
318319
uses: actions/checkout@v6

README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ curl -sSfL https://artifacts.nixos.org/nix-installer | sh -s -- install
2424
| -------------------------------------------------------------------- | :---------------: | :---------: | :---------------: |
2525
| Linux (`x86_64` and `aarch64`) | ✓ (via [systemd]) || Stable |
2626
| MacOS (`x86_64` and `aarch64`) || | Stable (see note) |
27+
| ostree (Fedora Silverblue, etc.) | ✓ (via [systemd]) || Stable |
28+
| [bootc] container images | ✓ (via [systemd]) || Stable |
2729
| [Valve Steam Deck][steam-deck] (SteamOS) || | Stable |
2830
| [Windows Subsystem for Linux][wsl] 2 (WSL2) (`x86_64` and `aarch64`) | ✓ (via [systemd]) || Stable |
2931
| [Podman] Linux containers | ✓ (via [systemd]) || Stable |
@@ -205,6 +207,48 @@ podman rmi $IMAGE
205207
With some container tools, such as [Docker], you can omit `sandbox = false`.
206208
Omitting this will negatively impact compatibility with container tools like [Podman].
207209

210+
### On ostree-based desktops (Fedora Silverblue, etc.)
211+
212+
Immutable Linux distributions based on [ostree](https://ostreedev.github.io/ostree/) have a
213+
read-only root filesystem, so `/nix` cannot be created directly. The `ostree` planner handles
214+
this by setting up a bind mount from a persistence directory (`/var/home/nix` by default) to
215+
`/nix` via systemd units.
216+
217+
On a running ostree system the installer auto-detects the right planner, so the default command
218+
works:
219+
220+
```shell
221+
curl -sSfL https://artifacts.nixos.org/nix-installer | sh -s -- install
222+
```
223+
224+
### In bootc container image builds
225+
226+
[bootc] container images are also ostree-based, but they are built without a running systemd
227+
and without `/var` (it is created on first boot). This means the ostree planner cannot be used
228+
during container builds. The dedicated `bootc` planner handles this lifecycle:
229+
230+
1. **At build time**, Nix is installed into `/nix` which becomes part of the image layer.
231+
Systemd units, [sysusers.d](https://www.freedesktop.org/software/systemd/man/sysusers.d.html),
232+
and [tmpfiles.d](https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html) configs
233+
are written but no daemons are started.
234+
2. **On first boot**, `systemd-tmpfiles` copies `/nix` to `/var/lib/nix` (mutable storage),
235+
`systemd-sysusers` recreates the build users, and a bind-mount makes `/var/lib/nix`
236+
available at `/nix`. The Nix daemon starts automatically.
237+
238+
The `bootc` planner is auto-detected when `/usr/bin/bootc` is present, or can be selected
239+
explicitly:
240+
241+
```dockerfile
242+
# Containerfile
243+
FROM quay.io/fedora/fedora-bootc:42
244+
RUN curl -sSfL https://artifacts.nixos.org/nix-installer | sh -s -- install bootc \
245+
--extra-conf "sandbox = false" --no-confirm
246+
```
247+
248+
> [!NOTE]
249+
> Uninstall is not supported on bootc systems. To remove Nix, rebuild the container image
250+
> without the `nix-installer install bootc` step.
251+
208252
### In GitHub Actions
209253

210254
[The nix installer action repository](https://github.com/NixOS/nix-installer-action/) provides a GitHub Action for installing Nix in CI workflows.
@@ -421,6 +465,7 @@ nix-installer uninstall /path/to/receipt.json
421465
`nix-installer self-test` only takes [general settings](#general-settings).
422466

423467
[actions]: https://github.com/features/actions
468+
[bootc]: https://containers.github.io/bootc/
424469
[docker]: https://docker.com
425470
[enabling-systemd]: https://devblogs.microsoft.com/commandline/systemd-support-is-now-available-in-wsl/#how-can-you-get-systemd-on-your-machine
426471
[flakes]: https://zero-to-nix.com/concepts/flakes
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
FROM default
2+
COPY nix-installer /nix-installer
3+
RUN chmod +x /nix-installer/bin/nix-installer
4+
# In a container build there is no systemd, so we use the bootc planner which
5+
# installs Nix into /nix (part of the image layer). On first boot,
6+
# systemd-tmpfiles copies it to /var/lib/nix and a bind-mount exposes it.
7+
RUN /nix-installer/bin/nix-installer install bootc --logger pretty --log-directive nix_installer=trace --extra-conf "sandbox = false" --no-confirm -vvv
8+
ENV PATH="${PATH}:/nix/var/nix/profiles/default/bin"
9+
RUN nix-build --no-substitute -E 'derivation { name = "foo"; system = "x86_64-linux"; builder = "/bin/sh"; args = ["-c" "echo foobar > $out"]; }'
10+
# Verify build-time artifacts:
11+
RUN test -d /nix/store || { echo "FAIL: /nix/store does not exist"; exit 1; }
12+
RUN test -f /etc/sysusers.d/nix-installer.conf || { echo "FAIL: /etc/sysusers.d/nix-installer.conf missing"; exit 1; }
13+
RUN test -f /etc/tmpfiles.d/nix-installer.conf || { echo "FAIL: /etc/tmpfiles.d/nix-installer.conf missing"; exit 1; }
14+
RUN grep -q 'After=systemd-tmpfiles-setup.service' /etc/systemd/system/nix.mount || { echo "FAIL: nix.mount missing After=systemd-tmpfiles-setup.service"; cat /etc/systemd/system/nix.mount; exit 1; }

nix/tests/container-test/default.nix

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,35 @@ let
2424
tester = ./default/Dockerfile;
2525
system = "x86_64-linux";
2626
};
27+
28+
# Fedora 42 WSL rootfs – used for the bootc planner container test
29+
# (no systemd) reproducing
30+
# https://github.com/NixOS/nix-installer/issues/155
31+
# Re-compressed from .tar.xz to .tar.zst because podman import is
32+
# extremely slow at xz decompression inside a VM.
33+
"fedora-v42-bootc" =
34+
let
35+
xzTarball = builtins.fetchurl {
36+
url = "https://dl.fedoraproject.org/pub/fedora/linux/releases/42/Container/x86_64/images/Fedora-WSL-Base-42-1.1.x86_64.tar.xz";
37+
sha256 = "138vibdf0qcln3r0f116qvmq5vx8im9cy0xv2ml7r8ccsw2kvywr";
38+
};
39+
pkgs = forSystem "x86_64-linux" ({ pkgs, ... }: pkgs);
40+
in
41+
{
42+
tarball =
43+
pkgs.runCommand "fedora-42-rootfs.tar.zst"
44+
{
45+
nativeBuildInputs = with pkgs; [
46+
xz
47+
zstd
48+
];
49+
}
50+
''
51+
xz -dc ${xzTarball} | zstd -o $out
52+
'';
53+
tester = ./bootc/Dockerfile;
54+
system = "x86_64-linux";
55+
};
2756
};
2857

2958
makeTest =

0 commit comments

Comments
 (0)