# How to Authenticate with Private Container Registries Using the Apple Container CLI

> Authenticate with private container registries using Apple Container CLI. Learn the simple three-step login, list, and logout workflow. Your credentials stay secure in the macOS keychain.

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

---

**The Apple Container tool authenticates to private registries through a three-step CLI workflow: `login`, `list`, and `logout`, with all credentials securely stored in the macOS keychain via the `KeychainHelper` class.**

The `apple/container` repository provides a native macOS container runtime that integrates with private OCI registries. Understanding how to authenticate with private container registries ensures secure access to your images without exposing credentials in shell history or log files.

## The Three-Step Authentication Workflow

Authentication is handled through sub-commands in `Sources/ContainerCommands/Registry/`. The tool never writes raw passwords to disk; instead, it leverages the system keychain using the constant `Constants.keychainID` (`"com.apple.container.registry"`).

### Login (`container registry login`)

When you execute `container registry login`, the implementation in [`RegistryLogin.swift`](https://github.com/apple/container/blob/main/RegistryLogin.swift) performs the following operations:

1. **Parse arguments** – The command reads `--username`, `--password-stdin`, and the target registry hostname via `Flags.Registry` (defined in [`Flags.swift`](https://github.com/apple/container/blob/main/Flags.swift) at line 154). The `--scheme` flag defaults to `auto` for protocol detection.

2. **Prompt for credentials** – If `username` is omitted, `KeychainHelper.userPrompt(hostname:)` requests it interactively. If the password is missing, `KeychainHelper.passwordPrompt()` reads it from stdin (when `--password-stdin` is used) or interactively.

3. **Resolve connectivity** – `Reference.resolveDomain(domain:)` canonicalises the hostname, while `RequestScheme(registry.scheme)` determines whether to use HTTP, HTTPS, or auto-detect based on Docker-compatible internal DNS logic.

4. **Validate credentials** – A `RegistryClient` is instantiated with `BasicAuthentication(username:…, password:…)` and calls `client.ping()` to verify reachability and credential validity.

5. **Persist securely** – Upon success, `KeychainHelper.save(hostname:username:password:)` stores the encrypted credentials in the macOS keychain.

### List (`container registry list`)

The `list` sub-command (implemented in [`RegistryList.swift`](https://github.com/apple/container/blob/main/RegistryList.swift)) retrieves stored credentials without exposing passwords:

- `KeychainHelper.list()` returns an array of `RegistryInfo` objects
- Each entry is deserialized into a `RegistryResource` (defined in [`RegistryResource.swift`](https://github.com/apple/container/blob/main/RegistryResource.swift)), containing hostname, username, and timestamps
- Output renders as a table by default, or as a quiet list of hostnames with the `-q` flag

### Logout (`container registry logout`)

To remove credentials, [`RegistryLogout.swift`](https://github.com/apple/container/blob/main/RegistryLogout.swift) handles the `logout` command:

- The supplied registry name is canonicalised via `Reference.resolveDomain(domain:)`
- `KeychainHelper.delete(hostname:)` removes the entry from the keychain under the `"com.apple.container.registry"` domain

## Authenticating to Non-TLS Registries

For registries without TLS certificates, force the HTTP scheme using the `--scheme` flag:

```bash
container registry login \
  --scheme http \
  --username alice \
  my.private.registry:5000

```

As implemented in [`Flags.swift`](https://github.com/apple/container/blob/main/Flags.swift), the `--scheme` option accepts `http`, `https`, or `auto` (default). When set to `auto`, `RequestScheme` attempts HTTPS first, falling back to HTTP only for Docker-compatible internal DNS domains.

## Security Architecture and Credential Storage

The authentication flow spans three architectural layers:

**CLI Layer** – `RegistryLogin`, `RegistryLogout`, and `RegistryList` extensions parse arguments and delegate to the service layer.

**Service Layer** – `RegistryClient` (from the Container API client) communicates with remote registries using the resolved scheme and `BasicAuthentication` payload. It includes retry logic for transient failures.

**Persistence Layer** – `KeychainHelper` isolates container credentials using the domain constant defined in [`Constants.swift`](https://github.com/apple/container/blob/main/Constants.swift). Passwords are never logged or exposed in process listings; they travel directly from stdin/interactive prompts to the encrypted keychain blob.

## Practical Examples

Authenticate interactively to a standard registry:

```bash
container registry login registry.example.com

```

Authenticate non-interactively using stdin for the password:

```bash
container registry login \
  --username alice \
  --password-stdin \
  registry.example.com < password.txt

```

List all saved credentials:

```bash

# Detailed view

container registry list

# Machine-readable hostnames only

container registry list -q

```

Remove stored credentials:

```bash
container registry logout registry.example.com

```

## Summary

- **Store credentials** using `container registry login`, which persists authentication data to the macOS keychain via `KeychainHelper`.
- **Verify connectivity** automatically through `RegistryClient.ping()` before saving credentials.
- **Manage entries** with `container registry list` to view saved hosts and `container registry logout` to delete specific entries.
- **Force protocols** with `--scheme http` for registries without TLS, or rely on auto-detection for Docker-compatible domains.
- **Secure by default** – Credentials are encrypted under `"com.apple.container.registry"` and never written to logs or shell history.

## Frequently Asked Questions

### How does the Container tool store registry passwords?

The tool stores passwords in the macOS system keychain using the `KeychainHelper` class under the domain `"com.apple.container.registry"` (defined in [`Constants.swift`](https://github.com/apple/container/blob/main/Constants.swift)). Passwords are read from stdin or interactive prompts and immediately encrypted, never appearing in command history or log files.

### Can I use the Container CLI with HTTP-only registries?

Yes. Pass the `--scheme http` flag to `container registry login` to force HTTP instead of HTTPS. This is useful for local registries without TLS certificates. The `RequestScheme` logic in the API client handles the protocol selection when you use the default `auto` setting.

### Where does the authentication logic live in the source code?

The login implementation resides in [`Sources/ContainerCommands/Registry/RegistryLogin.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Registry/RegistryLogin.swift), logout in [`RegistryLogout.swift`](https://github.com/apple/container/blob/main/RegistryLogout.swift), and credential listing in [`RegistryList.swift`](https://github.com/apple/container/blob/main/RegistryList.swift). The underlying data model is defined in [`Sources/ContainerResource/Registry/RegistryResource.swift`](https://github.com/apple/container/blob/main/Sources/ContainerResource/Registry/RegistryResource.swift), while keychain constants are in [`Sources/Services/ContainerAPIService/Client/Constants.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/Constants.swift).

### Why does the login command require a ping to the registry?

The `client.ping()` call validates that the registry is reachable and that the credentials are accepted before storing them in the keychain. This prevents saving invalid authentication data that would later cause silent failures during `container run` operations.