# Understanding the Caching Mechanism Employed by Container for Builds: cache-in and cache-out Explained

> Discover how apple/container uses cache-in and cache-out for reproducible and faster builds. Learn to import and export build cache layers efficiently.

- Repository: [Apple/container](https://github.com/apple/container)
- Tags: deep-dive
- Published: 2026-06-17

---

**The `apple/container` build system implements a persistent build cache using `--cache-in` and `--cache-out` flags to import and export cache layers, enabling reproducible and accelerated builds across machines, CI pipelines, and developer workstations.**

The `apple/container` repository provides a sophisticated build caching mechanism built on top of BuildKit-style *cache-to* and *cache-from* semantics. This system allows developers to attach external cache sources at build initiation and persist generated cache layers to external locations upon completion. By leveraging explicit cache import and export operations, the tool minimizes redundant computation while maintaining build reproducibility across distributed environments.

## How Cache-In Imports External Cache Sources

The `--cache-in` flag enables the build daemon to consume pre-existing cache artifacts from local or remote locations. When you specify one or more cache sources, the system treats these as read-only inputs available to any build step that supports cache mounting.

### Implementation in Builder.swift

In [`Sources/ContainerBuild/Builder.swift`](https://github.com/apple/container/blob/main/Sources/ContainerBuild/Builder.swift), the build request construction process iterates over the `config.cacheIn` array and attaches each value as a metadata entry. The daemon then processes these entries as *cache sources* that can satisfy `RUN --mount=type=cache` directives or layer reuse optimizations.

```swift
// Inside Builder.swift – attaching cache-in metadata
for cacheIn in config.cacheIn {
    metadata.addString(cacheIn, forKey: "cache-in")
}

```

Cache sources can include local directories, previously exported tar archives, or remote URLs pointing to OCI registries. The build daemon validates accessibility of these sources before attempting to resolve cached layers.

## How Cache-Out Exports Build Cache

The `--cache-out` flag operates as the inverse of `--cache-in`, capturing the build cache generated during the current execution and writing it to specified destinations. This export functionality is essential for CI workflows where subsequent builds or parallel jobs need access to warmed caches.

### Persistence and Storage Formats

Each `--cache-out` argument is attached to the build request as a `cache-out` metadata entry in [`Builder.swift`](https://github.com/apple/container/blob/main/Builder.swift). After the build completes successfully, the daemon serializes the cache layers to the specified locations, which can be local tarballs or remote OCI-compliant registries.

```swift
// Inside Builder.swift – attaching cache-out metadata
for cacheOut in config.cacheOut {
    metadata.addString(cacheOut, forKey: "cache-out")
}

```

The exported cache preserves layer relationships and build metadata, ensuring that imported caches maintain the same fidelity when reused in future builds.

## Filesystem Cache Mode Configuration

Beyond import/export semantics, the caching mechanism controls filesystem-level caching behavior through the `CacheMode` enum defined in [`Sources/Container/Filesystem.swift`](https://github.com/apple/container/blob/main/Sources/Container/Filesystem.swift).

### CacheMode Enum and Default Behavior

Every mount—whether a block device or volume—can operate in either `.on` (cached) or `.off` (uncached) mode. The default value is `.on`, which enables the build-time cache layer and minimizes disk I/O while preserving correctness.

```swift
// Filesystem.swift – default cache mode for mounts
public enum CacheMode: String, Codable {
    case on   // "cached" – the default; uses the build-time cache layer
    case off  // "uncached" – disables the cache for this mount
}

```

When `CacheMode` is set to `.off`, the build system bypasses the caching layer for that specific mount, performing direct I/O operations instead. This is useful for scenarios where cache coherence must be strictly enforced or when working with volatile data that should not be cached.

## Practical Usage Examples

### Local Cache Archives

Import a previously exported cache archive to warm the build, then export the resulting cache for future use:

```bash

# Import a previously-exported cache archive

container build \
    --cache-in ~/my-cache.tar \
    -t my-app:latest .

# Export the cache generated by the current build

container build \
    --cache-out ~/new-cache.tar \
    -t my-app:latest .

```

### Remote OCI Registries

For CI pipelines, use a remote OCI registry as both the cache source and destination, enabling shared caches across build agents:

```bash

# Use a remote OCI cache as both input and output

container build \
    --cache-in oci://ghcr.io/example/my-cache:latest \
    --cache-out oci://ghcr.io/example/my-cache:latest \
    -t my-app:ci .

```

This approach ensures that the first build populates the registry cache, while subsequent builds—even on different machines—can immediately leverage the warmed layers without rebuilding intermediate steps.

## Summary

- **Cache-in (`--cache-in`)** imports external cache sources by attaching them as metadata entries in [`Builder.swift`](https://github.com/apple/container/blob/main/Builder.swift), making them available to build steps that read from the cache.
- **Cache-out (`--cache-out`)** exports generated cache layers to specified destinations after build completion, facilitating cache reuse across machines and CI runs.
- **Filesystem caching** uses the `CacheMode` enum in [`Filesystem.swift`](https://github.com/apple/container/blob/main/Filesystem.swift), defaulting to `.on` for optimal performance while allowing explicit uncached mounts when needed.
- **Supported formats** include local directories, tar archives, and remote OCI registries, providing flexibility for both local development and cloud-based workflows.

## Frequently Asked Questions

### What is the difference between cache-in and cache-out in container builds?

**Cache-in** specifies external cache sources to import at the start of a build, while **cache-out** defines destinations where the build system should export generated cache layers after completion. You can use both flags simultaneously to update a shared cache while benefiting from previous cached state.

### Where does the container build system store cache metadata?

The container build system stores cache metadata temporarily during the build process in [`Sources/ContainerBuild/Builder.swift`](https://github.com/apple/container/blob/main/Sources/ContainerBuild/Builder.swift), where it attaches cache-in and cache-out values to the gRPC build request. Persistent storage locations depend on your `--cache-out` arguments, which can specify local paths or remote OCI registries.

### Can I disable caching for specific mounts in a container build?

Yes, you can disable caching for specific mounts by setting the `CacheMode` to `.off` in [`Sources/Container/Filesystem.swift`](https://github.com/apple/container/blob/main/Sources/Container/Filesystem.swift). By default, mounts use `.on` mode, which enables the caching layer, but you can configure individual mounts to perform uncached I/O operations when cache coherence is critical.

### What cache formats does the apple/container build system support?

The build system supports local directories, tar archives (`.tar` files), and remote OCI registry URLs (using the `oci://` scheme). This flexibility allows you to share caches across developer workstations via network drives or centralize them in container registries for CI/CD pipelines.