Skip to content

Instantly share code, notes, and snippets.

@maurigamg
Last active December 2, 2025 03:00
Show Gist options
  • Select an option

  • Save maurigamg/c4253f3ad4e6798c0bfd9afa9dee8d89 to your computer and use it in GitHub Desktop.

Select an option

Save maurigamg/c4253f3ad4e6798c0bfd9afa9dee8d89 to your computer and use it in GitHub Desktop.
GitHub CLI + SSH Configuration for Alpine Docker

Simple Setup for GitHub CLI (gh) with SSH in Alpine Docker

Description

This project demonstrates how to use GitHub CLI (gh) within a Docker Alpine container to interact with GitHub repositories using SSH authentication.

Note

For demonstration purposes, the project includes a simple Python script (main.py) that clones a GitHub repository using gh with SSH, but the same operation can be done directly within the container's shell environment.

Requirements

Project Structure

├── .ssh/
│   ├── config
│   └── my_gh_key
├── docker-compose.yaml
├── Dockerfile
├── main.py
├── requirements.txt
└── token.txt
  • .ssh/: Contains SSH configuration and private key for GitHub access.
    • config: SSH configuration file
    • my_gh_key: Private SSH key for GitHub.
  • docker-compose.yaml: Docker Compose configuration file.
  • Dockerfile: Instructions to build the Docker image.
  • main.py: Python script to clone a GitHub repository using gh.
  • requirements.txt: Python dependencies (Not included in this gist since it's empty).
  • token.txt: Contains GitHub token for authentication.

The .ssh/config is not included in this gist. However, it might look like this:

Host github.com
  IdentityFile /root/.ssh/my_gh_key
  # Other SSH options...

Usage

  1. Ensure you have your SSH key and GitHub token properly configured.
  2. Build and run the Docker container using Docker Compose:
docker-compose up --build

...
gh-1  | Cloning into 'clone-ssh'...
gh-1  | 
gh-1  | 0
gh-1  | Cloning into 'clone-ssh-explicit'...
gh-1  | 
gh-1  | 0
gh-1  | Cloning into 'clone-https'...
gh-1  | 
gh-1  | 0
gh-1 exited with code 0
services:
gh:
image: gh:local
build:
# args:
# - TOKEN=${GITHUB_ACCESS_TOKEN}
context: .
environment:
- GH_TOKEN=${GITHUB_ACCESS_TOKEN}
volumes:
# Name the copied SSH key file according to its algorithm (e.g., 'id_rsa')
# so that SSH can find it without using an explicit 'config' file.
- ./.ssh/my_gh_key:/root/.ssh/id_ed25519:ro
# OPTIONAL: If you want to store the key under a different name (e.g., 'my_gh_key'),
# you must mount a custom SSH config file to define the key path (IdentityFile).
# - ./.ssh/config:/root/.ssh/config:ro
# - ./.ssh/my_gh_key:/root/.ssh/my_gh_key:ro
FROM python:3.13-alpine
LABEL authors="maurigamg"
# 1. Update and upgrade Alpine packages
RUN apk --no-cache update && \
apk --no-cache upgrade && \
rm -rf /var/cache/apk/*
# 2. Install the GitHub CLI
## Option 1: Download a precompiled binary and install it manually (Recommended)
## In this case, we're downloading the latest release for linux_arm64 architecture because
## the base image is for that architecture.
RUN apk add --no-cache git curl jq \
&& curl -L \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/cli/cli/releases/latest \
| jq -r '.assets[] | select(.name | endswith("linux_arm64.tar.gz")) | .browser_download_url' \
| xargs curl -L -o /tmp/gh.tar.gz \
&& tar -xzf /tmp/gh.tar.gz -C /tmp/ \
&& mv /tmp/gh_*/bin/gh /usr/local/bin/gh \
&& rm -rf /tmp \
&& apk del curl jq
## Notes: If you need a specific version, replace the above RUN command with steps like these:
# ARG GH_CLI_VERSION=2.83.1
# RUN apk add --no-cache git curl \
# && curl -L -o /tmp/gh.tar.gz \
# "https://github.com/cli/cli/releases/download/v${GH_CLI_VERSION}/gh_${GH_CLI_VERSION}_linux_arm64.tar.gz" \
# && tar -xzf /tmp/gh.tar.gz -C /tmp/ \
# && mv "/tmp/gh_${GH_CLI_VERSION}_linux_arm64/bin/gh" /usr/local/bin/gh \
# && rm -rf /tmp \
# && apk del curl
## Option 2: Install via Alpine Linux community (Unofficial)
# See https://github.com/cli/cli/blob/trunk/docs/install_linux.md#alpine-linux
# 3. Authenticate GitHub CLI using a token
## Option 1: Setting GH_TOKEN environment variable (see docker-compose.yaml)
## Option 2: Using a token file
#COPY token.txt /tmp/token.txt
#RUN gh auth login --with-token < /tmp/token.txt && rm /tmp/token.txt
## Option 3: Using build argument
#ARG TOKEN
#RUN gh auth login --with-token < <(printf "%s" "${TOKEN}")
# 4. Setup SSH for Git operations
# 4.1 Configure GitHub CLI to use SSH for git operations
RUN gh config set git_protocol ssh -h github.com
## 4.2 Install openssh-client
RUN apk add --no-cache openssh-client
## 4.3 Create known_hosts file to avoid SSH authenticity prompt since we cannot interactively accept it
### Option 1: Using ssh-keyscan, which is simpler and already comes with openssh-client
RUN mkdir -p /root/.ssh && ssh-keyscan github.com > /root/.ssh/known_hosts
### Option 2: Using GitHub API to get SSH keys (more complex)
# RUN apk add --no-cache curl jq \
# && mkdir -p /root/.ssh \
# && curl -L \
# -H "Accept: application/vnd.github+json" \
# -H "X-GitHub-Api-Version: 2022-11-28" \
# https://api.github.com/meta \
# | jq -r '.ssh_keys[] | "github.com \(.)"' \
# > /root/.ssh/known_hosts \
# && apk del curl jq
### Option 3: Creating a static known_hosts file with hardcoded keys (see https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints)
# RUN mkdir -p /root/.ssh && cat <<EOF > /root/.ssh/known_hosts
# github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
# github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
# github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
# EOF
WORKDIR /app
COPY main.py main.py
COPY requirements.txt requirements.txt
RUN pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir -r requirements.txt \
&& rm -rf .cache/pip \
&& rm requirements.txt
ENTRYPOINT ["python", "main.py"]
import subprocess
def main():
repos = {
"cli/cli": "clone-ssh", # default with ssh
"git@github.com:cli/cli.git": "clone-ssh-explicit", # with ssh explicitly
"https://github.com/cli/cli.git": "clone-https" # with https explicitly
}
for repo, folder in repos.items():
result = subprocess.run(
args=["gh", "repo", "clone", repo, folder],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
)
print(result.stdout)
print(result.returncode)
# You can force the last command (https) to use ssh
# by executing the following configuration command
# git config --global url."git@github.com:".insteadOf "https://github.com/"
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment