# How to Push a Container Image to a Registry with the Apple Container CLI

> Easily push container images to a registry using the Apple Container CLI. Learn to upload OCI-compatible images with optional platform filtering to streamline your workflow.

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

---

**Use `container image push <reference>` to upload OCI-compatible images to a remote registry, with optional platform filtering via `--platform`, `--arch`, or `--os` flags.**

The `apple/container` repository provides an OCI-compatible CLI tool for building, running, and managing container images. When you need to push a container image to a registry, the `container image push` command handles the entire workflow from local image store to registry upload. This command operates entirely locally, contacting the registry directly while authenticating via your Docker config or environment variables.

## Command Syntax and Basic Usage

The push command follows a straightforward syntax that accepts an image reference as the primary argument. You can optionally specify platform constraints to push specific variants of multi-architecture images.

The basic structure is:

```bash
container image push [OPTIONS] NAME[:TAG]

```

Key options include:

- `--platform` – Filter by platform (e.g., `linux/arm64`)
- `--arch` – Specify architecture (e.g., `arm64`, `amd64`)
- `--os` – Specify operating system (e.g., `linux`)

## How the Push Workflow Works

The `container image push` command traverses several architectural layers before bytes reach the registry. Understanding this flow helps debug authentication issues and optimize upload performance.

### Step 1: Command Parsing in ImagePush.swift

The entry point resides in [`Sources/ContainerCommands/Image/ImagePush.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Image/ImagePush.swift). The `ImagePush` struct defines the sub-command, parses user-supplied options, and captures the image reference. This module validates flags like `--arch`, `--os`, and `--platform` before invoking the underlying services.

### Step 2: Registry Scheme Detection

Before establishing a connection, the `Flags.Registry` component determines whether to use HTTP or HTTPS by constructing a `RequestScheme` object. This logic resides around line 63 of [`ImagePush.swift`](https://github.com/apple/container/blob/main/ImagePush.swift), ensuring secure-by-default connections while allowing insecure-registry overrides for local development.

### Step 3: Image Lookup via ClientImage

The `ClientImage.get` method in [`Sources/Services/ContainerAPIService/Client/ClientImage.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/ClientImage.swift) retrieves the locally-stored image definition and metadata from the image store (line 384). This step validates that the image exists locally before attempting to push and retrieves the manifest and layer descriptors necessary for the upload.

### Step 4: Platform Resolution

The `DefaultPlatform.resolve` method consolidates any `--arch`, `--os`, or `--platform` flags into a concrete `Platform` value (line 61 of [`ImagePush.swift`](https://github.com/apple/container/blob/main/ImagePush.swift)). This platform filter constrains which image variant(s) will be pushed, ensuring you only upload the specific architecture required rather than the entire multi-arch manifest.

### Step 5: Progress Reporting

A `ProgressBar` from the `TerminalProgress` package is instantiated and wired to the push operation (lines 66-74 of [`ImagePush.swift`](https://github.com/apple/container/blob/main/ImagePush.swift)). This provides real-time, blob-by-blob progress display as the CLI streams image layers to the remote registry.

### Step 6: Streaming to the Registry

The actual upload occurs through `image.push`, which forwards the request to the `ImagesService` implementation. According to [`Sources/Services/ContainerImagesService/Server/ImagesService.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerImagesService/Server/ImagesService.swift) (line 117), this service calls `imageStore.push` to stream image layers to the registry using the OCI distribution specification. The architecture maintains separation between the CLI layer, network layer, and storage layer, making each component testable in isolation.

### Step 7: Result Output

After successful completion, the command prints the canonical reference of the pushed image (line 81 of [`ImagePush.swift`](https://github.com/apple/container/blob/main/ImagePush.swift)). This output can be piped into scripts or CI/CD logs for verification and audit purposes.

## Practical Push Examples

Push a single-platform image to a remote registry:

```bash
container image push registry.example.com/fido/web-test:latest

```

Push only the arm64 variant of a multi-arch image:

```bash
container image push --platform arm64 \
    registry.example.com/fido/web-test:latest

```

Push with explicit architecture and OS flags:

```bash
container image push --arch arm64 --os linux \
    registry.example.com/fido/web-test:latest

```

The progress bar displays by default, showing each blob being uploaded:

```bash
container image push registry.example.com/fido/web-test:latest

# → Progress bar shows blob upload status

```

## Summary

- **The `container image push` command** transfers OCI-compatible images from local storage to remote registries.
- **Platform filtering** works via `--platform`, `--arch`, and `--os` flags, implemented through `DefaultPlatform.resolve` in [`ImagePush.swift`](https://github.com/apple/container/blob/main/ImagePush.swift).
- **Authentication** leverages Docker config files or environment variables, with registry schemes auto-detected via `Flags.Registry`.
- **Progress tracking** uses the `TerminalProgress` package to display real-time blob upload status.
- **Key source files** include [`Sources/ContainerCommands/Image/ImagePush.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Image/ImagePush.swift) for CLI logic and [`Sources/Services/ContainerImagesService/Server/ImagesService.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerImagesService/Server/ImagesService.swift) for the actual streaming implementation.

## Frequently Asked Questions

### How do I push a specific architecture to a registry?

Use the `--platform` flag to specify the target architecture, or combine `--arch` and `--os` for explicit control. The `DefaultPlatform.resolve` method in [`ImagePush.swift`](https://github.com/apple/container/blob/main/ImagePush.swift) processes these flags to filter the image manifest before pushing only the matching variant.

### Where does the Apple Container CLI store registry credentials?

The tool authenticates using credentials stored in your Docker config file or via environment variables. The `Flags.Registry` component handles the connection scheme (HTTP/HTTPS) but relies on standard Docker credential stores for authentication headers.

### Can I disable the progress bar when pushing images?

The source code shows that `ProgressBar` from `TerminalProgress` is wired into the push workflow by default (lines 66-74 of [`ImagePush.swift`](https://github.com/apple/container/blob/main/ImagePush.swift)). While the command reference may offer a `--quiet` flag, the architecture supports progress reporting as the default behavior for all push operations.

### What happens if the image doesn't exist locally?

The `ClientImage.get` method in [`Sources/Services/ContainerAPIService/Client/ClientImage.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/ClientImage.swift) retrieves the image from the local store before initiating the push. If the image reference cannot be found locally, the command fails at this lookup stage before attempting any network connection to the registry.