A Go application for installing bootc-compatible containers to physical disks with A/B partitioning and atomic updates.
nbc is a command-line tool that installs bootc-compatible container images directly to physical disks. It handles the complete installation process including partitioning, filesystem creation, container extraction, and bootloader installation - all without requiring the bootc command itself.
The tool implements an A/B partition scheme for safe, atomic system updates with automatic rollback capability.
For successful installation and updates, the source container image must meet the following requirements:
The kernel and initramfs files must be located in /usr/lib/modules/$KERNEL_VERSION/ within the container image:
- Kernel:
/usr/lib/modules/$KERNEL_VERSION/vmlinuzor/usr/lib/modules/$KERNEL_VERSION/vmlinuz-$KERNEL_VERSION - Initramfs: One of the following in the same directory:
/usr/lib/modules/$KERNEL_VERSION/initramfs.img/usr/lib/modules/$KERNEL_VERSION/initrd.img/usr/lib/modules/$KERNEL_VERSION/initramfs-$KERNEL_VERSION.img/usr/lib/modules/$KERNEL_VERSION/initrd.img-$KERNEL_VERSION
During installation or update, nbc will automatically copy these files from /usr/lib/modules/$KERNEL_VERSION/ to the shared /boot partition.
The container image should follow standard Linux Filesystem Hierarchy Standard (FHS):
/usr: System binaries and libraries (read-only in production)/etc: Configuration files (user modifications merged during A/B updates)/var: Variable data (symlinked to shared partition)/home: User home directories (symlinked to/var/home)/root: Root user home directory (symlinked to/var/roothome)/opt: Optional packages/srv: Service data (symlinked to/var/srv)/tmp: Temporary files (mounted as tmpfs at runtime)
The image should contain:
- Linux kernel modules in
/usr/lib/modules/$KERNEL_VERSION/ - dracut: Required for initramfs generation with etc-overlay module
- System libraries in
/usr/liband/usr/lib64 - Essential binaries in
/usr/binand/usr/sbin - Basic system configuration in
/etc
- systemd: For service management and system initialization
- NetworkManager or similar for network configuration
- SSH server: For remote access
- Package manager: For installing additional software after deployment
Note: Container images can include a pre-built initramfs with the etc-overlay module to skip dracut regeneration during installation. See docs/ETC-OVERLAY.md for details.
For Secure Boot compatibility, the image should include:
- shimx64.efi.signed: The signed shim bootloader (typically from the
shim-signedpackage) - mmx64.efi: MOK (Machine Owner Key) manager for key enrollment (optional)
nbc automatically detects these files and sets up the proper EFI boot chain:
EFI/BOOT/
├── BOOTX64.EFI ← shimx64.efi (Secure Boot entry point)
├── grubx64.efi ← actual bootloader (chain-loaded by shim)
├── mmx64.efi ← MOK manager (for key enrollment)
└── fbx64.efi ← fallback bootloader (optional)
If shim is not found in the image, nbc falls back to direct boot (no Secure Boot).
/
├── usr/
│ ├── bin/
│ ├── lib/
│ ├── lib64/
│ └── lib/modules/
│ └── 6.11.0-1.el9.x86_64/
│ ├── vmlinuz
│ ├── initramfs.img
│ └── kernel/
├── etc/
│ ├── fstab
│ ├── hostname
│ └── systemd/
├── var/ (will be symlinked to shared partition)
├── home/ (will be symlinked to /var/home)
└── root/ (will be symlinked to /var/roothome)
To create a compatible bootc image, ensure your Containerfile/Dockerfile includes kernel installation:
FROM quay.io/centos/centos:stream9
# Install kernel and other packages
RUN dnf install -y kernel kernel-modules initramfs-tools
# Kernel and initramfs will be in /usr/lib/modules/$(uname -r)/
# No need to manually move them - nbc handles the extraction- 🔍 Disk Discovery: List and inspect available physical disks
- ✅ Validation: Verify disks are suitable for installation
- 🚀 Automated Installation: Complete installation workflow with safety checks
- 🔄 A/B Updates: Dual root partition system for safe, atomic updates with rollback
- 🔧 Kernel Arguments: Support for custom kernel arguments
- 💾 /etc Overlay Persistence: User modifications to /etc persist via overlayfs across A/B updates
- 🏷️ Multiple Device Types: Supports SATA (sd*), NVMe (nvme*), virtio (vd*), and MMC devices
- 🛡️ Safety Features: Confirmation prompts and force flag for automation
- 📝 Detailed Logging: Verbose output for troubleshooting
- 🔐 Configuration Storage: Stores image reference for easy updates
- 🔒 Secure Boot Support: Automatic shim detection and Secure Boot chain setup
- 📀 Filesystem Choice: Support for ext4 (default) and btrfs filesystems
- 🔑 Full Disk Encryption: LUKS2 encryption with optional TPM2 automatic unlock
Before using nbc, ensure you have the following installed:
- sgdisk: GPT partition table manipulation tool (usually in
gdiskpackage) - mkfs tools:
mkfs.vfat,mkfs.ext4for filesystem creation - GRUB2:
grub-installorgrub2-installfor bootloader installation - Root privileges: Required for disk operations
Note: Container image handling is built-in using go-containerregistry. No external container runtime (podman/docker) is required!
- Linux operating system (tested on Fedora, Ubuntu, CentOS Stream)
- x86_64 or ARM64 architecture
- Root/sudo access for disk operations
- Minimum 50GB disk space (43GB for system partitions + space for /var)
# Clone the repository
git clone https://github.com/frostyard/nbc.git
cd nbc
# Build the binary
make build
# Install to system (optional)
sudo make installDownload the latest release from GitHub Releases:
# Download for your architecture
curl -LO https://github.com/frostyard/nbc/releases/latest/download/nbc-linux-amd64
# Make executable
chmod +x nbc-linux-amd64
# Install
sudo mv nbc-linux-amd64 /usr/local/bin/nbc# List all available disks
nbc list
# List with verbose output
nbc list -vExample output:
Available disks:
Device: /dev/sda
Size: 238.5 GB (238475288576 bytes)
Model: Samsung SSD 850
Removable: false
Partitions:
- /dev/sda1 (512.0 MB) mounted at /boot/efi
- /dev/sda2 (237.5 GB) mounted at /
Device: /dev/nvme0n1
Size: 1.0 TB (1000204886016 bytes)
Model: Samsung SSD 970 EVO
Removable: false
Partitions: none
# Check if a disk is suitable for installation
nbc validate --device /dev/sda
# Or use device aliases
nbc validate -d /dev/disk/by-id/ata-Samsung_SSD_850# Basic installation
nbc install \
--image quay.io/centos-bootc/centos-bootc:stream9 \
--device /dev/sda
# With btrfs filesystem instead of ext4
nbc install \
--image quay.io/centos-bootc/centos-bootc:stream9 \
--device /dev/sda \
--filesystem btrfs
# With custom kernel arguments
nbc install \
--image quay.io/my-org/my-image:latest \
--device /dev/nvme0n1 \
--karg console=ttyS0 \
--karg quiet
# Skip image pull (use already pulled image)
nbc install \
--image localhost/my-custom-image \
--device /dev/sda \
--skip-pull
# Skip confirmation prompt (for automation)
nbc install \
--image quay.io/example/image:latest \
--device /dev/sda \
--force
# Dry run (test without making changes)
nbc install \
--image quay.io/example/image:latest \
--device /dev/sda \
--dry-run
# With full disk encryption (LUKS2)
nbc install \
--image quay.io/example/image:latest \
--device /dev/sda \
--encrypt \
--passphrase "your-secure-passphrase"
# With encryption + TPM2 automatic unlock
nbc install \
--image quay.io/example/image:latest \
--device /dev/sda \
--encrypt \
--passphrase "your-secure-passphrase" \
--tpm2The A/B update system allows you to safely update your system by installing to an inactive root partition:
# Update to latest version of the installed image
nbc update --device /dev/sda
# Update to a specific image
nbc update \
--image quay.io/my-org/my-image:v2.0 \
--device /dev/sda
# Check if an update is available (without installing)
nbc update --check
# Skip pulling (use already pulled image)
nbc update \
--image localhost/my-image:latest \
--device /dev/sda \
--skip-pull
# Force update without confirmation
nbc update \
--device /dev/sda \
--force
# Force reinstall even if already up-to-date
nbc update --force
# Add custom kernel arguments for the new system
nbc update \
--device /dev/sda \
--karg console=ttyS0 \
--karg debugThe update command automatically compares the installed image digest with the remote image. If they match, the update is skipped (unless --force is used).
After update, reboot to activate the new system. The previous version remains available in the boot menu for rollback.
View the current system status including installed image, digest, and active partition:
# Show current status
nbc status
# Verbose output (includes update check)
nbc status -vExample output:
nbc System Status
====================
Image: quay.io/centos-bootc/centos-bootc:stream9
Digest: sha256:abc123de
Device: /dev/sda
Active Root: /dev/sda3 (Slot A)
Bootloader: grub2
With verbose mode (-v), additional information is shown including install date, kernel arguments, and whether an update is available.
Download container images to a local cache for offline installation or staged updates:
# Download image for embedding in a live ISO (offline installation)
nbc download --image quay.io/example/myimage:latest --for-install
# Download update for later application (uses image from system config)
nbc download --for-update
# Download a specific update image
nbc download --image quay.io/example/myimage:v2.0 --for-update
# JSON output for scripting
nbc download --image quay.io/example/myimage:latest --for-install --jsonStaged images are stored in OCI layout format:
- Installation images:
/var/cache/nbc/staged-install/(supports multiple images) - Update images:
/var/cache/nbc/staged-update/(single image)
# Download update without applying
nbc download --for-update
# Later, apply the staged update
nbc update --local-image# List staged installation images (JSON for GUI installers)
nbc cache list --install-images --json
# List staged update image
nbc cache list --update-images
# Remove a cached image by digest
nbc cache remove sha256:abc123...
# Clear all staged installation images
nbc cache clear --install
# Clear staged update
nbc cache clear --update# Verbose output
nbc install --image IMAGE --device DEVICE -v
# Dry run mode (no actual changes)
nbc install --image IMAGE --device DEVICE --dry-runnbc performs a native installation without requiring the bootc command. The system is designed with A/B partitioning for safe, atomic updates.
nbc creates a GPT partition table with dual root partitions for atomic updates:
- EFI System Partition (2GB, FAT32): UEFI boot files and bootloader
- Boot Partition (1GB, ext4): Shared kernel and initramfs files
- Root Partition 1 (12GB, ext4): First root filesystem (OS A)
- Root Partition 2 (12GB, ext4): Second root filesystem (OS B)
- Var Partition (remaining space, ext4): Shared
/varfor both systems
This layout enables:
- Atomic Updates: Install new version to inactive partition without affecting running system
- Safe Rollback: Previous system remains bootable via GRUB menu
- Shared Data:
/varpartition shared between both systems for persistent data - Zero Downtime: Switch between versions with a simple reboot
The initial installation follows these steps:
- Prerequisites Check: Verifies required tools (sgdisk, mkfs, grub) are available
- Disk Validation: Ensures the target disk meets requirements (size, not mounted)
- Image Pull: Downloads the container image using built-in Go libraries (unless
--skip-pullis used) - Confirmation: Prompts user to confirm data destruction (unless
--forceis used) - Disk Wipe: Removes existing partition tables and filesystem signatures
- Partitioning: Creates the 5-partition GPT layout
- Formatting: Formats all partitions (FAT32 for EFI, ext4 for others)
- Mounting: Mounts partitions in correct order for extraction
- Extraction: Extracts container filesystem to Root Partition 1
- System Setup: Creates
/varstructure and overlay directories - Dracut Setup: Installs etc-overlay dracut module and regenerates initramfs
- Configuration: Creates
/etc/fstab,/etc/nbc/config.json - Bootloader Installation: Installs and configures bootloader with kernel parameters
Updates use the inactive root partition for safe atomic updates:
- Active Detection: Determines which root partition is currently booted
- Target Selection: Selects the inactive partition as update target
- Image Pull: Downloads the new container image (unless
--skip-pullis used) - Mounting: Mounts target partition and boot partition
- Clearing: Removes old content from target partition
- Extraction: Extracts new filesystem to target partition
- Dracut Setup: Installs etc-overlay dracut module and regenerates initramfs
- Conflict Detection: Detects files modified by both user and container
- Bootloader Update: Updates bootloader to boot from new partition by default
- Dual Boot Menu: Creates menu entries for both updated and previous systems
After reboot, the system boots from the new partition with overlay /etc automatically applied. The old partition remains available for rollback via the boot menu.
nbc uses an overlayfs-based approach to persist user modifications to /etc across A/B updates. This is handled by a custom dracut module that runs during early boot.
How it works:
- Initial Install:
/etcfrom the container image is extracted normally - Early Boot: A dracut module (
95etc-overlay) mounts overlayfs over/etc - Overlay Structure:
- Lower layer: Original
/etcfrom container (read-only, moved to/.etc.lower) - Upper layer:
/var/lib/nbc/etc-overlay/upper(writable, stores modifications) - Merged view:
/etc(what the system sees)
- Lower layer: Original
Kernel parameters (automatically configured):
rd.etc.overlay=1- Enable the overlayrd.etc.overlay.var=UUID=xxx- Location of/varpartition for overlay storage
Behavior during updates:
- New container's
/etcbecomes the new lower layer - User modifications in overlay upper are automatically merged at boot
- Conflicts are detected and reported (user modifications take precedence)
Benefits of overlay approach:
- User modifications persist without explicit merge during updates
- Both A and B root partitions share the same overlay on
/var - Clean separation between container configuration and user changes
- Conflict detection warns when container and user both modify the same file
Detecting nbc-managed boot:
A marker file /run/nbc-booted is created on systems installed with nbc, similar to /run/ostree-booted for bootc/ostree systems.
For detailed documentation, see docs/ETC-OVERLAY.md.
After installation, nbc writes a configuration file to /etc/nbc/config.json:
{
"image_ref": "quay.io/example/bootc-image:latest",
"image_digest": "sha256:abc123...",
"device": "/dev/sda",
"install_date": "2025-12-16T10:30:00Z",
"kernel_args": ["console=ttyS0", "quiet"],
"bootloader_type": "grub2"
}This configuration is automatically used during updates:
- image_ref: Used if no
--imageflag is provided - image_digest: Compared with remote digest to detect if update is needed
Create ~/.nbc.yaml for user defaults:
# Enable verbose logging
verbose: false
# Enable dry-run mode by default
dry-run: false
# Default kernel arguments
# kernel-args:
# - console=ttyS0
# - quietSee .nbc.yaml.example for a complete example.
- Unmounted Check: Refuses to install if any partition is mounted
- Size Validation: Ensures disk has minimum 50GB space
- Confirmation Prompt: Requires typing "yes" before wiping disk (unless
--force) - Dry Run Mode: Test operations without making changes
- Verbose Logging: Track exactly what's happening
- A/B Rollback: Previous system always available in boot menu
Install GRUB2:
# Fedora/RHEL/CentOS
sudo dnf install grub2-efi-x64 grub2-tools
# Ubuntu/Debian
sudo apt install grub-efi-amd64 grub2-commonInstall gdisk:
# Fedora/RHEL/CentOS
sudo dnf install gdisk
# Ubuntu/Debian
sudo apt install gdiskInstall podman:
# Fedora/RHEL/CentOS
sudo dnf install podman
# Ubuntu/Debian
sudo apt install podmanEnsure you're using the correct device path. Use nbc list to see available devices.
Unmount all partitions before installation:
sudo umount /dev/sda1
sudo umount /dev/sda2
# etc...Run nbc with sudo:
sudo nbc install --image IMAGE --device DEVICE- A/B Updates - Detailed documentation on the A/B update system
- /etc Overlay Persistence - How /etc modifications persist across updates
- Secure Boot - Secure Boot chain setup and troubleshooting
- Encryption - Full disk encryption with LUKS2 and TPM2
- Incus Integration Tests - VM-based testing documentation
- Implementation Details - Technical implementation details
# Run unit tests (no root required)
make test-unit
# Run linter
make lintIntegration tests require root privileges to perform disk operations:
# Run basic integration tests (loop devices)
sudo make test-integration
# Run bootc installation tests
sudo make test-install
# Run A/B update tests
sudo make test-updateFor comprehensive end-to-end testing in isolated virtual machines:
# Install Incus first: https://linuxcontainers.org/incus/docs/main/installing/
# Initialize Incus: incus admin init
# Run full integration tests in Incus VM
sudo make test-incusThe Incus test suite:
- Creates an isolated VM with dedicated virtual disk
- Tests complete installation workflow
- Verifies partition layout and bootloader
- Tests A/B update functionality
- Validates kernel/initramfs installation
- Checks GRUB configuration for both boot options
- Automatically cleans up all resources
Note: Incus tests take 10-20 minutes depending on network speed and system performance.
Contributions are welcome! Please feel free to submit issues or pull requests.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT License - see LICENSE file for details.
- go-containerregistry - Pure Go library for working with container images
- Cobra - CLI framework
- Viper - Configuration management
- GRUB2 - Bootloader
- bootc - Transactional, in-place operating system updates using OCI/Docker container images
- go-containerregistry - Go library for working with container registries
- OSTree - Operating system and container image management
Always double-check the device path before running install commands. Use --dry-run to test without making changes.
- someone ought to actually test this
- root mount RO
- export container as squashfs/erofs/similar, mount that instead of fs copy