How to Use Custom Linux Kernels with the container run Command

Override the default Linux kernel at runtime by passing the -k or --kernel flag followed by the absolute path to your kernel binary when executing container run.

The Apple Container runtime ships with a default Linux kernel that is automatically downloaded and cached for macOS virtualization. However, workloads requiring specific kernel features—such as nested virtualization (CONFIG_KVM), custom security modules, or hardware-specific drivers—can substitute a custom kernel binary directly at launch time.

Override the Default Kernel with the --kernel Flag

When you supply the --kernel <path> argument (or -k for short), the CLI bypasses the default kernel download logic and forwards the specified binary path directly to the runtime service. The runtime loads the binary from disk, appends any kernel command-line arguments from the configuration, and boots the VM backing your container.

This override is ephemeral for that specific container invocation unless you configure it permanently using container machine.

Architecture and Source Code Implementation

ContainerSystemConfig and Kernel Configuration

The default kernel behavior is defined in [ContainerSystemConfig.swift](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/ContainerSystemConfig.swift). This file contains a kernel struct with properties for binaryPath and url that determine which kernel binary to use and where to download it if missing.

When you specify --kernel, the CLI overrides the binaryPath field for that session only, preventing the KernelService from downloading or referencing the default kernel.

RuntimeConfiguration and Validation

The kernel path flows through [RuntimeConfiguration.swift](https://github.com/apple/container/blob/main/Sources/Services/Runtime/RuntimeClient/RuntimeConfiguration.swift), which carries the kernel object to the low-level runtime during container creation. Before the VM boots, the runtime validates the binary using MachineConfig.validateKernelPath, ensuring:

  • The file exists at the specified path
  • The binary matches the host architecture (currently arm64 for Apple Silicon)

If validation fails, the command returns an error before the VM initialization begins.

KernelService and XPC Communication

The [KernelService.swift](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Server/Kernel/KernelService.swift) manages kernel installation into the managed kernels/ directory and exposes the default kernel via XPC. When using a custom kernel path, this service is bypassed because the binary already exists on disk.

The [KernelHarness.swift](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Server/Kernel/KernelHarness.swift) handles the XPC messages that receive the custom kernel path from the CLI and forward it to the virtualization layer.

Practical Examples

Run a container with a KVM-enabled kernel for nested virtualization:


# Use a custom kernel with KVM support

container run -k /opt/kernels/vmlinux-kvm \
              --virtualization \
              --name my-vm \
              ubuntu:22.04 \
              /bin/bash -c "uname -a && dmesg | grep kvm"

Combine with resource limits and volume mounts:

container run -k ./vmlinux-custom \
              --virtualization \
              -c 2 -m 4G \
              --volume /tmp/data:/data \
              alpine:3.18 \
              sh -c 'ls /data && cat /proc/cpuinfo'

Persisting Kernel Configuration

To persist a custom kernel across multiple container runs, use the container machine subcommand. This stores the kernel path in the machine's boot-config.json.

Create a new machine with a permanent custom kernel:

container machine create --virtualization \
                         --kernel ./vmlinux-kvm \
                         my-machine

Reset to the default kernel by clearing the override:

container machine set -n my-machine kernel=

Validation Requirements and Constraints

Before the runtime accepts a custom kernel:

  • Architecture matching: The kernel must be compiled for arm64 to match Apple Silicon hosts
  • File accessibility: The path must point to an existing file readable by the current user
  • Virtualization flag: The --virtualization flag is typically required when using custom kernels with the run command

Summary

Frequently Asked Questions

Can I use any Linux kernel binary with container run?

No, the kernel must be compiled for the arm64 architecture to match Apple Silicon hosts. The runtime validates the binary architecture through MachineConfig.validateKernelPath before booting the VM. Additionally, the kernel should support the virtualization features required by the Container runtime.

How do I reset to the default kernel after using a custom one?

For ephemeral overrides, simply omit the --kernel flag on your next container run command. For persistent machine configurations, run container machine set -n <machine-name> kernel= to clear the custom path and revert to the default kernel managed by [KernelService.swift](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Server/Kernel/KernelService.swift).

Does the custom kernel path persist across container restarts?

Only if you configure it through container machine create or container machine set. The --kernel flag passed directly to container run applies only to that specific container instance and does not modify the underlying machine configuration stored in boot-config.json.

What happens if the custom kernel binary is missing or invalid?

The CLI returns a validation error before attempting to create the VM. The MachineConfig.validateKernelPath routine checks file existence and architecture compatibility, failing fast with a descriptive error if the kernel binary cannot be used.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →