Skip to content

composefs: Support transient /etc, transient root, and volatile /var #2201

Open
cgwalters wants to merge 6 commits into
bootc-dev:mainfrom
cgwalters:cfs-transient-root
Open

composefs: Support transient /etc, transient root, and volatile /var #2201
cgwalters wants to merge 6 commits into
bootc-dev:mainfrom
cgwalters:cfs-transient-root

Conversation

@cgwalters
Copy link
Copy Markdown
Collaborator

While working on the sealed images, I think having /etc transient is definitely something we want to encourage. I hit composefs/composefs-rs#287 right away, and this fixes that.

For other cases, we'll want to also support a transient / and to do that is a lot more complicated because of SELinux - right now we need a custom early overlay service.

For /var I don't think we want to encourage use of overlayfs because it breaks things like podman/docker and any usage of overlayfs in general there. So we now document (and better support) systemd.volatile=state for this.

cgwalters added 2 commits May 15, 2026 13:44
…eged processes

The overlayfs merged view inherits its root permissions from the upperdir.
When upper/ was created with 0700 (the same mode passed for work/), the
merged / appeared as drwx------ to all non-root processes, causing dbus,
systemd units that drop privileges, and anything using DAC to fail with
EACCES immediately after switch-root.

Fix: create upper/ with 0755 so the merged root is world-traversable.
work/ remains 0700 — it is kernel-internal and never exposed in the merged
view, so tighter permissions there are harmless.

This mirrors what systemd does in volatile-root.c and nspawn-mount.c, and
fixes the issue reported in composefs-rs#287.

Assisted-by: OpenCode (claude-sonnet-4-6@default)
Signed-off-by: Colin Walters <walters@verbum.org>
Image authors who ship /usr/lib/composefs/setup-root-conf.toml to configure
composefs mount behaviour (e.g. transient /etc) previously had to add
explicit --include flags to every dracut invocation in their Containerfile.

Teach module-setup.sh to install the file automatically when present,
mirroring what the composefs-rs dracut modules do. Use '[[ -e ]] &&
inst_simple' rather than inst_if_exists: the latter is not always available
when dracut is invoked explicitly with --force in a Containerfile RUN layer
(outside of kernel-install's dracut wrapper).

Assisted-by: OpenCode (claude-sonnet-4-6@default)
Signed-off-by: Colin Walters <walters@verbum.org>
@github-actions github-actions Bot added the area/documentation Updates to the documentation label May 15, 2026
@bootc-bot bootc-bot Bot requested a review from jeckersb May 15, 2026 17:48
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for transient and volatile configurations for the root filesystem, /etc, and /var when using the composefs backend. Key changes include the addition of a setup-root-conf.toml configuration file, a new bootc-early-overlay-relabel.service to handle SELinux labels on transient overlays, and updated initramfs logic to support these mount types. The PR also adds comprehensive documentation and new test suites for these features. Feedback suggests explicitly importing libc or using rustix constants in the generator to ensure better portability and clarity.

Comment thread crates/lib/src/generator.rs
cgwalters added 4 commits May 15, 2026 14:15
overlay_transient() now returns a detached fsmount fd rather than
immediately attaching it, letting the caller decide where to place the
overlay. This is a correctness fix: on pre-6.15 kernels, the old code
mounted the overlay then continued using the original composefs dirfd for
subsequent submounts, which meant /etc and /var landed in the hidden lower
layer rather than the visible merged view.

The overlay source name now embeds the composefs digest as
"transient:composefs=<hash>" so that composefs_booted() can extract the
digest from the mount source after switch-root, the same way it does for
the normal "composefs:<hash>" source.

overlay_state() also loses its unused _mode parameter.

Assisted-by: OpenCode (claude-sonnet-4-6@default)
Signed-off-by: Colin Walters <walters@verbum.org>
When root.transient = true, bootc-root-setup wraps the composefs lower
in an overlayfs whose source is "transient:composefs=<hash>" rather than
"composefs:<hash>". Handle both prefixes uniformly so that
composefs_booted() works correctly on transient root boots and soft-reboots
are detected the same way in both cases.

Assisted-by: OpenCode (Claude Sonnet 4.6)
Signed-off-by: Colin Walters <walters@verbum.org>
…nux fix

Transient overlays (/) inherit tmpfs_t from the upper dir's tmpfs via
fs_use_trans at SELinux policy-load time. Add a generator-emitted oneshot
unit, bootc-early-overlay-relabel.service, that runs
'bootc internals relabel-overlay-mountpoints' before sysinit.target to
restore the correct label on each writable overlayfs mount point.

Two detection paths, both needed because the generator runs before
local-fs.target:

- Root writability: inspect the mount source for the
  "transient:composefs=" prefix to detect a transient root overlay.

- Subdir mounts (/etc): bootc-root-setup.service mounts these after the
  generator, so we read setup-root-conf.toml directly from the booted
  image to know whether /etc will be a transient overlay.

The detection block runs before the OSTREE_BOOTED guard: native composefs
boots do not write /run/ostree-booted, but still need the relabel unit.

relabel_overlay_mountpoints() checks both OVERLAYFS_SUPER_MAGIC and
!RDONLY to distinguish writable transient overlays from the read-only
composefs root (both are overlayfs, only the former needs relabelling).

Assisted-by: OpenCode (claude-sonnet-4-6@default)
Signed-off-by: Colin Walters <walters@verbum.org>
Add TOML configuration (setup-root-conf.toml) for composefs mount
behaviour:

- [root] transient = true: wrap the composefs in a tmpfs overlay; all
  writes are discarded on reboot.
- [etc] mount = transient|overlay|bind|none: control how /etc is mounted
  from the deployment state directory.
- [var] mount = none|bind: control whether /var is bind-mounted from
  state. When mount = none, /var is left as an empty composefs directory.

bootc-root-setup also detects the systemd.volatile=state kernel argument
at boot time and automatically skips the /var state bind-mount when it is
set, leaving /var empty for systemd-fstab-generator to mount a fresh tmpfs
there at local-fs.target. This is the recommended way to get an ephemeral
/var: it uses a plain tmpfs rather than overlayfs, which is compatible with
tools like podman that use overlayfs under /var/lib/containers.

Add inject-baseconfig CI helper, a test-baseconfigs CI job, and a
040-test-baseconfigs.nu integration test that boots each configuration in a
VM and validates filesystem types, writability, SELinux labels, and podman
graph driver compatibility.

Assisted-by: OpenCode (claude-sonnet-4-6@default)
Signed-off-by: Colin Walters <walters@verbum.org>
@cgwalters cgwalters force-pushed the cfs-transient-root branch from 9ec1a58 to 73ae51e Compare May 15, 2026 18:17
@cgwalters cgwalters added the ci/merge Run full CI suite (all OSes) — equivalent to merge queue label May 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/documentation Updates to the documentation ci/merge Run full CI suite (all OSes) — equivalent to merge queue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant