Overview
Successfully refactored nbc to perform bootc-compatible container installations without using the external bootc command. The application now handles all installation steps natively in Go.
Architecture
Core Components
-
pkg/partition.go - Disk partitioning and formatting
- GPT partition table creation with
sgdisk - EFI (2GB FAT32), Boot (1GB ext4), Root1 (12GB ext4), Root2 (12GB ext4), Var (remaining ext4)
- A/B partition scheme for atomic updates
- Partition mounting and UUID management
- GPT partition table creation with
-
pkg/container.go - Container filesystem extraction
- Extracts container images using go-containerregistry (pure Go, no Docker/Podman required)
- Handles overlay filesystem whiteouts for proper layer merging
- Preserves SUID/SGID/sticky bits on files and directories
- Creates system directories and fstab with systemd auto-discovery
- Supports chroot operations for post-install configuration
-
pkg/bootloader.go - Bootloader installation
- GRUB2 and systemd-boot support (with automatic detection)
- Copies kernels/initramfs from /usr/lib/modules to appropriate boot location
- Configures kernel cmdline with root=UUID and systemd.mount-extra for /var
- Generates bootloader configuration files
- Kernel argument customization
- Secure Boot support: Automatic shim detection and EFI chain setup
- Searches for
shimx64.efi.signedin common locations - Sets up proper boot chain: shim -> bootloader
- Includes MOK manager for key enrollment
- Searches for
-
pkg/bootc.go - Main installation orchestrator
- Coordinates all installation steps
- Provides progress feedback
- Handles cleanup on errors
-
pkg/disk.go - Disk management utilities
- Disk discovery and enumeration
- Validation and safety checks
- Support for SATA, NVMe, virtio, MMC devices
Installation Process
The complete installation workflow (6 steps):
1. Create Partitions
+-- sgdisk creates GPT with EFI/boot/root1/root2/var partitions
+-- EFI: 2GB (ESP partition type, auto-mounted by systemd)
+-- Boot: 1GB (XBOOTLDR partition type, auto-mounted by systemd)
+-- Root1: 12GB (active root for OS A)
+-- Root2: 12GB (inactive root for OS B, for A/B updates)
+-- Var: remaining space (mounted via systemd.mount-extra)
2. Format Partitions
+-- mkfs.vfat for EFI (FAT32)
+-- mkfs.ext4 for boot, root1, root2, var
+-- Retrieve UUIDs with blkid
3. Mount Partitions
+-- Mount root1 -> /tmp/nbc-install
+-- Mount boot -> /tmp/nbc-install/boot
+-- Mount EFI -> /tmp/nbc-install/boot/efi
+-- Mount var -> /tmp/nbc-install/var
4. Extract Container
+-- Pull image layers via go-containerregistry
+-- Extract each layer handling whiteouts
+-- Preserve special file permissions (SUID/SGID)
+-- Extract filesystem to mounted root1
5. Configure System
+-- Create /etc/fstab (minimal, most mounts auto-discovered)
+-- Setup system directories (dev, proc, sys, run, tmp)
+-- Setup /etc overlay for persistence across A/B updates
| +-- Create /var/lib/nbc/etc-overlay/{upper,work} directories
| +-- Install dracut module (95etc-overlay) for overlay mount
+-- Parse os-release for OS name
Note: /etc persistence uses overlayfs with the container's /etc
as lowerdir and /var/lib/nbc/etc-overlay as upperdir. The overlay
is mounted by a dracut module during initramfs phase (pre-pivot).
User modifications are stored in the upperdir on /var and persist
across A/B updates. See docs/ETC-OVERLAY.md for details.
6. Install Bootloader
+-- Detect bootloader type (GRUB2/systemd-boot)
+-- Copy kernel/initramfs from /usr/lib/modules
+-- Install bootloader to EFI partition
+-- Setup Secure Boot chain (if shim available):
| +-- Copy shimx64.efi as BOOTX64.EFI
| +-- Copy bootloader as grubx64.efi (chain-loaded by shim)
| +-- Copy mmx64.efi for MOK key enrollment
+-- Generate boot configuration with:
| +-- root=UUID=<root1-uuid>
| +-- systemd.mount-extra=UUID=<var-uuid>:/var:ext4:defaults
+-- Set proper kernel cmdline arguments
Key Features
No External Dependencies
- All functionality implemented natively in Go
- Uses go-containerregistry (no Docker/Podman required)
- Uses standard Linux tools (sgdisk, mkfs, mount, grub/bootctl)
- More transparent and maintainable
Safety Features
- Comprehensive prerequisite checking
- Mounted partition detection
- Confirmation prompts before destructive operations
- Dry-run mode for testing
- Automatic cleanup on errors
Flexibility
- Multiple device type support (SATA, NVMe, virtio, MMC, loop devices)
- A/B partition scheme for atomic updates
- Custom kernel arguments
- Automatic bootloader detection (GRUB2 vs systemd-boot)
- Systemd Discoverable Partitions integration
- Configurable mount points
- Filesystem choice: ext4 (default) or btrfs for root/var partitions
User Experience
- Clear progress indicators (Step X/6)
- Verbose output option for debugging
- Detailed error messages
- Post-installation verification
Usage Examples
Basic Installation
sudo ./nbc install \
--image quay.io/fedora/fedora-coreos:stable \
--device /dev/sda
With Custom Kernel Args
sudo ./nbc install \
--image localhost/custom-image \
--device /dev/nvme0n1 \
--karg console=ttyS0 \
--karg quiet
Dry Run Test
./nbc install \
--image test/image \
--device /dev/sdb \
--dry-run
Technical Improvements
Removed Dependencies
- bootc command not required
- Docker/Podman not required (uses go-containerregistry directly)
- Uses standard Linux utilities only
Added Functionality
- Direct GPT partitioning with A/B update scheme
- Filesystem creation and mounting
- Container extraction via go-containerregistry (pure Go)
- Overlay filesystem whiteout handling
- Native bootloader installation (GRUB2 and systemd-boot)
- System configuration (fstab, directories)
- Systemd Discoverable Partitions support
- Kernel cmdline generation with systemd.mount-extra
Code Organization
- Modular package structure
- Clear separation of concerns
- Reusable components
- Comprehensive error handling
Testing Recommendations
-
Virtual Machine Testing
# Create a test VM with extra disk # Test with various container images sudo ./nbc install --image <test-image> --device /dev/vdb -
Dry Run Validation
# Verify workflow without changes ./nbc install --image <image> --device /dev/sdX --dry-run -
Different Device Types
- Test on SATA devices (/dev/sda)
- Test on NVMe devices (/dev/nvme0n1)
- Test on virtio devices (/dev/vda)
Future Enhancements
Potential improvements:
- A/B partition scheme (implemented)
- systemd-boot support (implemented)
- /etc persistence via overlayfs (implemented)
- Support for custom partition layouts
- BTRFS/XFS filesystem options
- RAID/LVM support
- Encrypted root filesystem
- Multi-boot configurations
- Progress bars for long operations
- Automatic A/B updates
- Rollback capability
- Pre/post installation hooks
- Secure Boot support (implemented)
Files Modified/Created
New Files:
pkg/partition.go- Partitioning logicpkg/container.go- Container extractionpkg/bootloader.go- Bootloader installation
Modified Files:
pkg/bootc.go- Complete rewrite of Install() methodREADME.md- Updated documentation
Unchanged Files:
pkg/disk.go- Disk management utilitiescmd/*.go- CLI commandsmain.go- Entry point
Dependencies
Required Tools
go-containerregistry(embedded) - Container image operationssgdisk- GPT partitioningmkfs.vfat- FAT32 formatting (EFI partition)mkfs.ext4- ext4 formatting (boot, root, var partitions)mount/umount- Filesystem mountingblkid- UUID retrievalpartprobe- Kernel partition updateudevadm- Device node synchronizationgrub-installorgrub2-install- GRUB bootloader (if using GRUB)bootctl- systemd-boot bootloader (if using systemd-boot)
Go Packages
github.com/spf13/cobra- CLI frameworkgithub.com/spf13/viper- Configurationgithub.com/google/go-containerregistry- Container image handling- Standard library for core logic (os, os/exec, archive/tar, path/filepath)
Conclusion
The nbc tool provides a complete, self-contained solution for installing bootc-compatible containers to physical disks with A/B partition scheme for atomic updates. It eliminates dependencies on external tools like bootc, Docker, or Podman by using native Go libraries (go-containerregistry) and standard Linux utilities. The implementation leverages systemd Discoverable Partitions for automatic mounting and provides a foundation for robust, atomic OS updates.