# How to Authenticate with Container Registries Using `container registry login`

> Authenticate with container registries using container registry login. Learn how this command verifies and securely stores your credentials in the macOS keychain.

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

---

**The `container registry login` command authenticates against OCI-compatible registries by collecting credentials, verifying them via a ping, and securely storing them in the macOS keychain.**

The Apple/container repository provides a Swift-based command-line tool for managing OCI containers on macOS. The `container registry login` command implements a secure authentication flow that stores credentials in the system keychain rather than plain text files, ensuring your registry passwords remain protected.

## Understanding the Authentication Flow

The authentication logic resides in [`Sources/ContainerCommands/Registry/RegistryLogin.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Registry/RegistryLogin.swift). The command follows a five-step process to establish and persist registry credentials safely.

### Step 1: Load System Configuration

The command first reads the global `ContainerSystemConfig` to obtain DNS and networking defaults. This configuration determines how the client resolves the registry host and handles internal DNS domains before establishing a connection.

### Step 2: Collect Credentials

The tool supports multiple credential input methods depending on your environment:

- **Interactive prompts**: If `--username` is omitted, the command calls `KeychainHelper.userPrompt` to request the username. If `--password-stdin` is not set, `KeychainHelper.passwordPrompt` securely prompts for the password via the macOS keychain UI.
- **Command-line arguments**: Supply `--username` to provide the username directly while still prompting securely for the password.
- **Standard input**: Use `--password-stdin` to pipe the password via stdin (requires `--username` to be specified), enabling automation in CI/CD pipelines.

The credentials are wrapped in a `BasicAuthentication` object for transport to the registry.

### Step 3: Create a Registry Client

The command instantiates `RegistryClient` with the host, scheme (derived from request scheme logic), optional port, and the `BasicAuthentication` object. The client configures a retry policy with 10 attempts and a 300ms interval to handle transient server errors gracefully.

### Step 4: Ping the Registry

The client calls `client.ping()` to verify credentials against the registry. This validation step ensures that only working credentials are saved. If the ping fails, the command exits with an error and does not persist the invalid credentials.

### Step 5: Persist the Credentials

Upon successful verification, the command stores credentials using `KeychainHelper.save` under the `Constants.keychainID` namespace. Subsequent commands like `container image pull` retrieve these credentials via `KeychainHelper.lookup` without requiring user interaction.

## Command-Line Usage Examples

Here are practical ways to use `container registry login` in different scenarios:

Basic interactive login that prompts for both username and password:

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

```

Supply the username on the command line while securely prompting for the password:

```bash
container registry login registry.example.com --username alice

```

Script-friendly authentication using stdin for the password (avoids interactive prompts):

```bash
echo "s3cr3tP@ss" | container registry login registry.example.com \
    --username alice --password-stdin

```

## Programmatic Authentication in Swift

You can implement the same authentication flow in Swift using the ContainerAPIClient and ContainerPersistence libraries. This example mirrors the logic found in [`RegistryLogin.swift`](https://github.com/apple/container/blob/main/RegistryLogin.swift):

```swift
import ContainerAPIClient
import ContainerPersistence

// Resolve the host and scheme
let server = "registry.example.com"
let scheme = try RequestScheme("https").schemeFor(host: server,
                       internalDnsDomain: systemConfig.dns.domain)

// Build the authentication object
let auth = BasicAuthentication(username: "alice", password: "s3cr3tP@ss")

// Create the client with retry logic matching the CLI defaults
let client = RegistryClient(
    host: server,
    scheme: scheme.rawValue,
    port: nil,
    authentication: auth,
    retryOptions: .init(maxRetries: 10,
                        retryInterval: 300_000_000,
                        shouldRetry: { $0.status.code >= 500 })
)

// Verify credentials before considering login successful
try await client.ping()
print("Login succeeded")

```

## Security Architecture

The tool prioritizes credential security through platform-native storage mechanisms. According to the source code in [`Sources/ContainerCommands/Registry/RegistryLogin.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Registry/RegistryLogin.swift), authentication data is **never written to disk** in plain text. Instead, the implementation relies on `KeychainHelper` from the ContainerPersistence package to:

- Securely prompt for credentials using native macOS keychain APIs
- Store credentials under the `Constants.keychainID` namespace, isolating them from other applications
- Retrieve saved credentials for subsequent registry operations without exposing them to process lists or shell history

Other components like `MachineClient.fetchMachineArtifact` in [`Sources/Services/MachineAPIService/Client/MachineClient.swift`](https://github.com/apple/container/blob/main/Sources/Services/MachineAPIService/Client/MachineClient.swift) demonstrate this pattern by calling `keychain.lookup` to obtain stored credentials before passing them to `RegistryClient` for image-related operations.

## Summary

- The `container registry login` command authenticates against OCI registries using a five-step flow implemented in [`RegistryLogin.swift`](https://github.com/apple/container/blob/main/RegistryLogin.swift)
- Credentials are collected via interactive prompts, command-line flags, or stdin, then wrapped in a `BasicAuthentication` object
- The `RegistryClient` verifies credentials through a `ping()` operation with automatic retry logic (10 attempts, 300ms intervals)
- All credentials are stored securely in the macOS keychain using `KeychainHelper.save` under the `Constants.keychainID` namespace
- Subsequent commands retrieve credentials via `KeychainHelper.lookup` without requiring re-authentication

## Frequently Asked Questions

### Where does `container registry login` store authentication credentials?

The command stores credentials exclusively in the macOS keychain using `KeychainHelper.save` under the `Constants.keychainID` namespace. They are never written to plain text configuration files or environment variables, protecting them from unauthorized access.

### Can I use `container registry login` in automated scripts without interactive prompts?

Yes. For automation, use the `--username` flag combined with `--password-stdin` to pipe the password via standard input. This avoids interactive keychain prompts while maintaining security by not exposing credentials in shell history or process lists.

### What authentication method does the container tool use when communicating with registries?

The tool uses HTTP Basic Authentication via the `BasicAuthentication` class. The credentials are passed to `RegistryClient` which includes them in requests to the OCI registry after verifying them with an initial ping request to the server.

### How does the tool verify that my credentials are valid before storing them?

The command calls `client.ping()` immediately after creating the `RegistryClient`. This sends a validation request to the registry to verify credential acceptance. Only after receiving a successful response does the command persist credentials to the keychain, preventing invalid logins from being saved.