To build the container project, you need:
- Mac with Apple silicon
- macOS 15 minimum, macOS 26 recommended
- Xcode 26, set as the active developer directory
- A running
containerinstallation, to build thecluster-shimLinux binary in a container. Builds leavecluster-shimout with a warning whencontaineris not running, and clusters from those builds cannot use--shared-image-store. To use a prebuilt binary instead, setCLUSTER_SHIM_BINARYto its path.
Important
There is a bug in the vmnet framework on macOS 26 that causes network creation to fail if the container helper applications are located under your Documents or Desktop directories. If you use make install, you can simply run the container binary in /usr/local. If you prefer to use the binaries that make all creates in your project bin and libexec directories, locate your project elsewhere, such as ~/projects/container, until this issue is resolved.
Build container and the background services from source, then run the unit and integration tests in an isolated application data directory:
make verifyThis is the quickest way to verify your toolchain after cloning. make verify runs the all, test, and integration targets. The integration suite boots virtual machines and downloads a kernel and images, so the first run takes a while.
Important
make integration stops any container services you already have running and leaves them stopped when it finishes; run container system start afterward to get back to your own installation.
| Target | What it does |
|---|---|
make verify |
Runs all, test, and integration. |
make all |
Compiles, then installs the binaries into bin/ and libexec/ in your project directory. |
make test |
Runs the unit tests. |
make integration |
Runs the CLI and integration test suites. These start the container services, create containers, and boot virtual machines. |
make install |
Builds an installer package and installs it to /usr/local. |
make clean |
Runs cleanclustershim, cleantest, then cleanbuild. |
make cleanclustershim |
Removes the cluster-shim binary, build image, and build volume. |
make cleanbuild |
Removes bin/, libexec/, generated docs, and coverage output. |
make cleantest |
Stops the container services and removes the APP_ROOT and SCRATCH_ROOT directories. |
By default, the test targets use .test-data in your project directory, so they do not disturb the images, containers, and volumes you use day to day, which live in ~/Library/Application Support/com.apple.container.
APP_ROOT sets that directory, and is passed as --app-root to the services the test targets start.
How make integration treats APP_ROOT:
- It erases the directory's contents before each run.
- Set
APP_ROOT=(empty) to run against~/Library/Application Support/com.apple.containerinstead. The tests will not erase it, but they will create and delete containers in it. Use this when you want the tests to exercise a realistic installation.
Important
If you have an instance of container running, stop it using container system stop before starting the dev CLI. Each user account has a single set of com.apple.container.* services, so a test run cannot coexist with an installation you are using.
# 1. Build
make all
# 2. Unit tests
make test
# 3. Test the dev CLI
./bin/container system start --app-root .test-data| Variable | Default | Effect |
|---|---|---|
PRESERVE_KERNELS |
false |
Set to true to keep the kernels directory when the run clears APP_ROOT, so repeated runs do not download the kernel again. |
LOG_ROOT |
unset | Passes --log-root to the services, and writes swift-testing event streams to the given path. |
A typical iteration on the integration tests:
make PRESERVE_KERNELS=true LOG_ROOT=.test-data/logs integrationCopy the binaries to /usr/local/bin and /usr/local/libexec (requires entering an administrator password):
make installOr to install a release build, with better performance than the debug build:
BUILD_CONFIGURATION=release make verify
BUILD_CONFIGURATION=release make installRelease builds strip local symbols from the installed binaries and leave out -enable-testing, which keeps the installer package smaller. The goals that build or run tests still enable testing, so BUILD_CONFIGURATION=release make verify works as above. To change this, set STRIP_BINARIES=false to keep the symbols, STRIP_OPTS= to use a plain strip, or ENABLE_TESTING=true to build a testable release.
Use make dsym after building to archive the debug symbols for every installed executable in bin/<configuration>/bundle/container-dSYM.zip. Keep this archive with each release, because the stripped binaries rely on it to symbolicate crash reports. The target generates a dSYM when the build did not produce one, and fails if a dSYM does not match its binary.
Remove the cluster-shim build files, stop the services, remove the build outputs, and delete the test directories:
make cleanUse make cleanbuild to remove only the build outputs, or make cleantest to stop the services and remove only the APP_ROOT and SCRATCH_ROOT directories.
container uses gRPC to communicate to the builder virtual machine that creates images from Dockerfiles, and depends on specific versions of grpc-swift and swift-protobuf. If you make changes to the gRPC APIs in the container-builder-shim project, install the tools and re-generate the gRPC code in this project using:
make protosTo make changes to container that require changes to the Containerization project, or vice versa:
-
Clone the Containerization repository such that it sits next to your clone of the
containerrepository. Ensure that you follow containerization instructions to prepare your build environment. -
In your development shell, go to the
containerproject directory.cd container -
If the
containerservices are already running, stop them.bin/container system stop
-
Reconfigure the Swift project to use your local
containerizationpackage and update yourPackage.resolvedfile./usr/bin/swift package edit --path ../containerization containerization /usr/bin/swift package update containerization
[!IMPORTANT] If you are using Xcode, do not run
swift package edit. Instead, temporarily modifyPackage.swiftto replace the versionedcontainerizationdependency:.package(url: "https://github.com/apple/containerization.git", exact: Version(stringLiteral: scVersion)),
with the local path dependency:
.package(path: "../containerization"),
Note: If you have already run
swift package edit, whether intentionally or by accident, follow the steps in the next section to restore the normalcontainerizationdependency. Otherwise, the modifiedPackage.swiftfile will not work, and the project may fail to build. -
If you want
containerto use any changes you made in thevminitsubproject of Containerization, set the init image in your runtime configuration file at~/.config/container/config.toml:[vminit] image = "vminit:latest"
-
Build
container.make APP_ROOT= clean all -
Restart the
containerservices.bin/container system stop bin/container system start
To revert to using the Containerization dependency from your Package.swift:
-
If you were using the local init filesystem, remove the
initoverride from your~/.config/container/config.toml(or delete the[vminit]section if no other image settings are present). -
Use the Swift package manager to restore the normal
containerizationdependency and update yourPackage.resolvedfile. If you are using Xcode, revert yourPackage.swiftchange instead of usingswift package unedit./usr/bin/swift package unedit containerization /usr/bin/swift package update containerization
-
Rebuild
container.make clean all
-
Restart the
containerservices.bin/container system stop bin/container system start
To test changes that require the container-builder-shim project:
-
Clone the container-builder-shim repository and navigate to its directory.
-
After making the necessary changes, build the custom builder image, set it as the active builder image in
~/.config/container/config.toml, and remove the existingbuildkitcontainer so the new image will be used:
container build -t builder .
container rm -f buildkitAdd the following to your ~/.config/container/config.toml:
[build]
image = "builder:latest"- Run the
containerbuild as usual:
container build ...Important
If your modified builder image is broken, make sure to rebuild and correctly tag the builder image before attempting to build container-builder-shim again.
Attach debugger to the XPC helpers using their launchd service labels:
-
Find launchd service labels:
% container system start % container run -d --name test debian:bookworm sleep infinity test % launchctl list | grep container 27068 0 com.apple.container.container-network-vmnet.default 27072 0 com.apple.container.container-core-images 27073 0 com.apple.container.container-core-clusters 26980 0 com.apple.container.apiserver 27331 0 com.apple.container.container-runtime-linux.test
-
Stop container and start again after setting the environment variable
CONTAINER_DEBUG_LAUNCHD_LABELto the label of service to attach debugger. Services whose label starts with theCONTAINER_DEBUG_LAUNCHD_LABELwill wait the debugger:% 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` waits debugger
% export CONTAINER_DEBUG_LAUNCHD_LABEL=com.apple.container.container-runtime-linux % container system start # Every service starting with `com.apple.container.container-runtime-linux` waits debugger
-
Run the command to launch the service, and attach debugger:
% container run -it --name test debian:bookworm ⠧ [6/6] Starting container [0s] # It hangs as the service is waiting for debugger
Run make pre-commit to install a pre-commit hook that ensures that your changes have correct formatting and license headers when you run git commit.