Use SSH credentials in Docker without baking in private keys
Choose scoped runtime mounts or agent forwarding, use BuildKit SSH mounts during builds, and verify server host keys.
Keep private SSH keys out of image layers. Use a narrowly scoped runtime credential mount or SSH agent, and use BuildKit's SSH mount for a build step that needs private repository access. Read-only access still lets a process read and potentially disclose a mounted private key.
Runtime: mount only the files needed
For a trusted Linux image containing an SSH client, running as root:
docker run --rm -it \
--mount "type=bind,source=$HOME/.ssh/deploy_key,target=/root/.ssh/id_ed25519,readonly" \
--mount "type=bind,source=$HOME/.ssh/known_hosts,target=/root/.ssh/known_hosts,readonly" \
my-image:1.2
Both source files must exist on the daemon host. With a remote daemon, the CLI machine's filesystem is not automatically available as a bind mount. Use a dedicated, least-privileged deploy key rather than exposing an entire personal .ssh directory.
The key must be readable by the container user and have suitably restrictive ownership and permissions. A non-root image needs the correct home path and UID/GID arrangement. A read-only mount cannot be repaired by running chmod inside the container; fix the host-side permissions or select another credential delivery method.
Keep host-key checking enabled. A known_hosts file should contain keys verified through a trusted source. ssh-keyscan can collect a key but does not authenticate it by itself.
Build time: forward an agent to one RUN step
Prepare known_hosts with the verified key for your Git server, and keep private keys out of the build context. A schematic private-repository build:
# syntax=docker/dockerfile:1
FROM python:3.12-slim-bookworm
RUN apt-get update \
&& apt-get install -y --no-install-recommends git openssh-client \
&& rm -rf /var/lib/apt/lists/*
RUN mkdir -p -m 0700 /root/.ssh
COPY known_hosts /root/.ssh/known_hosts
RUN --mount=type=ssh,required=true \
GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=yes' \
python -m pip install 'git+ssh://git@git.example.com/team/private-package.git'
Replace the repository with your own package and pin a reviewed commit for reproducibility. With the required key already loaded into your agent:
ssh-add -l
docker build --ssh default -t my-image:dev .
If no agent is running, start one using the method appropriate to your shell and load the intended key. BuildKit exposes the SSH agent socket only to the mounted build step rather than adding the private-key file to the image. That step can still use the agent's authority and can leak data through malicious commands or output; trust the Dockerfile and dependency code.
Runtime agent forwarding
For a local Linux daemon with access to the agent socket:
: "${SSH_AUTH_SOCK:?Start an SSH agent first}"
docker run --rm -it \
--mount "type=bind,source=${SSH_AUTH_SOCK},target=/ssh-agent" \
-e SSH_AUTH_SOCK=/ssh-agent \
my-image:1.2
Match socket permissions to the container user. Docker Desktop and remote daemons require their supported forwarding arrangement; mounting an arbitrary macOS or Windows client path is not universally sufficient. Agent forwarding avoids directly copying the key but allows the container to request signatures while it has access.
FAQ
Why not COPY a key and delete it later? Earlier layers can retain it. ENV SSH_KEY=... also exposes it in image metadata. Deletion in a later step is not secure erasure.
Why does SSH report open permissions? Check ownership and mode for the actual mounted filesystem. Windows permission translation can differ; use an agent or a supported secret mount instead of trying to chmod a read-only file.
Does Git need user.name and user.email to authenticate? No. Those identify commit authors. SSH authentication needs an authorized public key and repository permission. A command such as ssh -T git@github.com can report successful authentication yet exit nonzero because shell access is unavailable; read its message.
What about Kubernetes? Mount a narrowly scoped Secret with permissions the workload user can read, and configure verified host keys. Secret access and storage protection still require cluster configuration. Build-time SSH support is builder-specific; do not assume Kaniko implements BuildKit's mount syntax.
See BuildKit secret and SSH mounts, OpenSSH host-key collection, OpenSSH client configuration, Docker bind mounts.
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan