Skip to main content

Debugging XPC helpers

The container system uses XPC helpers for various services. You can attach a debugger to these helpers to troubleshoot issues or understand their behavior.

Finding service labels

First, identify the launchd service labels for the XPC helpers you want to debug:
1

Start container services

Start the container system:
container system start
2

Create a test container

Run a container to ensure all services are active:
container run -d --name test debian:bookworm sleep infinity
3

List container services

Use launchctl to find the service labels:
launchctl list | grep container
Example output:
27068   0       com.apple.container.container-network-vmnet.default
27072   0       com.apple.container.container-core-images
26980   0       com.apple.container.apiserver
27331   0       com.apple.container.container-runtime-linux.test

Attaching the debugger

To debug a specific XPC helper, use the CONTAINER_DEBUG_LAUNCHD_LABEL environment variable:

Debug a specific service

To debug a single service by its exact label:
export CONTAINER_DEBUG_LAUNCHD_LABEL=com.apple.container.container-runtime-linux.test
container system start
Only the service com.apple.container.container-runtime-linux.test will wait for the debugger to attach.

Debug multiple services by prefix

To debug all services matching a label prefix:
export CONTAINER_DEBUG_LAUNCHD_LABEL=com.apple.container.container-runtime-linux
container system start
Every service starting with com.apple.container.container-runtime-linux will wait for the debugger.
Services configured to wait for a debugger will pause at startup until you attach a debugger to them.

Debugging workflow

Here’s the complete workflow for debugging an XPC helper:
1

Stop existing services

If container services are running, stop them:
container system stop
2

Set debug environment variable

Configure which service(s) to debug:
export CONTAINER_DEBUG_LAUNCHD_LABEL=com.apple.container.container-runtime-linux.test
3

Start services

Start the container system with the debug configuration:
container system start
4

Trigger service launch

Run the command that will launch the service you want to debug. For a runtime helper:
container run -it --name test debian:bookworm
The command will appear to hang:
⠧ [6/6] Starting container [0s]
This is expected - the service is waiting for the debugger.
5

Attach debugger

In Xcode or your debugger of choice, attach to the waiting process. The process will resume once the debugger is attached.
6

Debug as needed

Set breakpoints, step through code, and inspect variables as you would with any other debugging session.

Common debugging scenarios

Debugging the runtime helper

To debug container startup and execution:
export CONTAINER_DEBUG_LAUNCHD_LABEL=com.apple.container.container-runtime-linux
container system start
container run -it --name mycontainer debian:bookworm bash

Debugging the network helper

To debug network configuration:
export CONTAINER_DEBUG_LAUNCHD_LABEL=com.apple.container.container-network-vmnet
container system start
container network create mynetwork

Debugging the image helper

To debug image operations:
export CONTAINER_DEBUG_LAUNCHD_LABEL=com.apple.container.container-core-images
container system start
container pull debian:bookworm

Debugging the API server

To debug the main API server:
export CONTAINER_DEBUG_LAUNCHD_LABEL=com.apple.container.apiserver
container system start
When debugging the API server, almost any container command will trigger the wait state since the server handles all requests.

Development tips

Using the pre-commit hook

Install the pre-commit hook to ensure your changes have correct formatting and license headers:
make pre-commit
This installs a hook that runs automatically when you use git commit.
The pre-commit hook helps maintain code quality and consistency across the project.

Project location considerations

Due to a vmnet framework bug, avoid placing your project under ~/Documents or ~/Desktop:
# Good locations
~/projects/container
~/Development/container
~/code/container

# Problematic locations (vmnet bug)
~/Documents/container
~/Desktop/container
If you use make install, this limitation doesn’t apply since the binaries are installed to /usr/local.

Cleaning build artifacts

When switching between debug and release builds, or after major changes:
make clean all
This ensures a fresh build without cached artifacts.

Using local dependencies

When working on features that span multiple repositories:
  1. Clone related repositories (Containerization, container-builder-shim) alongside your container clone
  2. Use swift package edit to link to local copies
  3. Make changes across repositories
  4. Test thoroughly with make test integration
  5. Revert to normal dependencies before committing
See the building guide for detailed instructions.

Debugging with logging

Container services use the macOS unified logging system. View logs using:
# View all container logs
log stream --predicate 'subsystem CONTAINS "container"'

# View logs for a specific process
log stream --predicate 'processImagePath CONTAINS "container-runtime"'
You can also use Console.app for a graphical log viewer.
Structured logging provides valuable context for debugging without requiring a debugger attachment.

Testing with different macOS versions

While macOS 26 is recommended, you may need to test on macOS 15:
  • Be aware of vmnet limitations (no container-to-container networking)
  • Network configuration may require manual adjustment
  • Some features may not be available
Issues that only reproduce on macOS 15 may not be addressed if they work correctly on macOS 26.

Build docs developers (and LLMs) love