How Image Caching Works in the Apple Container Tool: A Technical Deep Dive
The container command-line tool caches OCI images locally using a three-tier system comprising ImageStore for image manifests, ContentStore for addressable layer blobs, and BuildImageResolver for cache-to-registry resolution, enabling automatic reuse of downloaded layers across pulls and builds without redundant network traffic.
The container tool from the apple/container repository implements a sophisticated image caching mechanism designed to eliminate redundant network transfers and accelerate both image pulls and container builds. By storing OCI image manifests and layer blobs in local content-addressable stores, the tool can resolve dependencies from disk rather than fetching them repeatedly from remote registries. This architecture leverages three core components—ImageStore, ContentStore, and BuildImageResolver—to provide seamless caching across the pull, build, and run workflows.
Core Caching Components
The image caching architecture rests on three primary components that manage different aspects of the storage lifecycle:
-
ImageStore– The on-disk store of images and layers that holds pulled image manifests and associated layer blobs. Whencontainer image pullis invoked, the store is queried first; if required layers are present, they are used directly, skipping network download. This is initialized in [Sources/Plugins/CoreImages/ImagesHelper.swift](https://github.com/apple/container/blob/main/Sources/Plugins/CoreImages/ImagesHelper.swift#L98) at line 98. -
ContentStore– The generic content-addressable cache providing backing for any binary data, including image layers. The build pipeline uses this to resolve layers from cache when needed for new images. TheBuilderclass maintains references to this store in [Sources/ContainerBuild/Builder.swift](https://github.com/apple/container/blob/main/Sources/ContainerBuild/Builder.swift#L282-L284) at lines 282–284. -
BuildImageResolver– The resolver that decides whether to pull from registry or fetch from cache. When a build step needs a base image, the resolver checks thepullflag. Iffalse, it callsfetch()which first looks in theContentStore; only if the image is missing does it fall back toClientImage.pull. This logic is implemented in [Sources/ContainerBuild/BuildImageResolver.swift](https://github.com/apple/container/blob/main/Sources/ContainerBuild/BuildImageResolver.swift#L82-L85) at lines 82–85.
How Pull Caching Works
When you execute container image pull <reference>, the tool follows a resolution path that prioritizes local cache before initiating network requests:
-
ClientImage.pullis called (seeSources/ContainerAPIService/Client/ClientImage.swiftat line 247). -
Inside
ImagesService.pull, theImageStoreis consulted. If the requested image (identified by digest) already exists in theImageStore, the call returns immediately without network traffic. -
If the image is not present, the service downloads the manifest and each missing layer, stores them in the
ContentStore, and registers the image in theImageStorefor future reuse.
# Pull an image – will reuse cached layers if already present
container image pull alpine:latest
Build-Time Cache Management
The container build command supports explicit cache import and export through --cache-from and --cache-to flags. These populate the Builder.cacheIn and Builder.cacheOut arrays (see Sources/ContainerBuild/Builder.swift lines 282–284).
Cache-in workflow:
When using --cache-from=mycache.tar, the resolver extracts layer tarballs from the supplied archive and populates the ContentStore. Subsequent build steps can reuse those layers without rebuilding them.
Cache-out workflow:
After a successful build, --cache-to=mycache.tar writes any newly created layer tarballs into the archive, allowing reuse by later builds.
The resolver respects the pull boolean defined at line 284 in Builder.swift. When pull is false, only the local cache is consulted; when true, the resolver may still pull missing layers from a registry.
# Build with a cache-in archive (reuses previously built layers)
container build --cache-from=mycache.tar -t myapp .
# Build with a cache-out archive (exports newly built layers)
container build --cache-to=mycache.tar -t myapp .
Filesystem Caching for Running Containers
Beyond image layers, container supports host-side caching of mounted filesystems through the Filesystem.CacheMode enum defined in Sources/ContainerResource/Container/Filesystem.swift at lines 36–40. These modes are passed to the virtualization runtime (RuntimeService.swift lines 1357–1397) and affect I/O performance of block or volume mounts:
.on– Enable host caching (default)..off– Disable host caching..auto– Let the system decide based on workload characteristics.
Summary
- The image caching system in
apple/containerrelies on three coordinated components:ImageStorefor manifests,ContentStorefor layer blobs, andBuildImageResolverfor cache resolution logic. - During
container image pull, theImageStoreis checked first at line 247 ofClientImage.swift, eliminating network calls when images exist locally. - Build pipelines can import and export caches explicitly using
--cache-fromand--cache-to, with logic managed inBuilder.swiftandBuildImageResolver.swift. - The
pullflag in the builder determines whether cached layers are used exclusively or if network fallback is permitted. - Running containers benefit from configurable filesystem caching via
CacheModesettings inFilesystem.swift.
Frequently Asked Questions
Where does the container tool store cached image layers?
The tool stores cached image layers in the ContentStore, a generic content-addressable cache defined in Sources/ContainerBuild/Builder.swift at lines 282–284. This store holds binary data including image layer blobs, while the ImageStore maintains the associated manifests and metadata.
How does the build process decide between using cached layers versus pulling from a registry?
The BuildImageResolver makes this determination based on the pull boolean flag. When pull is false, the resolver calls fetch() which queries the ContentStore first, only falling back to ClientImage.pull if the layer is missing. This logic is implemented in Sources/ContainerBuild/BuildImageResolver.swift at lines 82–85.
Can I export and import build caches between different machines?
Yes. The container build command supports --cache-to and --cache-from flags that allow you to export newly built layers to a tarball and import them on another system. The Builder class manages these through the cacheIn and cacheOut arrays, populating the ContentStore from archive files during subsequent builds.
Does filesystem caching affect running containers?
Yes. The Filesystem.CacheMode enum in Sources/ContainerResource/Container/Filesystem.swift (lines 36–40) controls host-side caching for mounted volumes, with options for .on, .off, and .auto. These settings are passed to the virtualization runtime in RuntimeService.swift and directly impact I/O performance for block or volume mounts.
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 →