Accessing Private GitHub Repos in CircleCI
When setting up a CI/CD pipeline, you often need to work with private repositories. If one repository depends on code from another private GitHub repository, things can get tricky in an automated build environment. This post explains this common issue, how to solve it by adding SSH fingerprints in CircleCI and mount them during Docker builds.
The Problem
Consider this scenario:
- You have a private GitHub repository containing a package.
- Another repository depends on that package and installs it during the build process.
On your local machine, cloning works fine because you already have the necessary SSH access:
1
git clone git@github.com:org/private-repo.git
But in a CI/CD pipeline, the build runs on a fresh container or VM without your SSH credentials. As a result, the git clone
fails with a permission error.
You could package the dependency as a wheel or upload it to a private package index, but that adds extra maintenance. A more direct approach is to allow the pipeline itself to authenticate and clone the repository. Ofcourse, this might not be the best way forward but sometimes it is an only requirement.
Using SSH Fingerprints in CircleCI
CircleCI supports adding SSH keys to a project. These keys are associated with a fingerprint, which you can then reference in your pipeline configuration.
Here’s how you can set it up:
1. Generate an SSH Key Pair
1
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
This gives you:
- A private key (kept secret)
- A public key (shared with GitHub)
Complete reference can be found here to generate a new SSH key.
2. Add the Public Key to GitHub
Go to GitHub SSH settings and add the public key.
3. Add the Private Key to CircleCI
- In CircleCI, open your project settings.
- Navigate to SSH Keys > Additional SSH Keys.
- Paste in the private key.
- CircleCI will display the corresponding fingerprint.
Reference: CircleCI docs on adding SSH keys
4. Store the Fingerprint as an Environment Variable
In Project Settings > Environment Variables, add:
- Name:
GH_SSH_FINGERPRINT
- Value:
12:34:56:78:9a:bc:de:f0:12:34:56:78:9a:bc:de:f0
5. Reference the SSH Fingerprint in Config
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
version: 2.1
jobs:
build:
docker:
- image: cimg/base:stable
steps:
- checkout
- add_ssh_keys:
fingerprints:
- $GH_SSH_FINGERPRINT
- setup_remote_docker:
docker_layer_caching: false
- run:
name: Build Docker image
command: docker build --ssh default -t myapp:latest .
SSH Mount in Dockerfile
When installing private dependencies inside Docker, you need to mount the SSH key during the build. Docker supports this with the --mount=type=ssh
option.
Here’s an example Dockerfile
:
1
2
3
4
5
6
7
8
9
10
11
FROM python:3.12-alpine
# Trust GitHub SSH host
RUN apk add openssh && \
mkdir -p -m 0600 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
# Install dependencies
# The private repo is referenced in pyproject.toml (or requirements.txt)
# Example: git+ssh://git@github.com/org/private-repo.git@main
# `pip install .` is just for demonstration simplicity; prefer a package manager
RUN --mount=type=ssh pip install .
With this setup, Docker can use the SSH key provided by CircleCI during the build, letting private repositories install securely. No more broken builds because of dreaded “Permission denied” errors in CI/CD.
Adding SSH fingerprints and mounting them into Docker builds may feel like an extra step at first, but it removes that pain for good. Once this is in place, your CI/CD flow mirrors the convenience of local development and that consistency is what makes shipping code a whole lot smoother as your project dependencies grow.