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

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. 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:

container registry login registry.example.com

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

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

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

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:

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, 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 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
  • 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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →