How to Pull and Push Container Images with OCI-Compatible Registries in apple/container
The apple/container project implements OCI Distribution Specification-compliant pull and push operations through first-class CLI commands that authenticate via the Apple keychain and support platform-specific image selection.
The apple/container repository provides a Swift-based container runtime that communicates with OCI-compatible registries using standard distribution protocols. Understanding how to pull and push container images with OCI-compatible registries in apple/container requires familiarity with its authentication mechanisms, reference normalization, and platform resolution strategies.
Configuring Registry Authentication and Defaults
Before pulling or pushing images, you must configure the target registry and authentication credentials. The system uses a combination of global configuration files and secure keychain storage.
Global Configuration Storage
The default registry domain is defined in ~/.config/container/config.toml under the [registry] table. As implemented in Sources/ContainerPersistence/ContainerSystemConfig.swift, the ContainerSystemConfig.registry.domain property defaults to docker.io when no host is supplied in an image reference.
// Configuration is loaded at runtime
let config = try await Application.loadContainerSystemConfig()
Authenticating with container registry login
Use the container registry login <host> command to store credentials in the Apple keychain. According to Sources/Services/ContainerAPIService/Client/Constants.swift, credentials are stored under the service ID com.apple.container.registry. The RegistryResource model in Sources/ContainerResource/Registry/RegistryResource.swift validates hostnames according to OCI specification requirements and associates the stored username with the registry entry.
container registry login my-registry.example.com
Pulling Images from OCI-Compatible Registries
The container image pull command implementation resides in Sources/ContainerCommands/Image/ImagePull.swift. The ImagePull.run() method orchestrates the retrieval process through the following sequence:
- Parses the image reference and resolves the platform using
DefaultPlatform.resolve - Creates a
RequestSchemefrom the optional--schemeflag (http, https, or auto) - Normalizes the reference via
ClientImage.normalizeReference - Executes
ClientImage.pull()to stream blob downloads to the local content store - Unpacks the image layers using
Image.unpack
let config = try await Application.loadContainerSystemConfig()
let platform = try DefaultPlatform.resolve(platform: platform, os: os, arch: arch, log: log)
let scheme = try RequestScheme(registry.scheme)
let normalized = try ClientImage.normalizeReference(reference,
containerSystemConfig: config)
let image = try await ClientImage.pull(
reference: normalized,
platform: platform,
scheme: scheme,
containerSystemConfig: config,
progressUpdate: progress.handler,
maxConcurrentDownloads: imageFetchFlags.maxConcurrentDownloads)
Pull the official Alpine image from Docker Hub using default settings:
container image pull alpine:3.18
Pull with explicit platform and TLS scheme:
container image pull \
--platform linux/arm64 \
--scheme https \
my-registry.example.com/username/my-app:latest
Pushing Images to OCI-Compatible Registries
The push workflow implemented in Sources/ContainerCommands/Image/ImagePush.swift follows a similar pattern but reverses the data flow. The ImagePush.run() method performs these operations:
- Loads the system configuration and resolves the target platform
- Retrieves the local image using
ClientImage.get() - Invokes
Image.push()to stream the manifest and blobs to the registry - Outputs the final reference upon completion
let config = try await Application.loadContainerSystemConfig()
let platform = try DefaultPlatform.resolve(platform: platform, os: os, arch: arch, log: log)
let scheme = try RequestScheme(registry.scheme)
let image = try await ClientImage.get(reference: reference,
containerSystemConfig: config)
_ = try await image.push(
platform: platform,
scheme: scheme,
containerSystemConfig: config,
progressUpdate: progress.handler)
print(image.reference) // echo the full reference on success
Tag and push a local image to a private registry:
container image tag my-local-image \
my-registry.example.com/username/my-app:latest
container image push my-registry.example.com/username/my-app:latest
Platform Selection and Transport Schemes
RequestScheme and TLS Configuration
The --scheme <scheme> flag accepts auto, http, or https values. As defined in Sources/Services/ContainerAPIService/Client/RequestScheme.swift, the RequestScheme.init(_:) constructor automatically upgrades to HTTPS unless the host matches known internal registry patterns. When set to auto, the client evaluates the hostname against internal registry definitions before establishing the connection.
Platform Resolution with DefaultPlatform
The --platform os/arch[/variant] flag overrides separate --os and --arch parameters. The DefaultPlatform.resolve method parses the platform string and produces a Platform value that filters manifest lists during pull and push operations. This ensures you retrieve or publish the correct architecture-specific image variant.
Both commands instantiate a ProgressBar via TerminalProgress and coordinate updates through ProgressTaskCoordinator, providing real-time feedback during blob transfers.
Summary
- Authentication: Credentials are stored in the Apple keychain under
com.apple.container.registryand retrieved automatically based on the hostname defined inRegistryResource. - Configuration: Default registries are configured in
~/.config/container/config.tomlviaContainerSystemConfig, defaulting to docker.io when unspecified. - Pull workflow:
ImagePull.swiftdelegates toClientImage.pull()after normalizing references and resolving platforms viaDefaultPlatform.resolve. - Push workflow:
ImagePush.swiftretrieves local images withClientImage.get()and streams them viaImage.push()to OCI registries. - Transport security:
RequestSchemehandles automatic HTTPS upgrades and explicit scheme selection through the--schemeflag.
Frequently Asked Questions
Where are registry credentials stored in apple/container?
Registry credentials are stored in the Apple keychain under the service identifier com.apple.container.registry, as defined in Sources/Services/ContainerAPIService/Client/Constants.swift. The container registry login command persists the username and password securely, and subsequent pull or push operations automatically retrieve these credentials when connecting to matching hostnames.
How does the auto scheme selection work for registry connections?
When --scheme auto is specified (the default), the RequestScheme initializer in Sources/Services/ContainerAPIService/Client/RequestScheme.swift evaluates whether the target host matches any known internal registry domains. If the host is external, the scheme automatically upgrades to HTTPS; otherwise, it may use HTTP for internal registries. You can override this behavior by explicitly setting --scheme http or --scheme https.
Can I pull images for specific CPU architectures using apple/container?
Yes. Use the --platform flag followed by an OS/architecture combination such as linux/arm64 or darwin/amd64. The DefaultPlatform.resolve method parses this string and filters the manifest list during the pull operation in ClientImage.pull(), ensuring you retrieve only the image variant matching your target platform.
What happens if I omit the registry hostname in an image reference?
If no registry host is specified, ContainerSystemConfig loads the default domain from the [registry] table in ~/.config/container/config.toml. As implemented in Sources/ContainerPersistence/ContainerSystemConfig.swift, this value defaults to docker.io, allowing commands like container image pull alpine:latest to resolve automatically against Docker Hub.
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 →