bootc-docs/building/base-images.md
robojerk d204c35734
Some checks failed
Test bootc Documentation / test-base-image (push) Failing after 24s
Test bootc Documentation / test-documentation (push) Failing after 30s
Add essential initramfs integration for composefs support
- Add initramfs integration files based on debian-bootc project
- Include bootc-initramfs-setup.service for systemd integration
- Add dracut module-setup.sh for initramfs generation
- Include prepare-root.conf for OSTree composefs configuration
- Update Containerfile examples to include initramfs files
- Fix systemctl calls to use symlinks instead of systemctl commands
- Add comprehensive initramfs-integration.md documentation
- Update README to reference new initramfs documentation

Based on files from https://github.com/bootcrew/debian-bootc:
- bootc-initramfs-setup.service
- module-setup.sh
- prepare-root.conf

These files are essential for proper composefs support and boot
functionality in Debian bootc images.
2025-09-15 14:48:24 -07:00

13 KiB

Building Base bootc Images

⚠️ EXPERIMENTAL WARNING ⚠️

bootc on Debian is HIGHLY EXPERIMENTAL and NOT production-ready!

  • No reproducible bootc images exist yet - Everything is experimental
  • Use only for testing on virtual machines or test hardware
  • Expect failures - bootc is not stable on Debian
  • DO NOT use in production - Data loss and system instability possible

This document provides detailed instructions for creating base bootc images using two different approaches:

  1. Method 1: Building from an existing OCI image
  2. Method 2: Building from scratch using debootstrap/mmdebstrap

Important Note

bootc does not create base images - it only deploys and manages them. Base image creation requires separate tools and processes as described below.

Method 1: Building from Existing OCI Image

This method takes an existing Debian OCI image and converts it into a bootc-compatible base image.

Prerequisites

# Install required tools
sudo apt update
sudo apt install -y podman buildah skopeo

# Install bootc for validation
git clone https://github.com/containers/bootc.git
cd bootc
make
sudo make install

Essential Initramfs Integration

⚠️ CRITICAL: For proper composefs support and boot functionality, you must include initramfs integration files in your base image. These files are essential for Debian bootc images and are based on the debian-bootc project.

Required Files

Create these files in your build context:

1. files/37composefs/bootc-initramfs-setup.service:

[Unit]
DefaultDependencies=no
ConditionKernelCommandLine=composefs
ConditionPathExists=/etc/initrd-release
After=sysroot.mount
Requires=sysroot.mount
Before=initrd-root-fs.target
Before=initrd-switch-root.target
OnFailure=emergency.target
OnFailureJobMode=isolate

[Service]
Type=oneshot
ExecStart=/usr/bin/bootc-initramfs-setup
StandardInput=null
StandardOutput=journal
StandardError=journal+console
RemainAfterExit=yes

2. files/37composefs/module-setup.sh:

#!/usr/bin/bash
check() {
    return 0
}

depends() {
    return 0
}

install() {
    inst \
        "${moddir}/bootc-initramfs-setup" /bin/bootc-initramfs-setup
    inst \
        "${moddir}/bootc-initramfs-setup.service" \
        "${systemdsystemunitdir}/bootc-initramfs-setup.service"
    $SYSTEMCTL -q --root "${initdir}" add-wants \
        'initrd-root-fs.target' 'bootc-initramfs-setup.service'
}

3. files/ostree/prepare-root.conf:

[composefs]
enabled = yes

[sysroot]
readonly = true

Step 1: Create Containerfile

Create a Containerfile.base:

# Containerfile.base - Convert existing Debian image to bootc base
FROM debian:sid-slim

# Install essential packages for bootc
RUN apt update && apt install -y \
    systemd \
    systemd-sysusers \
    systemd-tmpfiles \
    kernel \
    initramfs-tools \
    dracut \
    grub2 \
    grub2-common \
    efibootmgr \
    ostree \
    && apt clean

# Copy initramfs integration files
COPY files/37composefs/bootc-initramfs-setup.service /usr/lib/systemd/system/
COPY files/37composefs/module-setup.sh /usr/lib/dracut/modules.d/37composefs/
COPY files/ostree/prepare-root.conf /usr/lib/ostree-boot/prepare-root.conf

# Create essential directories
RUN mkdir -p /usr/lib/systemd/system \
    /usr/lib/systemd/user \
    /etc/systemd/system \
    /etc/systemd/user \
    /var/lib/systemd \
    /run/systemd

# Configure systemd as init
RUN ln -sf /lib/systemd/systemd /sbin/init

# Set up basic systemd configuration
RUN ln -sf /lib/systemd/system/multi-user.target /etc/systemd/system/default.target

# Create essential systemd services
RUN ln -sf /usr/lib/systemd/system/systemd-resolved.service \
    /etc/systemd/system/multi-user.target.wants/systemd-resolved.service
RUN ln -sf /usr/lib/systemd/system/systemd-networkd.service \
    /etc/systemd/system/multi-user.target.wants/systemd-networkd.service
RUN ln -sf /usr/lib/systemd/system/systemd-timesyncd.service \
    /etc/systemd/system/multi-user.target.wants/systemd-timesyncd.service

# Configure basic networking
RUN echo -e "[Match]\nName=*\n\n[Network]\nDHCP=yes" > /etc/systemd/network/80-dhcp.network

# Set up basic users and groups
RUN systemd-sysusers --root=/ --create

# Configure tmpfiles
RUN systemd-tmpfiles --create --root=/

# Install bootc (for validation)
COPY --from=quay.io/containers/bootc:latest /usr/bin/bootc /usr/bin/bootc

# Validate the image
RUN bootc container lint

# Required bootc labels
LABEL containers.bootc 1
LABEL ostree.bootable 1

# Set default command
CMD ["/lib/systemd/systemd"]

Step 2: Build the Base Image

#!/bin/bash
# build-base-from-existing.sh

set -euo pipefail

IMAGE_NAME="debian-bootc-base"
TAG="${1:-latest}"

echo "🏗️  Building Debian bootc Base Image from Existing OCI"
echo "Image: ${IMAGE_NAME}:${TAG}"

# Build the base image
echo "📦 Building container image..."
podman build -f Containerfile.base -t "${IMAGE_NAME}:${TAG}" .

echo "✅ Base image built successfully!"
echo "Image: ${IMAGE_NAME}:${TAG}"

# Show image info
echo ""
echo "📊 Image Information:"
podman images "${IMAGE_NAME}:${TAG}"

echo ""
echo "🚀 Ready to build application layers!"
echo "Example: podman build -f examples/nginx/Containerfile -t ${IMAGE_NAME}:nginx"

Step 3: Test the Base Image

# Test the base image
podman run --rm -it debian-bootc-base:latest /bin/bash

# Validate with bootc
bootc container lint debian-bootc-base:latest

Method 2: Building from Scratch with debootstrap/mmdebstrap

This method creates a completely custom Debian base image from scratch.

Prerequisites

# Install required tools
sudo apt update
sudo apt install -y \
    debootstrap \
    mmdebstrap \
    podman \
    buildah \
    skopeo \
    qemu-user-static

# Install bootc for validation
git clone https://github.com/containers/bootc.git
cd bootc
make
sudo make install

Step 1: Create Root Filesystem

Create a script to build the root filesystem:

#!/bin/bash
# build-rootfs.sh - Build Debian rootfs from scratch

set -euo pipefail

ROOTFS_DIR="rootfs"
DEBIAN_RELEASE="bookworm"
ARCH="amd64"

echo "🏗️  Building Debian Root Filesystem from Scratch"
echo "Release: ${DEBIAN_RELEASE}"
echo "Architecture: ${ARCH}"
echo "Rootfs: ${ROOTFS_DIR}/"

# Clean up previous build
if [ -d "${ROOTFS_DIR}" ]; then
    echo "🧹 Cleaning up previous build..."
    sudo rm -rf "${ROOTFS_DIR}"
fi

# Create rootfs directory
mkdir -p "${ROOTFS_DIR}"

# Build rootfs with mmdebstrap (faster and more reliable)
echo "📦 Building rootfs with mmdebstrap..."
sudo mmdebstrap \
    --arch="${ARCH}" \
    --variant=minbase \
    --include=systemd,systemd-sysusers,systemd-tmpfiles,kernel,initramfs-tools,grub2,grub2-common,efibootmgr,ostree \
    "${DEBIAN_RELEASE}" \
    "${ROOTFS_DIR}" \
    http://deb.debian.org/debian

echo "✅ Rootfs built successfully!"

# Set up basic systemd configuration
echo "⚙️  Configuring systemd..."
sudo chroot "${ROOTFS_DIR}" systemctl set-default multi-user.target

# Create essential systemd services
sudo chroot "${ROOTFS_DIR}" systemctl enable systemd-resolved.service
sudo chroot "${ROOTFS_DIR}" systemctl enable systemd-networkd.service
sudo chroot "${ROOTFS_DIR}" systemctl enable systemd-timesyncd.service

# Configure basic networking
sudo tee "${ROOTFS_DIR}/etc/systemd/network/80-dhcp.network" > /dev/null <<EOF
[Match]
Name=*

[Network]
DHCP=yes
EOF

# Set up users and groups
sudo chroot "${ROOTFS_DIR}" systemd-sysusers --create

# Configure tmpfiles
sudo chroot "${ROOTFS_DIR}" systemd-tmpfiles --create

# Create essential directories
sudo mkdir -p "${ROOTFS_DIR}/usr/lib/systemd/system"
sudo mkdir -p "${ROOTFS_DIR}/usr/lib/systemd/user"
sudo mkdir -p "${ROOTFS_DIR}/etc/systemd/system"
sudo mkdir -p "${ROOTFS_DIR}/etc/systemd/user"
sudo mkdir -p "${ROOTFS_DIR}/var/lib/systemd"
sudo mkdir -p "${ROOTFS_DIR}/run/systemd"

# Configure systemd as init
sudo ln -sf /lib/systemd/systemd "${ROOTFS_DIR}/sbin/init"

# Set proper permissions
sudo chown -R root:root "${ROOTFS_DIR}"
sudo chmod 755 "${ROOTFS_DIR}"

echo "✅ Rootfs configuration complete!"
echo "Rootfs: ${ROOTFS_DIR}/"

Step 2: Create Containerfile for Scratch Build

Create a Containerfile.scratch:

# Containerfile.scratch - Build bootc image from scratch rootfs
FROM scratch

# Copy the entire rootfs
COPY rootfs/ /

# Install bootc (copy from existing image)
COPY --from=quay.io/containers/bootc:latest /usr/bin/bootc /usr/bin/bootc

# Validate the image
RUN bootc container lint

# Required bootc labels
LABEL containers.bootc 1
LABEL ostree.bootable 1

# Set default command
CMD ["/lib/systemd/systemd"]

Step 3: Build Script

Create the main build script:

#!/bin/bash
# build-scratch-bootc.sh - Complete build script for debian-scratch-bootc

set -euo pipefail

# Configuration
IMAGE_NAME="debian-scratch-bootc"
TAG="${1:-latest}"
ROOTFS_DIR="rootfs"

echo "🏗️  Building Debian Scratch Bootc Base Image"
echo "Image: ${IMAGE_NAME}:${TAG}"
echo "Rootfs: ${ROOTFS_DIR}/"

# Check if rootfs exists, build if not
if [ ! -d "${ROOTFS_DIR}" ]; then
    echo "📦 Building rootfs..."
    ./build-rootfs.sh
fi

# Build the base image
echo "📦 Building container image..."
podman build -f Containerfile.scratch -t "${IMAGE_NAME}:${TAG}" .

echo "✅ Base image built successfully!"
echo "Image: ${IMAGE_NAME}:${TAG}"

# Show image info
echo ""
echo "📊 Image Information:"
podman images "${IMAGE_NAME}:${TAG}"

echo ""
echo "🚀 Ready to build application layers!"
echo "Example: podman build -f examples/nginx/Containerfile -t ${IMAGE_NAME}:nginx"

Step 4: Create Example Application Layer

Create an example showing how to build on top of the base image:

# examples/nginx/Containerfile
FROM debian-scratch-bootc:latest

# Install nginx
RUN apt update && apt install -y nginx && apt clean

# Configure nginx
RUN echo "server { listen 80; location / { return 200 'Hello from bootc!'; } }" > /etc/nginx/sites-available/default
RUN ln -sf /etc/nginx/sites-available/default /etc/nginx/sites-enabled/

# Enable nginx service
RUN systemctl enable nginx.service

# Validate the image
RUN bootc container lint

# Required bootc labels
LABEL containers.bootc 1
LABEL ostree.bootable 1

# Set default command
CMD ["/lib/systemd/systemd"]

Complete Project Structure

debian-bootc-base/
├── build-rootfs.sh              # Rootfs build script
├── build-scratch-bootc.sh       # Main build script
├── Containerfile.base           # From existing OCI image
├── Containerfile.scratch        # From scratch build
├── rootfs/                      # Generated rootfs (Method 2)
├── examples/
│   ├── nginx/
│   │   └── Containerfile        # Example nginx layer
│   └── apache/
│       └── Containerfile        # Example apache layer
└── README.md

Usage Examples

Method 1: From Existing Image

# Build base image from existing Debian
./build-base-from-existing.sh

# Build application layer
podman build -f examples/nginx/Containerfile -t debian-bootc-base:nginx .

Method 2: From Scratch

# Build rootfs and base image
./build-scratch-bootc.sh

# Build application layer
podman build -f examples/nginx/Containerfile -t debian-scratch-bootc:nginx .

Validation and Testing

Validate Base Image

# Validate with bootc
bootc container lint debian-bootc-base:latest
bootc container lint debian-scratch-bootc:latest

Test Base Image

# Test the base image
podman run --rm -it debian-bootc-base:latest /bin/bash
podman run --rm -it debian-scratch-bootc:latest /bin/bash

Deploy with bootc

# Install to filesystem
bootc install to-filesystem debian-bootc-base:latest

# Check status
bootc status

Troubleshooting

Common Issues

  1. Permission Errors: Ensure scripts are executable

    chmod +x build-*.sh
    
  2. Rootfs Build Fails: Check internet connection and mirrors

    # Test mirror connectivity
    curl -I http://deb.debian.org/debian/
    
  3. bootc Validation Fails: Check required labels and systemd configuration

    # Check labels
    podman inspect debian-bootc-base:latest | grep -A 10 Labels
    
    # Check systemd
    podman run --rm debian-bootc-base:latest systemctl --version
    
  4. Image Too Large: Optimize by removing unnecessary packages

    # Clean up in rootfs
    sudo chroot rootfs apt autoremove --purge -y
    sudo chroot rootfs apt clean
    

Advanced Configuration

Custom Kernel

# Install specific kernel version
sudo chroot rootfs apt install -y linux-image-6.1.0-10-amd64

Custom Systemd Services

# Add custom service
sudo tee rootfs/etc/systemd/system/myapp.service > /dev/null <<EOF
[Unit]
Description=My Application
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/myapp
Restart=always

[Install]
WantedBy=multi-user.target
EOF

sudo chroot rootfs systemctl enable myapp.service

Security Hardening

# Install security packages
sudo chroot rootfs apt install -y \
    apparmor \
    auditd \
    ufw \
    fail2ban

# Configure AppArmor
sudo chroot rootfs systemctl enable apparmor.service

This comprehensive guide provides everything needed to create base bootc images using both methods, with practical examples and troubleshooting tips.