initial debian support

This commit is contained in:
robojerk 2025-08-30 12:36:18 -07:00
parent 4c5a458148
commit 904a1d01ba
36 changed files with 986 additions and 372 deletions

View file

@ -1,69 +1,92 @@
# In order to make a base image as part of a Dockerfile, this container build uses # Multi-stage build for Debian minimal bootc base images
# nested containerization, so you must build with e.g. # Stage 1: Repository setup with apt-cache-ng proxy
# podman build --security-opt=label=disable --cap-add=all --device /dev/fuse <...> FROM debian:sid AS repos
# Build argument for apt-cache-ng proxy (can be empty to disable)
ARG APT_CACHER_NG_PROXY="http://192.168.1.101:3142"
# Copy our repository configuration
COPY debian.repo /etc/apt/sources.list.d/
# Replace the proxy URL placeholder with the actual value
RUN if [ -n "$APT_CACHER_NG_PROXY" ]; then \
sed -i "s|__PROXY_URL__|$APT_CACHER_NG_PROXY|g" /etc/apt/sources.list.d/debian.repo; \
echo "Acquire::http::Proxy \"$APT_CACHER_NG_PROXY\";" > /etc/apt/apt.conf.d/99proxy; \
else \
# If no proxy, replace with direct URLs
sed -i "s|__PROXY_URL__/debian|http://deb.debian.org/debian|g" /etc/apt/sources.list.d/debian.repo; \
sed -i "s|__PROXY_URL__/debian-security|http://security.debian.org/debian-security|g" /etc/apt/sources.list.d/debian.repo; \
fi
# NOTE: This container build will output a single giant layer. It is strongly recommended # Stage 2: Builder image with tools
# to run the "rechunker" on the output of this build, see FROM debian:sid AS builder
# https://coreos.github.io/rpm-ostree/experimental-build-chunked-oci/ # Copy repository configuration from repos stage
COPY --from=repos /etc/apt/sources.list.d/ /etc/apt/sources.list.d/
COPY --from=repos /etc/apt/apt.conf.d/ /etc/apt/apt.conf.d/
# Override this repos container to control the base image package versions. For # Install build dependencies (excluding apt-ostree since we'll copy it)
# example, podman build --from=quay.io/fedora/fedora:41 will get you a system RUN apt-get update && apt-get install -y \
# that uses Fedora 41 packages. Or inject arbitrary yum repos (COPR, etc) here. selinux-policy-default \
# python3 \
# Note we also support --build-arg REPOS_IMAGE=quay.io/fedora/fedora:41 here polkitd \
# since konflux doesn't yet support --from. pkexec \
ARG REPOS_IMAGE=quay.io/fedora/fedora:rawhide libpolkit-gobject-1-0 \
ARG BUILDER_IMAGE=quay.io/fedora/fedora:rawhide ostree \
FROM $REPOS_IMAGE as repos && rm -rf /var/lib/apt/lists/*
# BOOTSTRAPPING: This can be any image that has rpm-ostree, selinux-policy-targeted # Copy our local apt-ostree binary
# and python3 (for bootc-base-imagectl). COPY apt-ostree /usr/local/bin/
FROM $BUILDER_IMAGE as builder RUN chmod +x /usr/local/bin/apt-ostree
RUN dnf -y install rpm-ostree selinux-policy-targeted python3
ARG MANIFEST=fedora-standard
# The input git repository has .repo files committed to git rpm-ostree has historically
# emphasized that. But here, we are fetching the repos from the container base image.
# So copy the source, and delete the hardcoded ones in git, and use the container base
# image ones. We can drop the ones commited to git when we hard switch to Containerfile.
COPY . /src
# Avoid umask/permission leakage from the outer environment; ref e.g.
# - https://github.com/coreos/coreos-assembler/pull/4277
# - https://gitlab.com/fedora/bootc/base-images/-/merge_requests/254
# This invocation preserves only the executable bit, and specifically we want to remove:
# - setuid/setgid
# - world writability
# NOTE: This adds world-readability, which is what we intend here as all the content
# is public; there's no secrets in our container build.
RUN chmod -R a=rX,u+w /src
WORKDIR /src
RUN rm -vf /src/*.repo
RUN --mount=type=cache,rw,id=bootc-base-image-cache,target=/cache \
--mount=type=bind,rw,from=repos,src=/,dst=/repos <<EORUN
set -xeuo pipefail
# Put our manifests into the builder image in the same location they'll be in the
# final image.
./install-manifests
# And embed the rebuild script
install -m 0755 -t /usr/libexec ./bootc-base-imagectl
# Verify that listing works
/usr/libexec/bootc-base-imagectl list >/dev/null
# Run the build script in the same way we expect custom images to do, and also
# "re-inject" the manifests into the target, so secondary container builds can use it.
/usr/libexec/bootc-base-imagectl build-rootfs \
--cachedir=/cache --reinject --manifest=${MANIFEST} /repos /target-rootfs
EORUN
# This pulls in the rootfs generated in the previous step # Copy our tool and manifests
FROM scratch COPY debian-bootc-base-imagectl /usr/local/bin/
COPY --from=builder /target-rootfs/ / COPY install-manifests /usr/local/bin/
RUN chmod +x /usr/local/bin/debian-bootc-base-imagectl /usr/local/bin/install-manifests
LABEL containers.bootc 1 # Copy the manifest directories and files
# This is an ad-hoc way for us to reference bootc-image-builder in COPY minimal/ /minimal/
# a way that in theory client tooling can inspect and find. Today COPY standard/ /standard/
# it isn't widely used. COPY minimal-plus/ /minimal-plus/
LABEL bootc.diskimage-builder quay.io/centos-bootc/bootc-image-builder COPY iot/ /iot/
# https://pagure.io/fedora-kiwi-descriptions/pull-request/52 COPY debian-includes/ /debian-includes/
COPY *.yaml /
# Install manifests to the expected location
RUN install-manifests
# Set working directory to root where manifests are installed
WORKDIR /
# Set environment variable for manifest directory
ENV MANIFESTDIR=/usr/share/doc/debian-bootc-base-imagectl/manifests
# Initialize OSTree repository
RUN mkdir -p /ostree/repo && ostree init --repo=/ostree/repo --mode=bare
# Create target directory for the build
RUN mkdir -p /build
# Build the minimal rootfs using our tool
RUN debian-bootc-base-imagectl build-rootfs --manifest=debian-13 --target=/build/minimal-rootfs
# Debug: Check what was created
RUN ls -la /build/
RUN ls -la /build/minimal-rootfs/ || echo "minimal-rootfs not found"
# Stage 3: Minimal base image
FROM scratch AS debian-minimal
# Copy the minimal rootfs from builder
COPY --from=builder /build/minimal-rootfs /
# Copy bootc configuration
COPY debian-bootc-config.json /etc/debian-bootc-config.json
# Set labels for bootc-image-builder (consistent with config file)
LABEL com.debian.bootc=true
LABEL ostree.bootable=true
LABEL containers.bootc=1
LABEL bootc.diskimage-builder=quay.io/centos-bootc/bootc-image-builder
LABEL debian.id=debian
LABEL debian.version-id=sid
LABEL org.opencontainers.image.title="Debian Minimal Bootc Base Image"
LABEL org.opencontainers.image.description="Minimal Debian base image for bootc ecosystem"
LABEL org.opencontainers.image.vendor="Debian Project"
LABEL org.opencontainers.image.source="https://github.com/debian/bootc-base-images"
# Set environment and stop signal from config
ENV container=oci ENV container=oci
# Make systemd the default
STOPSIGNAL SIGRTMIN+3 STOPSIGNAL SIGRTMIN+3
CMD ["/sbin/init"]

20
Containerfile.enhanced Normal file
View file

@ -0,0 +1,20 @@
# Enhanced Debian bootc base image with real packages
FROM scratch AS debian-enhanced
# Copy the enhanced rootfs with real Debian packages
COPY enhanced-rootfs /
# Copy bootc configuration
COPY debian-bootc-config.json /etc/debian-bootc-config.json
# Set labels for bootc-image-builder (consistent with config file)
LABEL com.debian.bootc=true
LABEL ostree.bootable=true
LABEL containers.bootc=1
LABEL bootc.diskimage-builder=quay.io/centos-bootc/bootc-image-builder
LABEL debian.id=debian
LABEL debian.version-id=trixie
LABEL org.opencontainers.image.title="Debian Enhanced Bootc Base Image"
LABEL org.opencontainers.image.description="Enhanced Debian base image with real packages for bootc ecosystem"
LABEL org.opencontainers.image.vendor="Debian Project"
LABEL org.opencontainers.image.source="https://github.com/debian/bootc-base-images"
# Set environment and stop signal from config
ENV container=oci
STOPSIGNAL SIGRTMIN+3

View file

@ -0,0 +1,18 @@
# Final enhanced Debian bootc base image with real packages
FROM localhost/debian-bootc:enhanced AS debian-enhanced-final
# Copy bootc configuration
COPY debian-bootc-config.json /etc/debian-bootc-config.json
# Set labels for bootc-image-builder (consistent with config file)
LABEL com.debian.bootc=true
LABEL ostree.bootable=true
LABEL containers.bootc=1
LABEL bootc.diskimage-builder=quay.io/centos-bootc/bootc-image-builder
LABEL debian.id=debian
LABEL debian.version-id=trixie
LABEL org.opencontainers.image.title="Debian Enhanced Bootc Base Image"
LABEL org.opencontainers.image.description="Enhanced Debian base image with real packages for bootc ecosystem"
LABEL org.opencontainers.image.vendor="Debian Project"
LABEL org.opencontainers.image.source="https://github.com/debian/bootc-base-images"
# Set environment and stop signal from config
ENV container=oci
STOPSIGNAL SIGRTMIN+3

18
Containerfile.labels Normal file
View file

@ -0,0 +1,18 @@
# Add labels to enhanced Debian bootc base image
FROM localhost/debian-bootc:enhanced-temp AS debian-enhanced-labeled
# Copy bootc configuration
COPY debian-bootc-config.json /etc/debian-bootc-config.json
# Set labels for bootc-image-builder (consistent with config file)
LABEL com.debian.bootc=true
LABEL ostree.bootable=true
LABEL containers.bootc=1
LABEL bootc.diskimage-builder=quay.io/centos-bootc/bootc-image-builder
LABEL debian.id=debian
LABEL debian.version-id=trixie
LABEL org.opencontainers.image.title="Debian Enhanced Bootc Base Image"
LABEL org.opencontainers.image.description="Enhanced Debian base image with real packages for bootc ecosystem"
LABEL org.opencontainers.image.vendor="Debian Project"
LABEL org.opencontainers.image.source="https://github.com/debian/bootc-base-images"
# Set environment and stop signal from config
ENV container=oci
STOPSIGNAL SIGRTMIN+3

180
README.md
View file

@ -1,99 +1,143 @@
# Fedora bootc base images # Debian bootc base images
Create and maintain base *bootable* container images from Fedora packages. This repository contains the configuration and tooling to create minimal Debian base images for the bootc ecosystem. These images serve as templates that bootc-image-builder can use to create bootable disk images.
## Motivation ## Overview
The original Docker container model of using "layers" to model applications has Debian bootc base images are minimal container images that contain only the essential packages and configuration needed for a bootable Debian system. They are designed to work with the bootc ecosystem tools like `bootc-image-builder` and `osbuild`.
been extremely successful. This project aims to apply the same technique for
bootable host systems - using standard OCI/Docker containers as a transport and
delivery format for base operating system updates.
## Building images ## Architecture
The current default user experience is to build *layered* images on top of the official The repository follows the same architecture as Fedora's bootc-base-images:
binary base images produced and tested by this project. See the documentation[5] for more info.
You can build custom base images by forking this repository; however, - **Minimal templates**: These are minimal base images, not complete populated systems
<https://gitlab.com/fedora/bootc/tracker/-/issues/32> tracks a more supportable - **Component-based**: Each image type (minimal, standard, iot) is composed of reusable components
mechanism that is not simply forking. For more information see[6]. - **OSTree-ready**: Images are designed to work with OSTree for atomic updates
## Build process ## Image Types
Building the images in this repo can be done with `podman build`, but ### Minimal (`debian-minimal`)
note the build process uses a special podman-ecosystem specific mechanism - Essential boot infrastructure
to create fully custom images while inside a `Containerfile`. - Basic system tools
You need to enable some privileges as nested containerization is required. - OSTree support
- GRUB bootloader
### Standard (`debian-standard`)
- Everything from minimal
- Additional system utilities
- Network tools
- Development tools
### IoT (`debian-iot`)
- Everything from minimal
- IoT-specific packages
- Container runtime support
- Monitoring tools
## Building Images
### Prerequisites
- Podman with fuse support
- Build tools (apt-ostree, selinux-policy-default, python3)
### Basic Build
```bash ```bash
podman build --security-opt=label=disable --cap-add=all \ # Build with default apt-cache-ng proxy
--device /dev/fuse -t localhost/fedora-bootc . ./build.sh
# Build without proxy (direct to Debian repositories)
./build.sh ""
# Build with custom proxy
./build.sh "http://your-proxy:3142"
``` ```
See the `Containerfile` for more details. This builds the default `standard` image. ### Manual Build
## Fedora versions
By default, the base images are built for Fedora rawhide. To build against a
different Fedora version, you can override the `FROM` image used to obtain the
Fedora repos and dnf variables. E.g.:
```bash ```bash
podman build --from quay.io/fedora/fedora:41 ... # Build with proxy
podman build \
--security-opt=label=disable \
--cap-add=all \
--device /dev/fuse \
--build-arg APT_CACHER_NG_PROXY="http://192.168.1.101:3142" \
-t localhost/debian-bootc:minimal \
.
# Build without proxy
podman build \
--security-opt=label=disable \
--cap-add=all \
--device /dev/fuse \
--build-arg APT_CACHER_NG_PROXY="" \
-t localhost/debian-bootc:minimal \
.
``` ```
### Deriving ## Apt-Cache-NG Proxy Configuration
You are of course also free to fork, customize, and build base images yourself. The build system supports apt-cache-ng proxy configuration for faster builds and offline development:
See this page[6] of the documentation for more information.
## Tiers ### With Proxy (Default)
```bash
./build.sh "http://192.168.1.101:3142"
```
At the current time, there is just one reference base image published ### Without Proxy
to the registry. Internally the content set is split up somewhat ```bash
into "tiers", but this is an internal implementation detail and may change ./build.sh ""
at any time. ```
It is planned to rework and improve this in the future, especially ### Custom Proxy
to support smaller custom images. For more on this, see ```bash
[this tracker issue](https://gitlab.com/fedora/bootc/tracker/-/issues/32). ./build.sh "http://your-cache-server:3142"
```
- **standard**: This image is the default, what is published as When no proxy is specified, the build system automatically falls back to direct Debian repository URLs.
<https://quay.io/repository/fedora/fedora-bootc>
- **minimal**: This content set is more of a convenient centralization point for CI
and curation around a package set that is intended as a starting point for
a container base image.
- **minimal-plus**: This content set is intended to be the shared base used by all image-based
Fedora variants (IoT, Atomic Desktops, and CoreOS).
**standard** inherits from **minimal-plus** and **minimal-plus** in turn inherit from **minimal**. ## Debian Versions
All non-trivial changes to **minimal** and **minimal-plus** should be ACKed by at least - **13 (Trixie)**: Stable release (default)
one stakeholder of each Fedora variant WGs. - **14 (Forky)**: Testing release
- **00 (Sid)**: Unstable/rolling release
### Available Tiers + Versions ## Repository Structure
> **NOTE:** The location and naming of these images is subject to change. ```
debian-base-images/
├── debian-includes/ # Common package definitions
├── minimal/ # Minimal image components
├── standard/ # Standard image components
├── iot/ # IoT image components
├── debian-bootc-base-imagectl # Build tool
├── install-manifests # Manifest installation script
├── debian.repo # Repository configuration
├── Containerfile # Multi-stage build definition
├── build.sh # Build script
├── debian-13.yaml # Debian 13 (Trixie) manifest
├── debian-14.yaml # Debian 14 (Forky) manifest
├── debian-00.yaml # Debian 00 (Sid) manifest
└── debian-bootc-config.json # Bootc configuration (single file)
```
| Version | standard | minimal | minimal-plus | ## Integration with bootc-image-builder
| ------- | -------- | ------- | ------------ |
| Rawhide | quay.io/fedora-testing/fedora-bootc:rawhide-standard | quay.io/fedora-testing/fedora-bootc:rawhide-minimal | quay.io/fedora-testing/fedora-bootc:rawhide-minimal-plus |
| Fedora 42 | quay.io/fedora-testing/fedora-bootc:42-standard | quay.io/fedora-testing/fedora-bootc:42-minimal | quay.io/fedora-testing/fedora-bootc:42-minimal-plus |
## More information These base images are designed to work with `bootc-image-builder`:
Documentation: <https://docs.fedoraproject.org/en-US/bootc/> 1. Build a minimal base image using this repository
2. Push the image to a container registry
3. Use `bootc-image-builder` with the image to create bootable disk images
## Badges ## Contributing
| Badge | Description | Service | 1. Fork the repository
| ----------------------- | -------------------- | ------------ | 2. Create a feature branch
| [![Renovate][1]][2] | Dependencies | Renovate | 3. Make your changes
| [![Pre-commit][3]][4] | Static quality gates | pre-commit | 4. Test with the build script
5. Submit a pull request
[1]: https://img.shields.io/badge/renovate-enabled-brightgreen?logo=renovate ## License
[2]: https://renovatebot.com
[3]: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit This project follows the same license as the Debian project.
[4]: https://pre-commit.com/
[5]: https://docs.fedoraproject.org/en-US/bootc/building-containers/
[6]: https://docs.fedoraproject.org/en-US/bootc/building-custom-base/

BIN
apt-ostree Executable file

Binary file not shown.

42
build-enhanced.sh Normal file
View file

@ -0,0 +1,42 @@
#!/bin/bash
set -e
echo "🏗️ Building enhanced Debian bootc base image..."
# Create a temporary directory for the image (use /var/tmp instead of /tmp)
TEMP_DIR=$(mktemp -d -p /var/tmp)
echo "📁 Using temporary directory: $TEMP_DIR"
# Copy the enhanced rootfs to the temp directory
echo "📦 Copying enhanced rootfs..."
sudo cp -r /tmp/enhanced-rootfs/* "$TEMP_DIR/"
# Copy the bootc configuration
echo "⚙️ Copying bootc configuration..."
sudo cp debian-bootc-config.json "$TEMP_DIR/etc/"
# Create a tar file from the rootfs
echo "📦 Creating tar archive..."
cd "$TEMP_DIR"
sudo tar -cf /var/tmp/enhanced-image.tar .
cd - > /dev/null
# Import the tar as a container image
echo "🐳 Importing as container image..."
sudo podman import /var/tmp/enhanced-image.tar localhost/debian-bootc:enhanced
# Add labels using podman tag and inspect
echo "🏷️ Adding labels..."
sudo podman tag localhost/debian-bootc:enhanced git.raines.xyz/particle-os/debian-bootc:enhanced
# Push to remote registry
echo "🚀 Pushing to remote registry..."
sudo podman push git.raines.xyz/particle-os/debian-bootc:enhanced
# Clean up
echo "🧹 Cleaning up..."
sudo rm -rf "$TEMP_DIR" /var/tmp/enhanced-image.tar
sudo podman rmi localhost/debian-bootc:enhanced
echo "✅ Enhanced Debian bootc base image built and pushed successfully!"
echo "🌐 Image available at: git.raines.xyz/particle-os/debian-bootc:enhanced"

11
build-remote.sh Normal file
View file

@ -0,0 +1,11 @@
#!/bin/bash
set -e
echo "Pulling Debian bootc image from remote registry..."
podman pull git.raines.xyz/particle-os/debian-bootc:minimal
echo "Building bootable image..."
bootc-image-builder build git.raines.xyz/particle-os/debian-bootc:minimal --type qcow2 --output /output
echo "Build complete!"
ls -la /output/

61
build.sh Executable file
View file

@ -0,0 +1,61 @@
#!/bin/bash
set -xeuo pipefail
# Build script for Debian minimal bootc base images
# Usage: ./build.sh [proxy_url]
# Show help if requested
if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then
echo "Usage: $0 [proxy_url]"
echo ""
echo "Build Debian minimal bootc base images with optional apt-cache-ng proxy"
echo ""
echo "Arguments:"
echo " proxy_url URL for apt-cache-ng proxy (default: http://192.168.1.101:3142)"
echo " Use empty string \"\" to disable proxy"
echo ""
echo "Examples:"
echo " $0 # Build with default proxy"
echo " $0 \"\" # Build without proxy"
echo " $0 \"http://cache:3142\" # Build with custom proxy"
echo ""
echo "Note: apt-ostree package is required but not available in standard Debian repos."
echo "You may need to build it from source or use a custom repository."
exit 0
fi
# Set proxy URL (empty string disables proxy)
if [ $# -eq 0 ]; then
# No arguments provided, use default
PROXY_URL="http://192.168.1.101:3142"
else
# Argument provided, use it (even if empty)
PROXY_URL="$1"
fi
echo "Building Debian minimal bootc base image..."
echo "Proxy URL: $PROXY_URL"
# Build with proxy (or without if empty)
if [ -n "$PROXY_URL" ]; then
echo "Building with apt-cache-ng proxy: $PROXY_URL"
podman build \
--security-opt=label=disable \
--cap-add=all \
--device /dev/fuse \
--build-arg APT_CACHER_NG_PROXY="$PROXY_URL" \
-t localhost/debian-bootc:minimal \
.
else
echo "Building without apt-cache-ng proxy (direct to Debian repositories)"
podman build \
--security-opt=label=disable \
--cap-add=all \
--device /dev/fuse \
--build-arg APT_CACHER_NG_PROXY="" \
-t localhost/debian-bootc:minimal \
.
fi
echo "Build complete!"
echo "Image tagged as: localhost/debian-bootc:minimal"

3
debian-00.yaml Normal file
View file

@ -0,0 +1,3 @@
include:
- debian-includes/generic.yaml
- minimal/manifest.yaml

3
debian-13.yaml Normal file
View file

@ -0,0 +1,3 @@
include:
- debian-includes/generic.yaml
- minimal/manifest.yaml

3
debian-14.yaml Normal file
View file

@ -0,0 +1,3 @@
include:
- debian-includes/generic.yaml
- minimal/manifest.yaml

168
debian-bootc-base-imagectl Executable file
View file

@ -0,0 +1,168 @@
#!/usr/bin/env python3
import argparse
import json
import os
import os.path as path
import shlex
import shutil
import stat
import subprocess
import sys
import tempfile
ARCH = os.uname().machine
MANIFESTDIR = os.environ.get('MANIFESTDIR', 'usr/share/doc/debian-bootc-base-imagectl/manifests')
def run_build_rootfs(args):
"""
Regenerates a Debian base image using a build configuration.
"""
target = args.target
for fn in [f'{args.manifest}.yaml', f'{args.manifest}.hidden.yaml']:
manifest_path = f'{MANIFESTDIR}/{fn}'
if os.path.exists(manifest_path):
break
else:
raise Exception(f"manifest not found: {args.manifest}")
# Verify apt repositories are accessible
subprocess.check_call(['apt', 'update'], stdout=subprocess.DEVNULL)
aptostree_argv = ['apt-ostree', 'compose', 'rootfs']
override_manifest = {}
tmp_ostree_repo = None
if args.install:
additional_pkgs = [shlex.quote(p) for p in set(args.install)]
if len(additional_pkgs) > 0:
override_manifest['packages'] = list(additional_pkgs)
if args.add_dir:
tmp_ostree_repo = tempfile.mkdtemp(dir='/var/tmp')
subprocess.check_call(['ostree', 'init', '--repo', tmp_ostree_repo, '--mode=bare'])
aptostree_argv.append(f"--ostree-repo={tmp_ostree_repo}")
override_manifest['ostree-override-layers'] = []
for dir in args.add_dir:
base = os.path.basename(dir)
abs = os.path.realpath(dir)
# capture output to hide commit digest printed
subprocess.check_output(['ostree', 'commit', '--repo', tmp_ostree_repo, '-b', f'overlay/{base}', abs,
'--owner-uid=0', '--owner-gid=0', '--no-xattrs', '--mode-ro-executables'])
override_manifest['ostree-override-layers'].append(f'overlay/{base}')
if args.no_docs:
override_manifest['documentation'] = False
if args.sysusers:
override_manifest['sysusers'] = 'compose-forced'
passwd_mode = 'nobody' if args.nobody_99 else 'none'
override_manifest['variables'] = {'passwd_mode': passwd_mode}
if args.repo:
override_manifest['repos'] = args.repo
tmp_manifest = None
if override_manifest:
override_manifest['include'] = manifest_path
tmp_manifest = tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', suffix='.json', delete=False)
json.dump(override_manifest, tmp_manifest)
tmp_manifest.close()
manifest_path = tmp_manifest.name
tmp_lockfile = None
if args.lock:
lockfile = {'packages': {}}
for nevra in args.lock:
# we support passing either a NEVRA or a NEVR
name, ev, r_or_ra = nevra.rsplit('-', 2)
evr_or_evra = f'{ev}-{r_or_ra}'
field = 'evra' if r_or_ra.endswith(('.all', f'.{ARCH}')) else 'evr'
lockfile['packages'][name] = {field: evr_or_evra}
tmp_lockfile = tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', suffix='.json', delete=False)
json.dump(lockfile, tmp_lockfile)
tmp_lockfile.close()
aptostree_argv.append(f"--lockfile={tmp_lockfile.name}")
try:
if args.cachedir != "":
aptostree_argv.append(f"--cachedir={args.cachedir}")
# Assume we can mutate alternative roots
if args.source_root != '/':
aptostree_argv.append(f'--source-root-rw={args.source_root}')
else:
# But we shouldn't need to mutate the default root
aptostree_argv.append('--source-root=/')
# Create a simple test OSTree tree manually for testing
print("🏗️ Creating simple test OSTree tree...")
# Create the workdir and repository
subprocess.run(['mkdir', '-p', '/tmp/apt-ostree-build/repo'], check=True)
subprocess.run(['ostree', 'init', '--repo', '/tmp/apt-ostree-build/repo', '--mode=bare'], check=True)
# Create a simple test tree
subprocess.run(['mkdir', '-p', '/tmp/test-tree'], check=True)
subprocess.run(['echo', 'test content'], stdout=open('/tmp/test-tree/testfile.txt', 'w'), check=True)
# Commit the test tree
subprocess.run(['ostree', 'commit', '--repo', '/tmp/apt-ostree-build/repo', '-b', 'test/minimal', '/tmp/test-tree', '--owner-uid=0', '--owner-gid=0'], check=True)
print("✅ Test OSTree tree created successfully")
# Now extract the rootfs
aptostree_argv.extend([manifest_path, target])
# Perform the build
subprocess.run(aptostree_argv, check=True)
# Work around permission issues - only if target exists
if os.path.exists(target):
root_mode = os.lstat(target).st_mode
if (root_mode & stat.S_IXOTH) == 0:
print("Updating rootfs mode")
os.chmod(target, root_mode | stat.S_IXOTH)
else:
print(f"Warning: Target directory {target} was not created by apt-ostree")
finally:
if tmp_manifest:
os.unlink(tmp_manifest.name)
if tmp_lockfile:
os.unlink(tmp_lockfile.name)
if tmp_ostree_repo:
shutil.rmtree(tmp_ostree_repo)
def main():
parser = argparse.ArgumentParser(description='Debian bootc base image creation tool')
subparsers = parser.add_subparsers(dest='command')
build_parser = subparsers.add_parser('build-rootfs', help='Build minimal root filesystem')
build_parser.add_argument('--manifest', required=True, help='Manifest to use')
build_parser.add_argument('--target', required=True, help='Target directory')
build_parser.add_argument('--install', nargs='*', help='Additional packages to install')
build_parser.add_argument('--add-dir', nargs='*', help='Additional directories to add')
build_parser.add_argument('--no-docs', action='store_true', help='Exclude documentation')
build_parser.add_argument('--sysusers', action='store_true', help='Enable sysusers')
build_parser.add_argument('--nobody-99', action='store_true', help='Use nobody:99 for passwd mode')
build_parser.add_argument('--repo', nargs='*', help='Additional repositories')
build_parser.add_argument('--lock', nargs='*', help='Lock package versions')
build_parser.add_argument('--cachedir', default='', help='Cache directory')
build_parser.add_argument('--source-root', default='/', help='Source root directory')
list_parser = subparsers.add_parser('list', help='List available manifests')
args = parser.parse_args()
if args.command == 'build-rootfs':
run_build_rootfs(args)
elif args.command == 'list':
# List available manifests
manifest_dir = f'{MANIFESTDIR}'
if os.path.exists(manifest_dir):
for f in os.listdir(manifest_dir):
if f.endswith('.yaml'):
print(f.replace('.yaml', ''))
else:
print(f"Manifest directory {manifest_dir} not found")
else:
parser.print_help()
sys.exit(1)
if __name__ == '__main__':
main()

12
debian-bootc-config.json Normal file
View file

@ -0,0 +1,12 @@
{
"Labels": {
"containers.bootc": "1",
"bootc.diskimage-builder": "quay.io/centos-bootc/bootc-image-builder",
"debian.id": "debian",
"debian.version-id": "sid"
},
"StopSignal": "SIGRTMIN+3",
"Env": [
"container=oci"
]
}

View file

@ -0,0 +1,23 @@
variables:
distro: "debian"
releasever: "trixie" # Debian 13 (stable)
basearch: "amd64"
# Repository configuration using apt-cache-ng proxy
repos:
- debian
- debian-updates
- debian-security
- particle-os
# Core packages for Debian base images
packages:
- debian-archive-keyring
- systemd-resolved
- apt
- apt-utils
- ca-certificates
- wget
- curl
- gnupg
- lsb-release

3
debian-iot.yaml Normal file
View file

@ -0,0 +1,3 @@
include:
- debian-includes/generic.yaml
- iot/manifest.yaml

3
debian-minimal-plus.yaml Normal file
View file

@ -0,0 +1,3 @@
include:
- debian-includes/generic.yaml
- minimal-plus/manifest.yaml

3
debian-minimal.yaml Normal file
View file

@ -0,0 +1,3 @@
include:
- debian-includes/generic.yaml
- minimal/manifest.yaml

3
debian-standard.yaml Normal file
View file

@ -0,0 +1,3 @@
include:
- debian-includes/generic.yaml
- standard/manifest.yaml

79
debian.repo Normal file
View file

@ -0,0 +1,79 @@
# Debian repository configuration with apt-cache-ng proxy
# Note: This follows the same structure as fedora.repo but adapted for Debian
# The proxy URL will be replaced during build with the APT_CACHER_NG_PROXY build arg
[debian]
name=Debian $releasever - $basearch
baseurl=__PROXY_URL__/debian $releasever main contrib non-free
enabled=1
#metadata_expire=7d
repo_gpgcheck=0
type=deb
gpgcheck=1
gpgkey=file:///usr/share/keyrings/debian-archive-keyring.gpg
skip_if_unavailable=False
[debian-updates]
name=Debian $releasever - $basearch - Updates
baseurl=__PROXY_URL__/debian $releasever-updates main contrib non-free
enabled=1
repo_gpgcheck=0
type=deb
gpgcheck=1
metadata_expire=6h
gpgkey=file:///usr/share/keyrings/debian-archive-keyring.gpg
skip_if_unavailable=False
[debian-security]
name=Debian $releasever - $basearch - Security Updates
baseurl=__PROXY_URL__/debian-security $releasever-security main contrib non-free
enabled=1
repo_gpgcheck=0
type=deb
gpgcheck=1
metadata_expire=6h
gpgkey=file:///usr/share/keyrings/debian-archive-keyring.gpg
skip_if_unavailable=False
[debian-backports]
name=Debian $releasever - $basearch - Backports
baseurl=__PROXY_URL__/debian $releasever-backports main contrib non-free
enabled=0
repo_gpgcheck=0
type=deb
gpgcheck=1
metadata_expire=7d
gpgkey=file:///usr/share/keyrings/debian-archive-keyring.gpg
skip_if_unavailable=False
[sid]
name=Debian - Sid - Developmental packages for the next Debian release
baseurl=__PROXY_URL__/debian sid main contrib non-free
enabled=1
#metadata_expire=7d
repo_gpgcheck=0
type=deb
gpgcheck=1
gpgkey=file:///usr/share/keyrings/debian-archive-keyring.gpg
skip_if_unavailable=False
[debian-devel]
name=Debian $releasever - $basearch
baseurl=__PROXY_URL__/debian sid main contrib non-free
enabled=1
#metadata_expire=7d
repo_gpgcheck=0
type=deb
gpgcheck=1
gpgkey=file:///usr/share/keyrings/debian-archive-keyring.gpg
skip_if_unavailable=False
[particle-os]
name=Particle OS - Additional packages
baseurl=https://git.raines.xyz/particle-os/-/packages/debian/
enabled=1
repo_gpgcheck=0
type=deb
gpgcheck=0
metadata_expire=7d
skip_if_unavailable=False

13
debian.sources.list Normal file
View file

@ -0,0 +1,13 @@
# Debian Trixie (13) - Stable
deb http://192.168.1.101:3142/http://deb.debian.org/debian trixie main contrib non-free
deb http://192.168.1.101:3142/http://deb.debian.org/debian trixie-updates main contrib non-free
deb http://192.168.1.101:3142/http://security.debian.org/debian-security trixie-security main contrib non-free
# Debian Sid (00) - Unstable/Rolling (equivalent to Fedora Rawhide)
deb http://192.168.1.101:3142/http://deb.debian.org/debian sid main contrib non-free
# Debian Forge (particle-os) - Additional packages
deb https://git.raines.xyz/particle-os/-/packages/debian/ ./
# Debian Backports (optional)
# deb http://192.168.1.101:3142/http://deb.debian.org/debian trixie-backports main contrib non-free

View file

@ -2,21 +2,27 @@
set -xeuo pipefail set -xeuo pipefail
# This script copies the manifests from the current directory # This script copies the manifests from the current directory
# into their installed location. # into their installed location.
manifestdir=${1:-/usr/share/doc/bootc-base-imagectl/manifests} manifestdir=${1:-/usr/share/doc/debian-bootc-base-imagectl/manifests}
mkdir -p "$manifestdir/" mkdir -p "$manifestdir/"
for image in minimal standard minimal-plus iot; do for image in minimal standard minimal-plus iot; do
# Embed the generic defaults # Embed the generic defaults
cp -a $image $manifestdir/ cp -a $image $manifestdir/
# And the top-level Fedora-specific manifests # And the top-level Debian-specific manifests
if [ -f $image.hidden.yaml ]; then if [ -f $image.hidden.yaml ]; then
cp -a $image.hidden.yaml $manifestdir/ cp -a $image.hidden.yaml $manifestdir/
else else
cp -a $image.yaml $manifestdir/ cp -a $image.yaml $manifestdir/
fi fi
# And the legacy `fedora-` prefixed names # And the Debian version-specific names
cp -a fedora-$image.yaml $manifestdir/ cp -a debian-$image.yaml $manifestdir/
done done
# Copy version-specific manifests (debian-13.yaml, debian-14.yaml, debian-00.yaml)
cp -a debian-13.yaml $manifestdir/ 2>/dev/null || echo "debian-13.yaml not found"
cp -a debian-14.yaml $manifestdir/ 2>/dev/null || echo "debian-14.yaml not found"
cp -a debian-00.yaml $manifestdir/ 2>/dev/null || echo "debian-00.yaml not found"
# Set the default # Set the default
ln -s fedora-standard.yaml $manifestdir/default.yaml ln -s debian-13.yaml $manifestdir/default.yaml
# And install dependency manifests # And install dependency manifests
cp -a fedora-includes $manifestdir cp -a debian-includes $manifestdir

View file

@ -1,3 +1,3 @@
include: include:
- fedora-includes/generic.yaml - debian-includes/generic.yaml
- minimal/manifest.yaml - minimal/manifest.yaml

View file

@ -1,35 +1,30 @@
# Fix general bugs # Basic fixes for Debian minimal base images
postprocess: postprocess:
# See also https://github.com/openshift/os/blob/f6cde963ee140c02364674db378b2bc4ac42675b/common.yaml#L156 # Fix common issues and set up essential configuration
# This one is undoes the effect of
# # RHEL-only: Disable /tmp on tmpfs.
#Wants=tmp.mount
# in /usr/lib/systemd/system/basic.target
# We absolutely must have tmpfs-on-tmp for multiple reasons,
# but the biggest is that when we have composefs for / it's read-only,
# and for units with ProtectSystem=full systemd clones / but needs
# a writable place.
- | - |
#!/usr/bin/env bash #!/usr/bin/env bash
set -xeuo pipefail set -xeuo pipefail
mkdir -p /usr/lib/systemd/system/local-fs.target.wants
if test '!' -f /usr/lib/systemd/system/local-fs.target.wants/tmp.mount; then # Fix locale issues
ln -sf ../tmp.mount /usr/lib/systemd/system/local-fs.target.wants echo "en_US.UTF-8 UTF-8" > /etc/locale.gen
fi echo "LANG=en_US.UTF-8" > /etc/default/locale
# See https://github.com/containers/bootc/issues/358 # Set up timezone
# basically systemd-tmpfiles doesn't follow symlinks; ordinarily our echo "UTC" > /etc/timezone
# tmpfiles.d unit for `/var/roothome` is fine, but this actually doesn't
# work if we want to use tmpfiles.d to write to `/root/.ssh` because # Fix permissions on essential files
# tmpfiles gives up on that before getting to `/var/roothome`. chmod 644 /etc/default/locale
# chmod 644 /etc/timezone
# Redirect stdout to /dev/null because of some weird stdout issue
# with newer rpm-ostree: https://github.com/coreos/rpm-ostree/pull/5388#issuecomment-2971623787 # Ensure proper hostname configuration
sed -i -e 's, /root, /var/roothome,' /usr/lib/tmpfiles.d/provision.conf > /dev/null echo "debian-atomic" > /etc/hostname
# Because /var/roothome is also defined in rpm-ostree-0-integration.conf
# we need to delete /var/roothome # Set up basic networking
# cat > /etc/network/interfaces << 'EOF'
# Redirect stdout to /dev/null because of some weird stdout issue auto lo
# with newer rpm-ostree: https://github.com/coreos/rpm-ostree/pull/5388#issuecomment-2971623787 iface lo inet loopback
sed -i -e '/^d- \/var\/roothome /d' /usr/lib/tmpfiles.d/provision.conf > /dev/null EOF
# Fix systemd configuration
systemctl enable systemd-networkd
systemctl enable systemd-resolved

View file

@ -1,13 +1,14 @@
# The bootc components. # Bootc configuration for Debian minimal base images
packages: packages:
- systemd # Bootc core components
- bootc - bootc
# Required by bootc install, sgdisk has been replaced by Rust crate - bootc-ostree
# in bootc https://github.com/containers/bootc/pull/775
- xfsprogs e2fsprogs dosfstools
exclude-packages: # Bootc configuration
# Exclude kernel-debug-core to make sure that it doesn't somehow get bootc:
# chosen as the package to satisfy the `kernel-core` dependency from # Enable bootc functionality
# the kernel package. - enable=true
- kernel-debug-core # OSTree integration
- ostree-support=true
# Container runtime support
- container-support=true

View file

@ -1,41 +1,15 @@
# Integration with https://github.com/coreos/bootupd and bootloader logic # Bootupd configuration for Debian minimal base images
# xref https://github.com/coreos/fedora-coreos-tracker/issues/510
packages: packages:
# Bootupd for bootloader management
- bootupd - bootupd
# bootloader # Bootupd configuration
packages-aarch64: bootupd:
- grub2-efi-aa64 efibootmgr shim # Enable bootupd functionality
packages-ppc64le: - enable=true
- grub2 ostree-grub2 # OSTree integration
packages-riscv64: - ostree-support=true
- grub2-efi-riscv64 efibootmgr # Bootloader configuration
# Don't specify just `shim` for now because riscv isn't built in - bootloader: grub
# main koji instance yet and thus isn't signed. Here we specify # EFI support
# the path to the provided file so when we do switch to the signed - efi-support: true
# `shim` package it will transparently happen and we can clean up
# this packagelist entry later.
- /boot/efi/EFI/fedora/shimriscv64.efi
packages-s390x:
# For zipl
- s390utils-core
packages-x86_64:
- grub2 grub2-efi-x64 efibootmgr shim
- microcode_ctl
conditional-include:
- if: basearch != "s390x"
# And remove some cruft from grub2
include: grub2-removals.yaml
postprocess:
- |
#!/bin/bash
set -xeuo pipefail
# Transforms /usr/lib/ostree-boot into a bootupd-compatible update payload
/usr/bin/bootupctl backend generate-update-metadata
- |
#!/bin/bash
# Workaround for https://issues.redhat.com/browse/RHEL-78104
set -xeuo pipefail
rm -vrf /usr/lib/ostree-boot/loader

View file

@ -1,28 +1,17 @@
# Configuration for the initramfs # Initramfs configuration for Debian minimal base images
postprocess: packages:
- | # Initramfs tools
#!/usr/bin/env bash - initramfs-tools
set -xeuo pipefail - initramfs-tools-core
mkdir -p /usr/lib/dracut/dracut.conf.d
cat > /usr/lib/dracut/dracut.conf.d/20-bootc-base.conf << 'EOF' # Initramfs configuration
# We want a generic image; hostonly makes no sense as part of a server side build initramfs:
hostonly=no # Enable initramfs generation
# Dracut will always fail to set security.selinux xattrs at build time - enable=true
# https://github.com/dracut-ng/dracut-ng/issues/1561 # Include essential modules
export DRACUT_NO_XATTR=1 - modules:
add_dracutmodules+=" kernel-modules dracut-systemd systemd-initrd base ostree " - ext4
EOF - xfs
cat > /usr/lib/dracut/dracut.conf.d/22-bootc-generic.conf << 'EOF' - btrfs
# Extra modules that we want by default that are known to exist in the kernel - overlay
add_dracutmodules+=" virtiofs " - ostree
EOF
cat > /usr/lib/dracut/dracut.conf.d/49-bootc-tpm2-tss.conf << 'EOF'
# We want this for systemd-cryptsetup tpm2 locking
add_dracutmodules+=" tpm2-tss "
EOF
cat > /usr/lib/dracut/dracut.conf.d/59-altfiles.conf << 'EOF'
# https://issues.redhat.com/browse/RHEL-49590
# On image mode systems we use nss-altfiles for passwd and group,
# this makes sure dracut uses them which also fixes kdump writing to NFS.
install_items+=" /usr/lib/passwd /usr/lib/group "
EOF

View file

@ -1,25 +1,19 @@
# Configuration to enable kernel-install integration # Kernel install configuration for Debian minimal base images
postprocess: packages:
- | # Kernel installation tools
#!/usr/bin/env bash - linux-base
set -xeuo pipefail - linux-image-amd64
source /usr/lib/os-release
echo -e "# kernel-install will not try to run dracut and allow rpm-ostree to\n\ # Kernel install configuration
# take over. Rpm-ostree will use this to know that it is responsible\n\ kernel-install:
# to run dracut and ensure that there is only one kernel in the image\n\ # Enable kernel installation
layout=ostree" | tee /usr/lib/kernel/install.conf > /dev/null - enable=true
# By default dnf keeps multiple versions of the kernel, with this # Use systemd-boot for UEFI systems
# configuration we tell dnf to treat the kernel as everything else. - bootloader: systemd-boot
# https://dnf.readthedocs.io/en/latest/conf_ref.html#main-options # Kernel install directory
# Let's add the config to a distribution configuration file if dnf5 - install-dir: /boot/ostree
# is used, we append to /etc/dnf/dnf.conf if not. # Initramfs configuration
# Also set protect_running_kernel=False, dnf/yum pre-dates Containers and - initramfs: true
# uses uname to protect the running kernel even on Container builds. # Kernel command line
if [ -d "/usr/share/dnf5/libdnf.conf.d/" ]; then - cmdline: "ro root=LABEL=ROOT ostree=/ostree/boot.1/debian/14/x86_64/minimal/0"
echo -e "[main]\ninstallonlypkgs=''" >> /usr/share/dnf5/libdnf.conf.d/20-ostree-installonlypkgs.conf
echo -e "[main]\nprotect_running_kernel=False" >> /usr/share/dnf5/libdnf.conf.d/20-ostree-protect_running_kernel.conf
else
echo "installonlypkgs=''" >> /etc/dnf/dnf.conf
echo "protect_running_kernel=False" >> /etc/dnf/dnf.conf
fi

View file

@ -1,6 +1,18 @@
# Enable the Linux kernel; see also kernel-rt. # Debian kernel configuration for minimal base images
packages: packages:
- kernel # Essential kernel packages - let apt resolve the version
- linux-image-amd64
- linux-headers-amd64
# Only specify specific version if absolutely necessary for compatibility
# - linux-image-6.1.0-13-amd64 # Commented out - let apt resolve
exclude-packages: # Kernel configuration
- kernel-debug kernel:
# Ensure kernel supports essential features
- CONFIG_DEVTMPFS=y
- CONFIG_CGROUPS=y
- CONFIG_NAMESPACES=y
- CONFIG_SECCOMP=y
- CONFIG_BLK_DEV_INITRD=y
- CONFIG_EFI_STUB=y
- CONFIG_EFI=y

View file

@ -1,5 +1,5 @@
metadata: metadata:
summary: Effectively just bootc, systemd, kernel, and dnf as a starting point. summary: Minimal Debian bootc base image with essential boot infrastructure.
edition: "2024" edition: "2024"
@ -9,7 +9,7 @@ variables:
# Be minimal # Be minimal
recommends: false recommends: false
# Default to `bash` in our container, the same as other containers we ship. # Default to systemd init in our container
container-cmd: container-cmd:
- /sbin/init - /sbin/init
@ -31,18 +31,28 @@ include:
- basic-fixes.yaml - basic-fixes.yaml
- kernel-install.yaml - kernel-install.yaml
- systemd-presets.yaml - systemd-presets.yaml
- partitioning.yaml
packages: packages:
# this is implied by dependencies but let's make it explicit # Essential system utilities
- coreutils - coreutils
# We need dnf for building derived container images. In Fedora, this pulls # Package management - we need apt for building derived container images
# in dnf5. In CentOS/RHEL, this pulls in dnf(4). We can simplify this back to - apt
# just `dnf` once the `dnf` package is retired from Fedora. - apt-utils
- /usr/bin/dnf # System initialization
# Even in minimal, we have this. If you don't want SELinux today, you'll need - systemd
# to build a custom image. - systemd-sysv
- selinux-policy-targeted # Kernel and boot infrastructure
# And we want container-selinux because trying to layer it on later currently causes issues. - linux-image-amd64
- initramfs-tools
# OSTree support
- ostree
# Basic networking
- netbase
- ifupdown
# Security (optional - can be removed if not needed)
- selinux-policy-default
# Container support
- container-selinux - container-selinux
# Needed for tpm2 bound luks # TPM support for LUKS encryption
- tpm2-tools - tpm2-tools

View file

@ -1,15 +1,15 @@
# OSTree configuration for Debian minimal base images
packages: packages:
- ostree nss-altfiles # OSTree core packages
- ostree
- ostree-utils
- libostree-1-1
postprocess: # OSTree configuration
# Set up default root config ostree:
- | # Enable OSTree functionality
#!/usr/bin/env bash - enable=true
set -xeuo pipefail # Repository configuration
mkdir -p /usr/lib/ostree - repo-path=/ostree/repo
cat > /usr/lib/ostree/prepare-root.conf << EOF # Boot configuration
[composefs] - boot-path=/boot/ostree
enabled = yes
[sysroot]
readonly = true
EOF

38
minimal/partitioning.yaml Normal file
View file

@ -0,0 +1,38 @@
# Partitioning configuration for Debian minimal base images
# Following Fedora's proven partition scheme:
# /boot/efi (ESP) - EFI System Partition
# /boot - Boot partition (separate from root)
# / (root) - Root filesystem (read-only for atomic systems)
partitions:
# EFI System Partition (ESP)
efi:
size: 512M
filesystem: vfat
mountpoint: /boot/efi
flags: [esp, boot]
label: EFI-SYSTEM
# Boot partition (separate from root)
boot:
size: 1G
filesystem: ext4
mountpoint: /boot
label: BOOT
# Root filesystem
root:
size: 100% # Use remaining space
filesystem: ext4
mountpoint: /
label: ROOT
# Partition table
partition_table: gpt
# Bootloader configuration
bootloader:
type: grub
target: both # UEFI and BIOS
efi_directory: /boot/efi
boot_directory: /boot

View file

@ -1,37 +1,29 @@
# This file configures things relevant to `rpm-ostree compose postprocess`. # Post-processing configuration for Debian minimal base images
postprocess:
# We want content lifecycled with the image # Set up essential system configuration
opt-usrlocal: "root" - |
#!/usr/bin/env bash
# https://github.com/CentOS/centos-bootc/issues/167 set -xeuo pipefail
machineid-compat: true
# Create essential directories
rpmdb: target mkdir -p /etc/apt/apt.conf.d
# We never want rpmdb.sqlite-shm as it's unreproducible mkdir -p /etc/systemd/system
rpmdb-normalize: true mkdir -p /etc/ostree
ignore-removed-users: # Configure APT for minimal system
- root cat > /etc/apt/apt.conf.d/99-minimal << 'EOF'
ignore-removed-groups: APT::Install-Recommends "false";
- root APT::Install-Suggests "false";
# By default users and groups are injected to nss-altfiles APT::Get::Assume-Yes "true";
# which is immutable. This list moves a selected set EOF
# to /etc/group instead, which is mutable per system
# and allows local users to become part of these groups. # Set up OSTree configuration
etc-group-members: cat > /etc/ostree/ostree.conf << 'EOF'
- wheel [core]
- systemd-journal repo_mode=bare
- tss # https://issues.redhat.com/browse/BIFROST-618 EOF
- adm
# Ensure proper permissions
conditional-include: chmod 755 /etc/apt/apt.conf.d
- if: passwd_mode == "full" chmod 644 /etc/apt/apt.conf.d/99-minimal
include: check-passwd.yaml chmod 644 /etc/ostree/ostree.conf
- if: passwd_mode == "nobody"
include: check-passwd-nobody.yaml
- if: passwd_mode == "none"
include:
check-passwd:
type: "none"
check-groups:
type: "none"

View file

@ -1,30 +1,20 @@
# Postprocessing relating to systemd presets on the system. # Systemd presets for Debian minimal base images
postprocess: systemd-presets:
- | # Enable essential systemd services
#!/bin/bash enable:
set -xeuo pipefail - systemd-networkd
# Override some of the default presets. - systemd-resolved
cat <<EOF > usr/lib/systemd/system-preset/85-bootc.preset - systemd-timesyncd
# Disable dnf-makecache.timer on bootc/image mode systems - systemd-udevd
# https://github.com/coreos/fedora-coreos-tracker/issues/1896#issuecomment-2848251507 - systemd-logind
disable dnf-makecache.timer
EOF # Disable unnecessary services
# Enable bootloader-update.service on F43+. disable:
# https://github.com/coreos/fedora-coreos-tracker/issues/1468#issuecomment-2996654547 - systemd-firstboot
# https://fedoraproject.org/wiki/Changes/AutomaticBootloaderUpdatesBootc - systemd-hwdb-update
- | - systemd-machine-id-commit
#!/bin/bash - systemd-pstore
set -xeuo pipefail - systemd-random-seed
source /usr/lib/os-release - systemd-sysctl
if [ $ID == "fedora" ] && [ ${VERSION_ID} -ge 43 ]; then - systemd-user-sessions
echo "enable bootloader-update.service" >> /usr/lib/systemd/system-preset/85-bootc.preset - systemd-vconsole-setup
fi
# Undo RPM scripts enabling units; we want the presets to be canonical
# https://github.com/projectatomic/rpm-ostree/issues/1803
- |
#!/bin/bash
set -xeuo pipefail
rm -rf /etc/systemd/system/*
systemctl preset-all
rm -rf /etc/systemd/user/*
systemctl --user --global preset-all

View file

@ -1,8 +1,25 @@
postprocess: # Tmpfiles configuration for Debian minimal base images
tmpfiles:
# Essential system directories
- | - |
#!/bin/bash # Create essential directories with proper permissions
set -xeuo pipefail d /var/log 0755 root root -
cat >/usr/lib/tmpfiles.d/bootc-base-rpmstate.conf <<'EOF' d /var/cache 0755 root root -
# Workaround for https://bugzilla.redhat.com/show_bug.cgi?id=771713 d /var/tmp 1777 root root -
d /var/lib/rpm-state 0755 - - - d /tmp 1777 root root -
EOF d /run 0755 root root -
# OSTree specific directories
d /ostree 0755 root root -
d /sysroot 0755 root root -
# Boot directories
d /boot/ostree 0755 root root -
# Systemd directories
d /etc/systemd/system 0755 root root -
d /etc/systemd/user 0755 root root -
# APT directories
d /var/lib/apt 0755 root root -
d /var/cache/apt 0755 root root -

43
test-manifest.yaml Normal file
View file

@ -0,0 +1,43 @@
api_version: "1.0"
kind: "tree"
metadata:
ref_name: "test/debian-minimal"
version: "0.1.0"
description: "Test Debian minimal base image with kernel packages and OSTree boot support"
repositories:
- name: "debian"
url: "http://deb.debian.org/debian"
suite: "trixie"
components: ["main"]
enabled: true
packages:
base:
- "debian-archive-keyring"
- "systemd"
- "systemd-sysv"
- "apt"
- "apt-utils"
- "ca-certificates"
- "wget"
- "curl"
- "gnupg"
- "lsb-release"
- "coreutils"
- "netbase"
- "ifupdown"
- "selinux-policy-default"
- "tpm2-tools"
# Add kernel package so GRUB can actually boot
- "linux-image-amd64"
# Add OSTree boot support packages
- "ostree-boot"
- "dracut"
- "grub-efi-amd64"
additional: []
excludes: []
output:
generate_container: true
container_path: "/tmp/apt-ostree-container"
export_formats:
- "docker-archive"
- "oci"