How to Use Custom Linux Kernels with Apple Container for Specific Kernel Features
Apple Container lets you override the default kata-containers kernel with any architecture-matching Linux binary by using the container system kernel set command for global defaults or the --kernel flag in machine configurations, enabling specialized features like nested virtualization that require compile-time flags such as CONFIG_KVM=y.
Apple Container runs Linux-based guests inside lightweight VMs on macOS, automatically downloading a kernel that matches the host version. When your workloads require specific compile-time features—such as CONFIG_KVM=y for nested virtualization or custom hardware drivers—you can configure custom Linux kernels with container to replace the default runtime binary.
Architecture Overview
Apple Container manages kernel selection through three primary components that bridge user configuration with the hypervisor.
ContainerSystemConfig
The ContainerSystemConfig structure in Sources/ContainerPersistence/ContainerSystemConfig.swift stores the default kernel information used by the runtime, including binaryPath and url fields. You can override these defaults via the CLI or configuration files to change the system-wide kernel behavior.
MachineConfig
Per-machine settings are stored in Sources/ContainerPersistence/MachineConfig.swift, which parses the kernel field from TOML configuration files. When you create or update a machine, this value is read and passed to the runtime, allowing different machines to run different kernel versions simultaneously.
RuntimeService
The RuntimeService in Sources/Services/RuntimeLinux/Server/RuntimeService.swift initializes the VM by reading RuntimeConfiguration.kernel.path and feeding the specified binary directly to the hypervisor. This ensures the custom kernel is loaded before the guest OS boots.
When to Use a Custom Kernel
You need a custom Linux kernel with container when the default kata-containers build lacks specific capabilities required by your workload:
- Nested virtualization: Running KVM inside the container requires a kernel compiled with
CONFIG_KVM=y, which the default kernel disables for security. - Hardware drivers: Accessing specialized peripherals or security modules requires kernel drivers not included in the default build.
- Kernel testing: Validating patches or specific kernel versions before upstreaming them to production environments.
Prerequisites
Before loading a custom kernel, Apple Container validates two requirements:
- Architecture match: The binary must target the host CPU architecture (
arm64for Apple Silicon,amd64for Intel). The CLI defaults toarm64on Apple Silicon Macs. - Feature availability: While the runtime does not validate
CONFIG_flags at boot, the kernel must be compiled with the features your workload expects (e.g.,CONFIG_KVM=yfor nested virtualization on Apple Silicon M3+ and macOS 15+).
Configuring Custom Linux Kernels
You can configure kernels at the system level (affecting all new machines) or per-machine (overriding the system default).
Set a System-Wide Default Kernel
Use container system kernel set to install a custom binary as the default for all newly created machines:
# Install a custom kernel from a local binary
container system kernel set \
--binary ./vmlinux \
--force
The --binary flag accepts a raw kernel file or a path within a tarball when combined with --tar. This updates the binaryPath in ContainerSystemConfig.
Configure Per-Machine Kernel Overrides
To use a specific kernel for an individual machine without changing the system default:
# Create a new machine with a custom kernel
container machine create \
--name dev \
--virtualization \
--kernel ./vmlinux-kvm \
alpine:latest
# Or update an existing machine
container machine set \
-n dev \
virtualization=true \
kernel=/opt/kernels/vmlinux-kvm
The kernel value is stored in the machine's TOML configuration at ~/.container/machines/<name>/boot-config.json.
Verify the Kernel Inside the Container
After starting the container, verify the running kernel matches your custom binary:
container run --name test --virtualization \
--kernel ./vmlinux-kvm \
ubuntu:latest \
sh -c "uname -r && dmesg | grep kvm"
The uname -r output should display the version string of your custom kernel, confirming that RuntimeService loaded the correct binary.
Revert to the Default Kernel
To clear a machine-specific override and return to the system default:
container machine set -n dev kernel=
Restart the machine to apply the change.
Practical Examples
Enabling Nested Virtualization
To run nested KVM workloads, supply a kernel compiled with CONFIG_KVM=y:
# Assuming vmlinux-kvm is built with CONFIG_KVM=y
container machine create \
-n nested \
--virtualization \
--kernel ./vmlinux-kvm \
ubuntu:latest
Switching Kernels on Existing Machines
You can change kernels without destroying the machine's persistent storage:
container machine set -n nested kernel=./vmlinux-new
container machine stop -n nested
container machine start -n nested
Installing from a Tarball
Download and extract a kernel before setting it as the system default:
curl -L https://example.com/custom-kernel.tar.zst -o kernel.tar.zst
tar --extract --file=kernel.tar.zst --strip-components=1 --wildcards '*/vmlinux'
container system kernel set \
--binary ./vmlinux \
--force
Summary
- Custom Linux kernels with container enable specialized features like nested virtualization that the default kata-containers kernel excludes.
- System-wide defaults are controlled via
ContainerSystemConfigand set usingcontainer system kernel set --binary <path>. - Per-machine overrides are stored in
MachineConfig(parsed from TOML inSources/ContainerPersistence/MachineConfig.swift) and specified via--kernelorkernel=in the CLI. - Runtime loading is handled by
RuntimeServiceinSources/Services/RuntimeLinux/Server/RuntimeService.swift, which passes the binary path to the hypervisor. - Clear machine-specific overrides by setting
kernel=(empty value) to revert to system defaults.
Frequently Asked Questions
How do I verify that my custom kernel is actually running inside the container?
Run uname -r inside the container to check the version string matches your custom build. You can also check for specific features with dmesg | grep <feature> or examine /boot/config-$(uname -r) if your kernel build includes the config file.
What is the difference between the system kernel and machine kernel in Apple Container?
The system kernel (managed in ContainerSystemConfig.swift) serves as the default for all newly created machines, while the machine kernel (stored in MachineConfig.swift) overrides this on a per-machine basis. If a machine has no kernel specified, it falls back to the system default.
Can I use any Linux kernel binary, or does it need specific patches for Apple Container?
You can use any Linux kernel binary that matches the host architecture (arm64 or amd64). The kernel does not require Apple-specific patches, but it must include the features your workload requires (e.g., CONFIG_KVM for nested virtualization).
Does Apple Container support nested virtualization on all Macs?
Nested virtualization requires Apple Silicon M3 or later running macOS 15 or newer. Additionally, you must provide a custom kernel compiled with CONFIG_KVM=y, as the default kernel shipped with Apple Container disables this flag.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →