How to Use SSH Agent Forwarding with Containers (--ssh flag)

Use the --ssh flag with container run or container exec to mount your host's SSH authentication socket at /run/host-services/ssh-auth.sock inside the container, allowing seamless access to host-loaded SSH keys without copying private credentials.

The apple/container CLI provides native SSH agent forwarding through the --ssh flag, enabling containers to leverage your host's existing SSH authentication. This feature captures the SSH_AUTH_SOCK environment variable at runtime and establishes a dynamic bind mount, eliminating the need to embed private keys in container images or manually configure SSH credentials.

How SSH Agent Forwarding Works in Container

When you append --ssh to a container command, the CLI reads the host's SSH_AUTH_SOCK path and configures the container runtime before the VM launches. This process involves three core components working together to ensure secure, transparent forwarding.

Configuration and Flag Parsing

The --ssh flag is parsed in Sources/Services/ContainerAPIService/Client/Flags.swift, which sets containerConfig.ssh = true in the configuration struct. This boolean is stored in Sources/ContainerResource/Container/ContainerConfiguration.swift and persists throughout the container lifecycle to signal that SSH forwarding is requested.

Runtime Mount Implementation

Before the container VM starts, Sources/Services/RuntimeLinux/Server/RuntimeService.swift implements the forwarding through the sshAuthSocketHostUrl method. The service validates that SSH_AUTH_SOCK exists on the host; if missing, it logs a warning ("ssh forwarding requested but no SSH_AUTH_SOCK found"). When present, the runtime creates a volume mount mapping the host socket to /var/host-services/ssh-auth.sock inside the VM while injecting SSH_AUTH_SOCK=/run/host-services/ssh-auth.sock into the container's environment.

Using the --ssh Flag in Practice

Basic Container Execution

To forward your SSH agent into an interactive container, append --ssh to your run command:

container run -it --rm --ssh alpine:latest sh

Inside the container, verify the forwarding by checking the environment variable and listing loaded keys:

env | grep SSH_AUTH_SOCK

# Output: SSH_AUTH_SOCK=/run/host-services/ssh-auth.sock

ssh-add -l

# Lists your host-loaded SSH keys

Cloning Private Repositories

SSH agent forwarding enables secure Git operations without embedding credentials in the container image:

container run -it --rm --ssh alpine:latest sh -c '
  apk add --no-cache openssh-client git &&
  ssh-add -l &&
  git clone git@github.com:my-org/private-repo.git
'

This approach uses the host's SSH agent to authenticate with GitHub or any private Git host, keeping private keys off the container filesystem.

Executing Commands in Running Containers

For long-running containers started with --ssh, subsequent exec commands inherit the forwarded socket:

container exec dev -- ssh -T git@github.com

As implemented in Sources/ContainerCommands/Container/ContainerRun.swift, the dynamic environment captured from ProcessInfo.processInfo.environment ensures that if your host's socket path changes between invocations, the next container launch automatically uses the updated source path while maintaining the static destination inside the container.

Technical Implementation Details

The forwarding mechanism relies on ContainerRun.swift to propagate the host's SSH_AUTH_SOCK value into the container's dynamic environment dictionary. This design ensures that the bind mount source updates automatically if you log out and obtain a new authentication socket, while the destination path inside the container remains constant at /run/host-services/ssh-auth.sock. According to the repository's documentation in docs/how-to.md, this implementation follows the principle of least privilege by never persisting private keys inside the container VM, only forwarding the authentication socket connection.

Summary

  • The --ssh flag enables dynamic SSH agent forwarding by mounting the host's SSH_AUTH_SOCK into the container at /run/host-services/ssh-auth.sock
  • Configuration flows from Flags.swift to ContainerConfiguration.swift, with runtime implementation in RuntimeService.swift
  • The forwarding works with container run, container start, and container exec commands
  • No private keys are copied into the container; only the authentication socket is forwarded
  • The implementation automatically adapts to changes in the host's socket path between container launches

Frequently Asked Questions

What happens if SSH_AUTH_SOCK is not set on the host when using --ssh?

If the host environment variable SSH_AUTH_SOCK is not present, RuntimeService.swift logs a warning message ("ssh forwarding requested but no SSH_AUTH_SOCK found") and continues launching the container without the SSH mount. The container will not have access to SSH authentication, but the launch will not fail.

Can I use SSH agent forwarding with container exec on a running container?

Yes. If the container was originally started with the --ssh flag, container exec commands inherit the same environment variables and mount points. This allows you to execute SSH commands in running containers without restarting them, as the dynamic environment persists through the container's lifecycle.

Is the SSH socket path inside the container always the same?

Yes. While the host-side socket path from SSH_AUTH_SOCK can vary between system restarts or user sessions, the container always sees the socket at /run/host-services/ssh-auth.sock. The RuntimeService dynamically updates the bind mount source each time while keeping the destination constant, ensuring consistent behavior across container invocations.

Does SSH agent forwarding work with private Git repositories?

Yes. Once forwarded, any SSH-aware tool inside the container—including git, ssh, and scp—can authenticate using the host's loaded keys. This is particularly useful for cloning private repositories or pushing changes without configuring deploy keys inside the container image, as documented in the docs/how-to.md examples.

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 →