# How to Pull and Push Container Images with OCI-Compatible Registries in apple/container

> Learn to pull and push container images using OCI-compatible registries with apple/container. Authenticate easily with the Apple keychain and select platform-specific images.

- Repository: [Apple/container](https://github.com/apple/container)
- Tags: how-to-guide
- Published: 2026-06-24

---

**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`](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/ContainerSystemConfig.swift), the `ContainerSystemConfig.registry.domain` property defaults to **docker.io** when no host is supplied in an image reference.

```swift
// 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`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/Sources/ContainerResource/Registry/RegistryResource.swift) validates hostnames according to OCI specification requirements and associates the stored username with the registry entry.

```bash
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`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Image/ImagePull.swift). The `ImagePull.run()` method orchestrates the retrieval process through the following sequence:

1. Parses the image reference and resolves the platform using `DefaultPlatform.resolve`
2. Creates a `RequestScheme` from the optional `--scheme` flag (http, https, or auto)
3. Normalizes the reference via `ClientImage.normalizeReference`
4. Executes `ClientImage.pull()` to stream blob downloads to the local content store
5. Unpacks the image layers using `Image.unpack`

```swift
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:

```bash
container image pull alpine:3.18

```

Pull with explicit platform and TLS scheme:

```bash
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`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Image/ImagePush.swift) follows a similar pattern but reverses the data flow. The `ImagePush.run()` method performs these operations:

1. Loads the system configuration and resolves the target platform
2. Retrieves the local image using `ClientImage.get()`
3. Invokes `Image.push()` to stream the manifest and blobs to the registry
4. Outputs the final reference upon completion

```swift
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:

```bash
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`](https://github.com/apple/container/blob/main/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.registry` and retrieved automatically based on the hostname defined in `RegistryResource`.
- **Configuration**: Default registries are configured in `~/.config/container/config.toml` via `ContainerSystemConfig`, defaulting to docker.io when unspecified.
- **Pull workflow**: [`ImagePull.swift`](https://github.com/apple/container/blob/main/ImagePull.swift) delegates to `ClientImage.pull()` after normalizing references and resolving platforms via `DefaultPlatform.resolve`.
- **Push workflow**: [`ImagePush.swift`](https://github.com/apple/container/blob/main/ImagePush.swift) retrieves local images with `ClientImage.get()` and streams them via `Image.push()` to OCI registries.
- **Transport security**: `RequestScheme` handles automatic HTTPS upgrades and explicit scheme selection through the `--scheme` flag.

## 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`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/Sources/ContainerPersistence/ContainerSystemConfig.swift), this value defaults to **docker.io**, allowing commands like `container image pull alpine:latest` to resolve automatically against Docker Hub.