PREVIEW: Moving Pantavisor from LXC 3 to LXC 6

We’re excited to share that Pantavisor is taking its first steps toward upgrading from LXC 3.x to LXC 6.x as the container runtime. Starting now, our Raspberry Pi and Docker/x86_64 appengine builds ship with LXC 6.0.5 by default — and we plan to roll this out across all supported platforms once we’ve gained enough confidence in the field.

Why LXC 6?

Pantavisor has been running on a patched fork of LXC 3.0.4 since the early days. LXC 3.x has served us well, but it’s showing its age:

  • Meson build system — LXC 6 moved from autotools to Meson, which makes cross-compilation for embedded targets significantly cleaner
  • Modern cgroup v2 — first-class cgroupv2 support and the unified cgroup hierarchy that modern kernels default to
  • Improved security — years of upstream hardening, better capability handling, and tighter namespace isolation
  • Active maintenance — LXC 6.0 is the current LTS release with ongoing security fixes; LXC 3.x has been EOL for years
  • io_uring event loop — optional high-performance event loop (we disable this for embedded, but it’s available for server workloads)

Our LXC Fork: pantavisor/lxc

Pantavisor uses LXC as a library, not as a standalone container manager. This means we need a few custom patches that go beyond what upstream LXC provides. We maintain these in our fork at github.com/pantavisor/lxc.

The LXC 3.x fork: 51 commits, 8 years of history

Our LXC 3.x branch (stable-3.0-BASE-2c5c780762) accumulated 51 custom commits over 8 years from 7 contributors. These patches cover everything from the initial Pantavisor build integration to namespace management, mount handling, and cgroup configuration.

The LXC 6.x fork: 12 clean commits

For the 6.0 rebase, we carefully triaged all 51 patches from the 3.x branch. Many were no longer needed — build system patches (autotools → Meson made 7 patches obsolete), CI infrastructure, merge commits, and fixes that upstream had already incorporated. We consolidated the remaining essential patches into 12 clean commits on top of upstream LXC 6.0.5:

# Patch What it does
1 pv_export.h Export LXC namespace enum constants for Pantavisor’s container runtime integration
2 set_container_type() API to override the container= env var so PID 1 sees pv-<group> instead of lxc
3 lxc.rootfs.bdev_type Expose block device type via config API (needed for overlay root filesystem setup)
4 realpath_x() Custom realpath that resolves symlinks within a virtual root boundary (FreeBSD-based)
5 origin=mkdir Mount option to create source directories on the host before bind-mounting
6 Escaped quotes in init.cmd State-machine parser for lxc.init.cmd supporting escaped quotes and backslashes
7 cgroup prefix lxc/ Use lxc/<name> cgroup path instead of upstream’s lxc.payload.<name>
8 Hook command parameters Allow start hooks to include command-line arguments
9 Overlay upperdir flexibility Allow overlay upper/workdir to live outside the container directory
10 Exports mount support Preserve /exports mounts and shared propagation for Pantavisor’s inter-container file sharing
11 SIGCHLD blocking in run_buffer Prevent Pantavisor from reaping hook child processes before LXC can collect their exit status
12 Rebase documentation Full mapping of which 3.x patches were ported, dropped, or consolidated, and why

What changed in the rebase

Porting from 3.x to 6.x wasn’t a mechanical cherry-pick. The LXC codebase changed substantially:

  • Data structures: struct lxc_list was replaced with kernel-style struct list_head linked lists
  • String handling: New helpers like strnequal(), strequal() replaced raw strcmp patterns
  • Mount options: Now uses struct lxc_mount_options with bitfields and a dedicated parser parse_lxc_mount_attrs()
  • Code reorganization: Functions moved between files (e.g., run_buffer moved to utils.c)

Several groups of related patches were consolidated:

  • 6 realpath-related commits → 1 clean commit
  • 3 init.cmd/quote parsing commits → 1 state-machine implementation
  • 2 SIGCHLD/reaping fixes → 1 comprehensive fix
  • set_container_type + log capture + pv-root handling → 1 combined patch

Upstreaming Plan

Looking at our 11 functional patches, several are good candidates for upstreaming to the LXC project:

Strong candidates:

  • realpath_x() — Useful for any system embedding LXC where mount targets can be symlinks within a container root
  • Escaped quotes in init.cmd — General-purpose improvement; the upstream parser doesn’t handle escaped characters
  • Hook command parameters — Currently hooks can’t take arguments, which seems like an oversight
  • origin=mkdir — Complements the existing create=dir option; useful beyond Pantavisor

Pantavisor-specific (likely staying in fork):

  • set_container_type() — Very specific to Pantavisor’s container identification needs
  • pv_export.h — Header for Pantavisor’s namespace integration
  • cgroup prefix — Intentional divergence from upstream naming convention
  • Exports mount support — Tied to Pantavisor’s inter-container communication model

We’ll be working on preparing these patches for upstream submission in the coming weeks. Our goal is to minimize the delta between our fork and upstream LXC, making maintenance easier and giving back to the LXC community.

How to Try It

Enabled by default (Raspberry Pi and Docker/x86_64)

If you’re building for raspberrypi-armv8 or docker-x86_64, LXC 6.x is now the default. No changes needed — just build as usual:

./kas-container build kas/machines/raspberrypi-armv8.yaml:kas/scarthgap.yaml:kas/bsp-base.yaml:kas/with-lxc-next.yaml:.github/configs/build-base-starter.yaml

Enable on any other platform

Add kas/with-lxc-next.yaml to your KAS config chain:

./kas-container build <your-config>:kas/with-lxc-next.yaml

This appends lxc-next to PANTAVISOR_FEATURES, which switches the LXC dependency from lxc-pv (3.x) to lxc6-pv (6.x).

Verify

After building, check your image includes lxc6-pv:

# In bitbake build environment
bitbake-getvar -r pantavisor RDEPENDS
# Should show lxc6-pv instead of lxc-pv

What’s Next

  • Broader rollout: Once we’re confident with Raspberry Pi and Docker builds, we’ll enable lxc-next for all platforms
  • Upstream submissions: Prepare and submit patches that benefit the wider LXC community
  • cgroup.relative: We recently added lxc.cgroup.relative support on our 3.x branch and plan to port this to 6.x as well
  • Default promotion: Eventually lxc-next will become the default and lxc-pv (3.x) will be deprecated

We’d love to hear from anyone testing LXC 6.x with Pantavisor — please share your experiences, issues, or questions in this thread!