How to Configure SSH Keys for Checkout in GitHub Actions: A Complete Guide
To configure SSH keys for repository checkout in GitHub Actions, provide your private key via the ssh-key input in actions/checkout, which automatically writes the credential to a temporary file, configures Git's core.sshCommand, and securely removes all sensitive data after job completion.
The actions/checkout action provides native support for SSH authentication, enabling secure access to private repositories and submodules without manual credential management on runners. When you configure SSH keys for checkout in GitHub Actions using the dedicated inputs, the action handles temporary file creation, host verification, and automatic cleanup according to GitHub's security model.
SSH Authentication Inputs
The action exposes four inputs specifically for SSH configuration, defined in action.yml and consumed by src/input-helper.ts.
The Four Key Inputs
ssh-key: The private SSH key content, typically stored as an encrypted GitHub Secret. This key is written to a temporary file in$RUNNER_TEMPduring execution.ssh-known-hosts: Additional SSH host keys to trust, formatted as you would see in a standardknown_hostsfile. Usessh-keyscanto generate entries for internal Git servers.ssh-strict: Boolean flag defaulting totrue. When enabled, the SSH command includesStrictHostKeyChecking=yesandCheckHostIP=noto prevent man-in-the-middle attacks.ssh-user: The username for SSH connections, defaulting togitfor standard GitHub repositories.
How the Action Processes SSH Credentials
The implementation spans two critical source files that handle input parsing and authentication configuration.
Input Parsing in input-helper.ts
In src/input-helper.ts, the getInputs() function reads workflow inputs and populates the IGitSourceSettings interface:
// Conceptual representation based on source implementation
result.sshKey = core.getInput('ssh-key')
result.sshKnownHosts = core.getInput('ssh-known-hosts')
result.sshStrict = core.getBooleanInput('ssh-strict')
result.sshUser = core.getInput('ssh-user') || 'git'
SSH Configuration in git-auth-helper.ts
The src/git-auth-helper.ts file contains the configureAuth() method, which calls configureSsh() to establish the authentication context. This process performs four critical operations:
- Writes the private key from
ssh-keyto a temporary file with restricted permissions (0600) - Writes known hosts data to
$RUNNER_TEMPif provided viassh-known-hosts - Constructs an SSH command string combining the key path, user, and strictness options
- Executes
git config core.sshCommandto persist the settings for all subsequent Git operations
When ssh-strict is true (default), the generated command resembles:
ssh -i "$RUNNER_TEMP/ssh-key-random-id" -o StrictHostKeyChecking=yes -o CheckHostIP=no -o "UserKnownHostsFile=$RUNNER_TEMP/known_hosts" -l git
When ssh-strict is false, the StrictHostKeyChecking and CheckHostIP options are omitted, allowing connections to hosts not present in known_hosts.
Temporary File Handling and Cleanup
The action generates unique temporary filenames for SSH keys to prevent collisions between concurrent jobs. After job completion, a post-step defined in the action metadata removes:
- The temporary private key file from
$RUNNER_TEMP - The temporary known-hosts file (if created)
- The
core.sshCommandGit configuration entry
This ensures no SSH credentials persist on self-hosted or GitHub-hosted runners after the workflow finishes.
Step-by-Step Workflow Configuration
Basic SSH Checkout Example
Store your SSH private key as a repository secret (typically named SSH_PRIVATE_KEY), then reference it in your workflow:
name: Checkout with SSH
on: [push]
jobs:
checkout:
runs-on: ubuntu-latest
steps:
- name: Checkout private repository
uses: actions/checkout@v4
with:
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
This minimal configuration uses defaults: strict host checking enabled, git as the user, and GitHub's pre-installed host keys.
Advanced Configuration with Known Hosts
For internal Git servers or enhanced security pinning, provide explicit host keys:
- name: Checkout with custom hosts
uses: actions/checkout@v4
with:
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
ssh-known-hosts: |
github.com ssh-rsa AAAAB3NzaC1yc2EAAA...
git.company.internal ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...
ssh-user: git
Generate host keys using ssh-keyscan git.company.internal locally before adding them to your secret or workflow file.
Disabling Strict Host Checking
For testing environments or dynamically provisioned hosts where host keys change frequently, disable strict checking:
- name: Checkout without strict verification
uses: actions/checkout@v4
with:
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
ssh-strict: false
Security Warning: Setting ssh-strict: false removes protection against man-in-the-middle attacks. Only use this in isolated, trusted network environments.
Security and Implementation Details
Why core.sshCommand Instead of GIT_SSH_COMMAND
The action configures Git's core.sshCommand rather than the GIT_SSH_COMMAND environment variable. According to the actions/checkout source code, this approach ensures:
- Persistence across subprocesses: Submodule fetches and subsequent
gitcommands inherit the SSH configuration automatically - Isolation: Settings are scoped to the repository context rather than the entire shell environment
- Cleanup: Git configuration entries are easier to reset than environment variables in action post-processing
This method ensures that nested Git operations (like recursive submodule updates) authenticate correctly without exposing credentials to parent processes.
Summary
- The
actions/checkoutaction accepts four SSH-related inputs (ssh-key,ssh-known-hosts,ssh-strict, andssh-user) processed insrc/input-helper.tsand applied viasrc/git-auth-helper.ts - SSH keys are written to temporary files in
$RUNNER_TEMPwith0600permissions, andcore.sshCommandis configured to use these credentials for all Git operations - Strict host checking (
ssh-strict: true) addsStrictHostKeyChecking=yesandCheckHostIP=noto prevent connection to untrusted hosts, whilefalseallows connections to any host - Automatic post-job cleanup removes temporary key files and Git configuration entries, ensuring no credentials leak between jobs or persist on runners
- Use
ssh-known-hoststo pin specific host keys for internal repositories, or setssh-strict: falsefor testing environments (with security trade-offs)
Frequently Asked Questions
Where should I store the SSH private key in GitHub Actions?
Store the SSH private key as an encrypted repository secret or organization secret, then reference it via ${{ secrets.SECRET_NAME }} in the ssh-key input. Never hardcode SSH keys directly in workflow files. The action writes this key to a temporary file during execution and deletes it immediately after the job completes according to the cleanup logic in git-auth-helper.ts.
What is the difference between ssh-strict true and false?
When ssh-strict is true (default), the action includes StrictHostKeyChecking=yes and CheckHostIP=no in the SSH command, requiring the host to exist in known_hosts and preventing DNS spoofing attacks. When false, these options are omitted from the core.sshCommand configuration, allowing connections to hosts whose keys haven't been cached, which carries security risks but enables connections to new or dynamically provisioned hosts.
How does actions/checkout clean up SSH keys after the job?
The action registers a post-step that executes after your job finishes, calling cleanup functions in git-auth-helper.ts that delete the temporary private key file and known-hosts file from $RUNNER_TEMP, then unset the repository's core.sshCommand configuration. This ensures ephemeral credentials never persist on the runner for subsequent jobs or workflow runs.
Can I use SSH authentication for submodules?
Yes. When you configure SSH keys using the ssh-key input, the core.sshCommand configuration persists for all Git operations in the repository, including recursive submodule fetches. If your submodules reside in different repositories requiring different SSH keys, you must configure authentication separately after the initial checkout or use deploy keys with access to multiple repositories.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →