# How actions/checkout Manages Known Hosts for SSH Connections

> actions/checkout creates a temporary known_hosts file for SSH connections by merging runner keys, workflow entries, and GitHub host key for secure Git operations.

- Repository: [GitHub Actions/checkout](https://github.com/actions/checkout)
- Tags: how-to-guide
- Published: 2026-08-29

---

**actions/checkout** constructs a temporary **known-hosts** file that isolates SSH trust by combining the runner's default keys, workflow-supplied entries, and a built-in GitHub host key, then injects this configuration via the `GIT_SSH_COMMAND` environment variable.

When cloning repositories over SSH in GitHub Actions, the `actions/checkout` action implements a secure, ephemeral approach to host key verification. Rather than modifying the runner's global SSH configuration, it creates temporary credential files that exist only for the job duration. This article examines the source code implementation to explain how the action manages known hosts without compromising runner security.

## Input Configuration for SSH Connections

The process begins in [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts), where the action reads SSH-specific workflow inputs. At line 161, the helper retrieves the `ssh-known-hosts` input using `result.sshKnownHosts = core.getInput('ssh-known-hosts')`.

- **ssh-key**: The private key used for authentication, stored as a repository secret.
- **ssh-known-hosts**: Additional host entries provided by the workflow author, typically containing public keys for self-hosted Git servers.
- **ssh-strict**: An optional boolean that enforces strict host key checking when enabled.

## The GitAuthHelper.configureSsh() Method

Core orchestration occurs in [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts) within the `configureSsh()` method. This function handles both key preparation and known-hosts assembly while ensuring no credentials leak between jobs.

### Writing the Private Key

When `ssh-key` is provided, the helper writes the key to a temporary file under `RUNNER_TEMP` with restrictive permissions (mode `600`). The path is persisted using `stateHelper.setSshKeyPath()` for later cleanup. This prevents unauthorized read access while the key remains on the filesystem during the job execution.

### Assembling the Known Hosts Payload

The action constructs a comprehensive known-hosts file by aggregating three distinct sources:

1. **Runner defaults**: The existing contents of `~/.ssh/known_hosts` from the GitHub-hosted runner image.
2. **User inputs**: Entries supplied via the `ssh-known-hosts` workflow input.
3. **Built-in GitHub key**: An implicit entry for `github.com` containing the public RSA key shipped with the action.

This combined payload is written to a uniquely named file (`<uniqueId>_known_hosts`) inside `RUNNER_TEMP`. The path is saved via `stateHelper.setSshKnownHostsPath()`, ensuring the post-action cleanup phase can locate and remove the file.

## Injecting SSH Configuration via GIT_SSH_COMMAND

Rather than modifying `~/.ssh/config`, the action uses the `GIT_SSH_COMMAND` environment variable to override SSH behavior exclusively for Git operations. In [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts) (lines 101-114), the helper constructs a command string:

```typescript
const sshPath = await io.which('ssh', true)
this.sshCommand = `"${sshPath}" -i "$RUNNER_TEMP/${path.basename(this.sshKeyPath)}"`
if (this.settings.sshStrict) {
  this.sshCommand += ' -o StrictHostKeyChecking=yes -o CheckHostIP=no'
}
this.sshCommand += ` -o "UserKnownHostsFile=$RUNNER_TEMP/${path.basename(this.sshKnownHostsPath)}"`

```

The command explicitly:
- Points to the temporary private key using the `-i` flag.
- References the temporary known-hosts file via `UserKnownHostsFile`.
- Enforces strict checking when `ssh-strict` is enabled, while disabling IP checks to avoid mismatches in dynamic cloud environments.

This command is exposed to Git through `this.git.setEnvironmentVariable('GIT_SSH_COMMAND', this.sshCommand)`.

### Persisting Credentials for Submodules

When the `persist-credentials` input is set to `true`, the action stores the same SSH command in Git's global configuration at `core.sshCommand` (lines 115-119 in [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts)). This ensures that subsequent submodule operations inherit the identical host verification policy without re-executing the checkout action.

## Secure Cleanup and State Management

Security isolation relies on complete removal of temporary files after job completion. The [`src/state-helper.ts`](https://github.com/actions/checkout/blob/main/src/state-helper.ts) module manages this through its cleanup functions (lines 43-52). During the post-action phase, the helper retrieves the stored paths for both the SSH key and known-hosts files, then deletes them from `RUNNER_TEMP`. This guarantees no sensitive material persists on the runner for subsequent jobs.

## Practical Implementation Examples

Configure SSH-based checkout in your workflow by supplying the appropriate inputs:

```yaml
name: Checkout with SSH Authentication
on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout via SSH with custom hosts
        uses: actions/checkout@v4
        with:
          ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
          ssh-known-hosts: |
            git.mycompany.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...
            10.0.0.5 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE...
          ssh-strict: true

```

For advanced scenarios requiring programmatic control, use the internal helper directly:

```typescript
import { createAuthHelper } from '@actions/checkout/lib/git-auth-helper';

const authHelper = createAuthHelper(gitCommandManager, {
  sshKey: process.env.SSH_PRIVATE_KEY,
  sshKnownHosts: process.env.SSH_KNOWN_HOSTS,
  sshStrict: true,
  persistCredentials: true
});

await authHelper.configureSsh();

```

## Summary

- **Temporary isolation**: actions/checkout never modifies the runner's global `~/.ssh/known_hosts` file.
- **Multi-source validation**: The [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts) module combines runner defaults, user inputs, and a built-in GitHub key into a single temporary file.
- **Environment injection**: SSH configuration uses `GIT_SSH_COMMAND` with explicit `UserKnownHostsFile` and identity file paths rather than system configuration.
- **Automatic cleanup**: The [`src/state-helper.ts`](https://github.com/actions/checkout/blob/main/src/state-helper.ts) module guarantees removal of all temporary files and keys after job completion.
- **Submodule support**: Persistent credentials store the SSH command in Git config for nested repository operations.

## Frequently Asked Questions

### Does actions/checkout modify the runner's permanent SSH configuration?

No. According to the actions/checkout source code, the action exclusively uses temporary files within `RUNNER_TEMP` and environment variables. The cleanup routine in [`src/state-helper.ts`](https://github.com/actions/checkout/blob/main/src/state-helper.ts) (lines 43-52) ensures complete removal of keys and known-hosts files after the job completes, leaving no trace on the runner.

### Why does the action include a built-in GitHub host key?

The implicit github.com entry ensures immediate compatibility with GitHub-hosted repositories without requiring users to manually specify GitHub's public RSA key. This key is hardcoded in the action and appended to any user-supplied known-hosts entries in [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts), providing a seamless experience for the primary use case while still allowing custom host definitions.

### How does strict host key checking work with actions/checkout?

When `ssh-strict: true` is set in the workflow, the action appends `-o StrictHostKeyChecking=yes` to the `GIT_SSH_COMMAND` constructed in [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts). This forces SSH to reject connections where the host key is not found in the temporary known-hosts file, while the accompanying `-o CheckHostIP=no` prevents IP address mismatches that commonly occur in dynamic cloud environments.

### Can I use actions/checkout with self-hosted runners that have existing SSH configurations?

Yes. The action safely reads the existing `~/.ssh/known_hosts` from the runner and includes those entries in the temporary file created in `RUNNER_TEMP`. However, it still isolates the configuration via `GIT_SSH_COMMAND`, ensuring your job trusts only the explicit key set without affecting other processes running concurrently on the self-hosted runner.