How to Export and Import Container Filesystems in the Apple Container Engine
The Apple Container Engine provides container export and container import CLI commands that wrap XPC messages to the daemon, enabling you to archive a stopped container’s root filesystem to a tar file and restore it as a new container.
The apple/container repository ships a lightweight container runtime that supports filesystem snapshots through a pair of CLI subcommands. These commands allow you to move container root filesystems between hosts or create backups by serializing the entire container contents into a tar archive. Understanding how to export and import container filesystems correctly requires knowing the underlying XPC communication pattern and temporary file handling implemented in the Swift source code.
Exporting Container Filesystems
The container export command creates a portable archive of a stopped container’s root filesystem. According to the source code in Sources/ContainerCommands/Container/ContainerExport.swift, this command requires the target container to be in a stopped state before the daemon will allow the operation.
How the Export Command Works
The implementation follows a four-step workflow that ensures atomic operations and proper cleanup:
-
Temporary directory creation: The CLI creates a fresh temporary directory using
FileManager.default.temporaryDirectoryand writes the archive toarchive.tarinside this location. -
Daemon communication: The command constructs an
XPCMessagewith routecontainerExportand sends it viaContainerClient.export(id:archive:)as defined inSources/Services/ContainerAPIService/Client/ContainerClient.swift. The daemon streams the container’s root filesystem directly into the tar file. -
Output handling: If you omit the
--outputflag, the command pipes the tar bytes from the temporary file to stdout, allowing Unix redirection. If you specify an output path with-o, the temporary file is atomically moved to that location. -
Cleanup: A
deferblock removes the temporary directory regardless of success or failure, guaranteeing no stray files remain on the host.
CLI Usage Examples
Export to stdout for redirection:
container export abc123 > abc123.tar
Export directly to a specific path:
container export abc123 -o /backups/abc123.tar
Importing Container Filesystems
The container import command reverses the process, creating a new container from an existing tar archive. This functionality is implemented in Sources/ContainerCommands/Container/ContainerImport.swift and follows a similar architecture to the export command.
How the Import Command Works
The import command creates a temporary directory for the incoming tar payload, then sends an XPCMessage with route containerImport to the daemon. As registered in Sources/APIServer/APIServer+Start.swift, the daemon handles this route by reading the tar payload and building a new container image from it, returning a fresh container ID that can be started immediately.
The command supports both file-based imports and stdin streams, with the same cleanup logic running after completion to remove temporary files.
CLI Usage Examples
Import from a file:
container import abc123.tar
Import from stdin using the - shorthand:
cat abc123.tar | container import -
Understanding the XPC Architecture
Both commands rely on XPC (Inter-Process Communication) to communicate with the privileged daemon. The client-side API in ContainerClient.swift abstracts the message construction, while APIServer+Start.swift registers the route handlers that map XPCRoute.containerExport and XPCRoute.containerImport to their respective implementation functions.
This architecture ensures that filesystem operations run with appropriate privileges while the CLI remains unprivileged, following the principle of least privilege common in container runtimes.
Summary
- Export requires stopped containers: The daemon explicitly rejects attempts to export running containers to ensure filesystem consistency.
- Tar format: Both commands use standard tar archives, compatible with tools like
docker exportanddocker import. - Temporary file management: All operations use
FileManager.default.temporaryDirectorywith guaranteed cleanup viadeferblocks. - Stdin support: The import command accepts
-to read from stdin, enabling pipeline workflows. - Source locations: Export logic lives in
ContainerExport.swift, import inContainerImport.swift, with shared client code inContainerClient.swiftand daemon routes inAPIServer+Start.swift.
Frequently Asked Questions
Can I export a running container?
No. The daemon returns an error if you attempt to export a container that is currently running. You must stop the container first using container stop <id> before calling container export. This restriction prevents race conditions and ensures the filesystem snapshot captures a consistent state.
What format does the export use?
The export creates a standard tar archive containing the container’s root filesystem. This format is compatible with other container tools and can be examined using standard Unix utilities like tar -tf archive.tar. The archive preserves the directory structure and permissions of the original container filesystem.
How do I import from stdin?
Use the hyphen character - as the file argument to read from stdin. This is useful for piped workflows: cat backup.tar | container import - or ssh host 'cat container.tar' | container import -. The command buffers the stream to a temporary file before sending it to the daemon via XPC.
Where does the export command store temporary files?
The export command uses FileManager.default.temporaryDirectory to create a unique subdirectory for each operation. This typically resolves to /tmp or the system-designated temporary directory. Files are automatically cleaned up via a defer block after the operation completes, even if the command fails or is interrupted.
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 →