# How actions/checkout Configures SSH Authentication and Known Hosts

> Learn how actions/checkout configures SSH authentication and known hosts by managing private keys and known hosts via temporary files and the GIT_SSH_COMMAND environment variable.

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

---

**The `actions/checkout` GitHub Action handles SSH authentication through a dedicated `GitAuthHelper` class that writes the private key and known-hosts to temporary files, constructs a custom SSH command, and exports it via the `GIT_SSH_COMMAND` environment variable.**

When cloning private repositories or accessing submodules via SSH in GitHub Actions, `actions/checkout` automatically manages authentication without requiring manual SSH agent setup. Understanding how the action configures SSH authentication and known hosts helps debug connectivity issues and secure self-hosted runner environments.

## The GitAuthHelper Architecture

All SSH logic is encapsulated in the **`GitAuthHelper`** class located in **[`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts)**. When the action receives the optional `ssh-key` input (and optionally `ssh-known-hosts`), the main entry point **`configureAuth()`** delegates to **`configureSsh()`** to set up the authentication context.

## The SSH Configuration Pipeline

The `configureSsh()` method executes a three-phase process to prepare Git for SSH operations.

### Step 1: Secure Private Key Storage

**`configureSsh()`** generates a unique temporary file under `$RUNNER_TEMP` and writes the trimmed private key contents with strict Unix permissions (`0o600`). The file path is stored in the action state as `SshKeyPath` (defined in **[`src/state-helper.ts`](https://github.com/actions/checkout/blob/main/src/state-helper.ts)**), ensuring the post-run cleanup phase can locate and remove the sensitive material.

```typescript
// From src/git-auth-helper.ts
await fs.promises.writeFile(this.sshKeyPath, `${this.settings.sshKey.trim()}\n`, {mode: 0o600});

```

### Step 2: Building the Known Hosts File

The action constructs a temporary known-hosts file by merging three sources:

1. **Existing system entries**: Reads the current `~/.ssh/known_hosts` if it exists
2. **User-supplied entries**: Appends the value from the `ssh-known-hosts` input
3. **Default GitHub entry**: Always appends a hard-coded RSA fingerprint for `github.com`

This concatenated content is written to another temporary file under `$RUNNER_TEMP`, and its path is saved in the action state as `SshKnownHostsPath`.

### Step 3: Injecting the SSH Command

The helper locates the system `ssh` executable and builds a command string that points to the temporary credentials:

- **Identity file**: `-i "$RUNNER_TEMP/<keyfile>"`
- **Known hosts**: `-o "UserKnownHostsFile=$RUNNER_TEMP/<hostsfile>"`
- **Strict checking**: If `ssh-strict` is `true`, adds `-o StrictHostKeyChecking=yes -o CheckHostIP=no`

The resulting command is exported to the **`GIT_SSH_COMMAND`** environment variable so all subsequent `git` operations use the custom configuration.

```typescript
// Command construction from src/git-auth-helper.ts
this.sshCommand = `"${sshPath}" -i "$RUNNER_TEMP/${path.basename(this.sshKeyPath)}"` +
  (this.settings.sshStrict ? ' -o StrictHostKeyChecking=yes -o CheckHostIP=no' : '') +
  ` -o "UserKnownHostsFile=$RUNNER_TEMP/${path.basename(this.sshKnownHostsPath)}"`;
this.git.setEnvironmentVariable('GIT_SSH_COMMAND', this.sshCommand);

```

## Persistence for Submodules and Later Steps

If the `persist-credentials` input is set to `true`, the same SSH command is also written to the repository's local Git configuration under **`core.sshCommand`**. This ensures that submodule operations or subsequent workflow steps reuse the same authentication context without re-exporting environment variables.

```typescript
// Persistence logic from src/git-auth-helper.ts
if (this.settings.persistCredentials) {
  await this.git.config('core.sshCommand', this.sshCommand);
}

```

## Automatic Cleanup

During the post-job phase, the action retrieves the stored `SshKeyPath` and `SshKnownHostsPath` from the state (via **[`src/state-helper.ts`](https://github.com/actions/checkout/blob/main/src/state-helper.ts)**) and invokes **`removeSsh()`** to securely delete the temporary files, preventing credential leakage between jobs on self-hosted runners.

## Configuration Example

Use the following workflow syntax to enable SSH authentication with strict host checking for a self-hosted Git server:

```yaml
steps:
  - uses: actions/checkout@v4
    with:
      ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
      ssh-known-hosts: |
        selfhost.example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE...
      ssh-strict: true
      persist-credentials: true

```

## Summary

- **`GitAuthHelper`** in [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts) orchestrates the entire SSH authentication flow.
- The action writes the private key to a temporary file with `0o600` permissions and tracks it via action state.
- Known hosts are assembled from the system, user input, and a hard-coded GitHub fingerprint, then stored in a temporary file.
- Git operations receive the custom SSH configuration through the **`GIT_SSH_COMMAND`** environment variable.
- When `persist-credentials` is enabled, the configuration is saved to `core.sshCommand` for submodule support.
- Temporary files are automatically removed during the post-job cleanup phase.

## Frequently Asked Questions

### Where does actions/checkout store the SSH private key?

The action writes the private key to a uniquely named temporary file under `$RUNNER_TEMP` with strict permissions (`0o600`). The path is stored in the action state as `SshKeyPath` (defined in [`src/state-helper.ts`](https://github.com/actions/checkout/blob/main/src/state-helper.ts)) so the post-run cleanup can locate and delete it.

### How does actions/checkout handle known hosts for GitHub.com?

The action automatically appends a hard-coded RSA fingerprint for `github.com` to the known-hosts file. This default entry is concatenated with any existing entries from `~/.ssh/known_hosts` and additional hosts provided via the `ssh-known-hosts` input.

### Can I use actions/checkout with a self-hosted Git server?

Yes. Provide your server's host key via the `ssh-known-hosts` input and your private key via the `ssh-key` input. The action will include your custom host entries alongside the default GitHub entry in the temporary known-hosts file.

### What is the difference between the ssh-strict and persist-credentials inputs?

The `ssh-strict` input adds `-o StrictHostKeyChecking=yes` to the SSH command, forcing strict host key verification. The `persist-credentials` input saves the constructed SSH command to the Git config key `core.sshCommand`, ensuring that submodule operations or later workflow steps reuse the same authentication configuration.