How to Handle Registry Authentication and Credentials in the Apple Container CLI

The Apple Container CLI stores registry credentials securely in macOS Keychain via the KeychainHelper class, enabling automatic authentication for push and pull operations after an initial container registry login.

The apple/container project provides a native macOS container management solution that handles registry authentication and credentials through system Keychain integration rather than insecure file-based storage. This approach ensures that sensitive authentication data remains encrypted and accessible only to the user, while supporting both interactive and automated workflows.

Authentication Flow

The authentication system follows a strict flow: credentials are verified against the registry, stored in Keychain, and then retrieved automatically for subsequent operations.

Logging In (container registry login)

The login implementation resides in Sources/ContainerCommands/Registry/RegistryLogin.swift. The command accepts --username and --password-stdin flags, or prompts interactively when omitted.

The process executes as follows:

  1. Host Resolution: The code calls Reference.resolveDomain to determine the target registry host and automatically detects the scheme (http, https, or auto) based on system DNS configuration.
  2. Credential Verification: A RegistryClient is instantiated with a BasicAuthentication payload containing the provided credentials. The client performs a ping request to verify validity before storage.
  3. Secure Storage: Upon successful verification, the system calls KeychainHelper.save(hostname:username:password:) to persist credentials under the security domain defined by Constants.keychainID.

Logging Out (container registry logout)

To remove credentials, the logout command—implemented in Sources/ContainerCommands/Registry/RegistryLogout.swift—resolves the registry host and invokes KeychainHelper.delete(hostname:). This operation requires no network request; it simply removes the entry from the macOS Keychain.

Automatic Credential Retrieval

When executing registry-related operations like container image push or pull, the system instantiates KeychainHelper (observed in Sources/Services/MachineAPIService/Client/MachineClient.swift at line 336 and Sources/Services/ContainerImagesService/Server/ImagesService.swift at line 426) with Constants.keychainID.

The helper calls userPrompt(hostname:) or passwordPrompt() only when no stored credentials exist. If valid credentials are found, they are returned transparently, bypassing manual input.

Configuration and Defaults

The default registry domain is defined in the system configuration under the [registry] table:

[registry]
domain = "docker.io"

Users can override this default by creating or editing ~/.config/container/config.toml. This configuration affects how image references are resolved when no explicit host is specified (e.g., myapp:latest resolves to my-registry.example.com/library/myapp:latest).

Security Guarantees

The authentication architecture provides three critical security layers:

  • Keychain Isolation: Credentials are stored under a unique security domain (Constants.keychainID), preventing cross-application access or exposure to other processes.
  • Memory Safety: Passwords remain in memory only until verified and saved; the code never writes secrets to logs, environment variables, or temporary files.
  • Phishing Mitigation: When executing container registry login without the --username flag, the system prompts the user to confirm the hostname via the Keychain dialog, preventing credential theft through spoofed registry endpoints.

Practical Examples

Interactive Login


# Prompts for username and password via secure dialog

container registry login my-registry.example.com

Non-Interactive Login (CI/CD)

echo "$MY_PASSWORD" | container registry login \
    --username myuser --password-stdin my-registry.example.com

Automatic Push Using Stored Credentials


# Uses credentials previously saved in Keychain

container image push my-registry.example.com/myuser/myapp:latest

Logout and Credential Removal

container registry logout my-registry.example.com

Override Default Registry

Create ~/.config/container/config.toml:

[registry]
domain = "my-registry.example.com"

Key Implementation Files

Summary

  • Keychain Storage: All credentials are stored in macOS Keychain under Constants.keychainID, not in plaintext files.
  • Login Flow: The RegistryLogin.swift implementation verifies credentials via ping before saving them using KeychainHelper.save().
  • Automatic Retrieval: ImagesService.swift and MachineClient.swift automatically retrieve stored credentials, bypassing prompts when available.
  • Configuration: Default registries are configured via ~/.config/container/config.toml under the [registry] table.
  • Security: The system prevents credential logging and requires explicit user consent for unknown hosts.

Frequently Asked Questions

Where exactly are my registry credentials stored?

Credentials are stored in the macOS Keychain under a unique security domain defined by Constants.keychainID. According to the source code in RegistryLogin.swift, the KeychainHelper.save(hostname:username:password:) method writes them to the user's default keychain, making them accessible only to the Container CLI process and requiring macOS user authentication for access.

How do I handle registry authentication in CI/CD pipelines without interactive prompts?

Use the --password-stdin flag combined with --username for non-interactive authentication. As implemented in RegistryLogin.swift, the command accepts piped input: echo "$PASSWORD" | container registry login --username $USER --password-stdin $HOST. This bypasses the interactive userPrompt() and passwordPrompt() calls while still securely storing credentials in Keychain for subsequent push operations.

Can I use multiple private registries simultaneously?

Yes. The KeychainHelper stores credentials keyed by hostname (via Reference.resolveDomain), allowing distinct entries for docker.io, ghcr.io, and private registries. When executing operations like container image push, the system extracts the domain from the image reference and retrieves the matching credentials automatically, as seen in the ImagesService.swift implementation.

What happens if I deny Keychain access when prompted?

If you deny Keychain access, the KeychainHelper cannot retrieve or save credentials, causing the authentication flow to fail. The system will either prompt again (for interactive sessions) or fail with an authentication error (for non-interactive sessions). You must grant Keychain access for the Constants.keychainID security domain to enable credential persistence.

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 →