Docker secrets - Using your SSH key during the build process
Securely access private Git repositories during your Docker build using SSH keys and Docker secrets. Includes complete Dockerfile and compose.yaml examples to prevent secrets from being stored in your final image.
This article is over a year old but was reviewed on July 30, 2026 — the content is still accurate.
This article is part of the SSH - From your first key to remote development series:
- GitHub - Connect your account using SSH and start to work with git@ protocol
- Docker secrets - Using your SSH key during the build process
- Using sftp on the command line, with or without a proxy
- SSH - Autosuggestions with ZSH
- SSH - Launch a terminal on your session without having to authenticate yourself
- How to connect to your hosting server using SSH
- SSH Remote development with VSCode
- Open a DevContainer on a Remote Server with VSCode Remote SSH and ProxyJump
- SSH Config Generator: Wildcard Stanzas, Aliases, and Automated Logins

This article shows how to git clone a private repository during a Docker build without ever baking the SSH key into the final image, using Docker's RUN --mount=type=secret BuildKit feature declared in compose.yaml and referenced in the Dockerfile. It also covers the separate case of sharing your SSH key with the running container (not the image) via a bind-mounted volume, for ongoing git pull/push work inside it.
There are plenty of articles on the Internet but I didn't find the one that allowed me, without an impressive amount of trial and error, to find the solution.
So here's another article to add to the long list: how to access a private project stored on Github when creating a Docker image. In other words, the SSH key is not stored in the image. Docker will just use your key when executing the project recovery layer (the one containing the git clone instruction) and will not keep track of the key afterwards.
My use case: I wish to build a Docker image and during the build phase, I need to grab a copy of a private repository I've put on github.com. The CI equivalent of this problem — pulling a private Docker image rather than a private repository — is covered in GitLab - Using Docker private images.
When I access the container, the project will then be available but I don't have, anywhere in my image, a copy of my SSH key so I won't be able to run a git pull f.i. since no further authentication is possible.
Preview

That's the proof the technique works: the SSH key was used to authenticate against GitHub during the build — without ever being baked into the final image.
Why it works
- Docker's
RUN --mount=type=secretmounts the secret only for the duration of that one build layer — it never gets written to a layer that ends up in the final image. - Copying your SSH key into the image instead (or hardcoding a token) is a serious design error: anyone reading the Dockerfile, or starting an interactive bash session and running
printenvor browsing.ssh/.gitfolders, will find it. - Even a secret deleted in a later layer can survive: tools like SecretScanner do deep layer-by-layer scanning and will still find it if it was ever written to disk in an earlier layer. The same reasoning applies to your git history: Git - pre-commit-hooks lists hooks that block a commit as soon as a credential is detected.
Which key to use
Caution
This part is one of the most important ones. First, of course, you should already have created an SSH key (see my Github - Connect your account using SSH and start to work with git@ protocol if needed).
Then you should know which protocol you've used and that's really important. Is your key stored in a file called id_ed25519 or id_rsa or something else? Only you know.
Just run ls -alh ~/.ssh to get the list of your keys:

I'm using two different keys as you can see: id_ed25519 or id_rsa. Files having the .pub extensions are the public keys; those without extensions are the private keys.
It's important to know which key has been used to link your Github profile.
You can just display the file to your console like f.i. cat ~/.ssh/id_ed25519.pub or cat ~/.ssh/id_rsa.pub (the public key file) and look at the end: is the mentioned email is the one you're using on Github.com? If yes, you've probably identified the key.
In my case, the key I've used for github.com is id_ed25519 so I'll use that one in the next chapter.
Creating files
We'll need two files: compose.yaml and Dockerfile.
compose.yaml
Here is my compose.yaml content:
services: app: build: context: . dockerfile: Dockerfile // highlight-next-line secrets: // highlight-next-line - my_ssh_key args: // highlight-next-line - KEY_NAME=id_ed25519
// highlight-next-linesecrets: // highlight-next-line my_ssh_key: // highlight-next-line file: ${HOME}/.ssh/id_ed25519This Docker Compose configuration sets up a service that builds a Docker image while securely passing an SSH key into the build process. The `secrets` section in the build defines what sensitive data the container can access during building, and the top-level `secrets` section tells Docker where to find that SSH key file on the host machine.
As you can see, we've five specific lines.
In our services -> app --> build entry, we should add the secrets and the name of one (or more) secrets.
I've also added an argument called KEY_NAME just because I need to inform Docker which key I'm using (is it id_ed25519 or id_rsa or another one).
Secrets have to be defined at the same indentation level of services and the notation is this one:
secrets: a_secret_name: file: existing_fileThis YAML snippet defines a secret named 'a_secret_name' that reads its value from a file called 'existing_file'. In container orchestration systems like Docker Swarm or Kubernetes, this tells the system to load sensitive data from disk rather than embedding it directly in the configuration.
You can use what you want for a_secret_name; for instance, my_ssh_key.
docker compose config
You can, if you want, run docker compose config to check if your file is correct. You'll also see the full path for the used key.

Dockerfile
Time to create our second file, Dockerfile:
FROM alpine:3.19
ARG KEY_NAME="id_rsa"
USER root
RUN apk add --no-cache bash ca-certificates git openssh-client
// highlight-next-lineRUN mkdir -p -m 0700 /root/.ssh && ssh-keyscan "github.com" >> /root/.ssh/known_hosts
// highlight-next-lineRUN --mount=type=secret,id=my_ssh_key,dst=/root/.ssh/${KEY_NAME} \ // highlight-next-line mkdir -p /app && cd /app \ // highlight-next-line && ssh -T git@github.com || true \ // highlight-next-line && git clone git@github.com:cavo789/my_private_repo.git
WORKDIR /app
# Keep the container runningENTRYPOINT ["tail", "-f", "/dev/null"]This Dockerfile creates a lightweight Alpine container that can clone private GitHub repositories using SSH. It sets up SSH client tools, configures GitHub's host key to prevent connection prompts, securely mounts an SSH private key at build time, tests the connection, and clones a private repository into the container.
Remember to replace cavo789/my_private_repo.git with one of your own repositories
As you can see, we are setting KEY_NAME="id_rsa" as the default value (but will be overwritten by our declaration in the yaml file) then later on, we need to create the /root/.ssh folder and we are adding github.com in the list of known hosts.
The most important thing comes then. We should use the RUN --mount=type=secret syntax to inform Docker that this layer will use a Docker secret. We need to provide the name of our secret (which was set in our yaml file and it's my_ssh_key here) then we need to define where that secret has to be stored during this layer. Our variable KEY_NAME finds its use here: our SSH key was id_ed25519 and this is the value of the KEY_NAME variable.
So, in short --mount=type=secret,id=my_ssh_key,dst=/root/.ssh/${KEY_NAME} will be translated to --mount=type=secret,id=my_ssh_key,dst=/root/.ssh/id_ed25519.
During this layer thus, our local SSH key will be saved to /root/.ssh/id_ed25519. The other lines in that RUN layer will first create a /app folder, jump in it, then run ssh -T git@github.com just for debugging (we expect to see on the screen Hi your_name! You've successfully authenticated) and finally we're git cloning our private repository using SSH.
Create the image and jump in the container
Ok, so you've the created the two files on your hard disk.
Run docker compose --progress plain build --no-cache in your console to build the image and enable the verbose mode.
As you can see, we can confirm that our SSH key was shared during the build process — that's the "You're authenticated" output shown at the top of this article.
You'll now create the container; by running docker compose up --detach.
Now, just to check, we can jump inside the container by running docker compose exec app /bin/bash.

We can verify that our key wasn't stored in the image by running ls -alh /root/.ssh/

We can check too: jumping in our project folder and running git pull will fail with the error below:
And this is what we expect: our key isn't part of the image so we can't connect anymore to Github.
Sharing your keys also with the container
Ok, we've seen how to share the SSH key with Docker but only during the build phase. As we've seen above, jumping in the container and running git pull has failed; exactly what we wanted in the previous chapter.
But now, if we need to share our key also with the container so we can work on the project, update it and push changes / pull modifications, how to do?
Please edit the compose.yaml file and add these two lines:
services: app: build: context: . dockerfile: Dockerfile secrets: - my_ssh_key args: - KEY_NAME=id_ed25519 // highlight-next-line volumes: // highlight-next-line - ${HOME}/.ssh:/root/.ssh
secrets: my_ssh_key: file: ${HOME}/.ssh/id_ed25519This Docker Compose configuration sets up a service called 'app' that builds a Docker image using a Dockerfile in the current directory. It passes an SSH key into the build process both as a secret and by mounting the host's SSH directory into the container, allowing the application to authenticate with external services that require SSH credentials.
Then recreate the container by running docker compose up --detach. Jump in the container by running docker compose exec app /bin/bash and go in your project folder. Now, by running git pull, as you'll see, it'll work.
Why? Because your SSH keys are now part of the container as we can see by running ls -alh /root/.ssh:

Keys are not stored in the image even when you've shared them with the container
Just to be clear: the notion of volume we've just implemented concerns the container and not the image. In other words, SSH keys are not stored in the Docker image. You could give it to someone else (e.g. by saving the image on Docker Hub); your keys won't be there and will remain on your computer. Your image and your secrets are safe.
Then, when the user run docker compose up --detach using the compose.yaml file where there are the two lines we've just added (the ones for adding the volume); these are his keys, on his computer, not yours.
Please take note of the running user in your container
In our example here, the default user is root as we can see by jumping in the container and running whoami.
So, when we've started git pull, it was under root. This is why we've mounted our volume like below:
services: app: [...] // highlight-next-line volumes: // highlight-next-line - ${HOME}/.ssh:/root/.sshThis is a Docker Compose configuration snippet that mounts the SSH keys from your home directory into a container's root user directory. This allows the containerized application to use your SSH credentials for things like accessing git repositories or remote servers without needing to copy keys into the image.
Imagine the current user was christophe. In that case the mount should be done like this:
services: app: [...] // highlight-next-line volumes: // highlight-next-line - ${HOME}/.ssh:/christophe/.sshThis Docker Compose configuration mounts the SSH credentials from your local machine into a container so that the app service can authenticate using your SSH keys. The ${HOME} variable expands to your user's home directory, making the config portable across different machines.
Conclusion
Two different needs, two different mechanisms: a build-time secret (--mount=type=secret)
that never touches the final image, for one-off operations like cloning a private repo during
docker build; and a bind-mounted volume, for when the running container itself needs your
key on an ongoing basis to git pull/push. Pick the first by default, and only add the second
when you actually need to work from inside the container.
This article is part of the SSH - From your first key to remote development series:
- GitHub - Connect your account using SSH and start to work with git@ protocol
- Docker secrets - Using your SSH key during the build process
- Using sftp on the command line, with or without a proxy
- SSH - Autosuggestions with ZSH
- SSH - Launch a terminal on your session without having to authenticate yourself
- How to connect to your hosting server using SSH
- SSH Remote development with VSCode
- Open a DevContainer on a Remote Server with VSCode Remote SSH and ProxyJump
- SSH Config Generator: Wildcard Stanzas, Aliases, and Automated Logins




