# How actions/checkout Handles Credentials for Submodules in Containers

> Learn how actions/checkout handles submodule credentials in containers. Discover how it creates temp credential files and uses includeIf rules for seamless Git authentication within Docker environments.

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

---

**The `actions/checkout` action injects authentication tokens into submodules by creating a temporary shared credentials file and adding conditional `includeIf.gitdir` rules to both host-side and container-side Git configurations, ensuring seamless authentication whether Git commands run on the runner or inside a Docker container.**

When running GitHub Actions jobs inside Docker containers, accessing private submodules requires careful credential propagation across filesystem boundaries. The `actions/checkout` action solves this through a sophisticated authentication helper that mirrors security tokens between the host runner and the containerized environment. According to the `actions/checkout` source code, this process centers on the `configureSubmoduleAuth()` method in [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts), which dynamically generates Git configuration entries for both execution contexts.

## The Submodule Authentication Architecture

### Entry Point in [`git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/git-auth-helper.ts)

All submodule credential handling flows through `GitAuthHelper.configureSubmoduleAuth()` in [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts) (lines 57-130). This method orchestrates the creation of temporary credential files and the injection of Git configuration directives that apply conditionally based on the repository's location, ensuring that authentication material is available regardless of where Git commands execute.

### Dual-Path Configuration Strategy

The helper operates on two distinct filesystem perspectives simultaneously. It identifies host-side Git configuration paths using `git.getSubmoduleConfigPaths()` (lines 71-74), while mapping these to container-specific locations under `/github/workspace` (lines 196-203). This dual-path approach ensures that Git commands execute correctly whether they run on the runner host or inside the containerized environment, with both configurations pointing to the same physical credential file through bind mounts.

## Step-by-Step Credential Injection Process

### 1. Removal of Stale Configuration

Before injecting new credentials, the helper sanitizes existing Git configurations by removing previous `insteadOf` entries that might interfere with authentication (lines 57-60). This prevents credential leakage or conflicts from earlier workflow steps or previous checkouts.

### 2. Persist-Credentials Validation

The entire submodule authentication flow is gated by the `persist-credentials` input parameter. When set to `false`, the method exits immediately without writing sensitive data to disk (lines 61-63). By default, this value is `true`, enabling credential persistence for subsequent Git operations.

### 3. Shared Credentials File Creation

The helper generates a temporary file under `RUNNER_TEMP` named `git-credentials-<uuid>.config` (lines 24-30). This file contains the HTTP extra-header with the GitHub token and is designed to be accessible from both the host and the container via the runner's bind mount architecture (lines 33-40).

### 4. Host and Container Path Resolution

For each submodule, the helper determines two critical paths:

- **Host path**: The actual `.git/modules/<name>/config` file location retrieved via `git.getSubmoduleConfigPaths()` (lines 71-74)
- **Container path**: A POSIX-constructed path mirroring the workspace structure:

```typescript
const containerSubmoduleGitDir = path.posix.join(
  '/github/workspace',
  relativeSubmoduleGitDir
)

```

This mapping appears at lines 196-203 in [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts), translating host filesystem locations to the container's `/github/workspace` mount point.

### 5. Conditional IncludeIf Rules

The method inserts two `includeIf.gitdir` directives for every submodule configuration:

- **Host rule**: `includeIf.gitdir:<host-git-dir>.path` pointing to the credentials file (lines 84-90)
- **Container rule**: `includeIf.gitdir:<container-git-dir>.path` referencing the same file via `/github/runner_temp` (lines 105-112)

These conditional includes ensure Git only loads credentials when operating within specific submodule directories, preventing token leakage to unrelated repositories.

### 6. Protocol-Specific Handling

For SSH-based submodules, the helper configures `core.sshCommand` for each submodule using `git submodule foreach`. For HTTPS repositories, it creates URL rewrite rules using `insteadOf` to convert SSH URLs to HTTPS equivalents (lines 122-128). This applies to both host and container contexts, ensuring consistent behavior regardless of the transport protocol.

### 7. Secure Cleanup

When the checkout step completes, `removeAuth()` and `removeSubmoduleGitConfig()` delete the temporary credentials file and strip all `includeIf` entries from submodule configurations (lines 72-78, 132-138). This ensures no authentication material persists beyond the job's execution, preventing credential leakage to subsequent workflow steps or different jobs.

## Configuration Requirements

To leverage this functionality in containerized workflows, configure your action with the appropriate inputs:

```yaml

# .github/workflows/example.yml

name: Checkout with submodules in Docker
on: [push]

jobs:
  test:
    runs-on: ubuntu-latest
    container: node:18   # any Docker container

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          submodules: true            # fetch submodules

          persist-credentials: true  # keep auth for submodule ops (default)

          # optional: ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}

```

The TypeScript implementation simplifies to these key calls:

```typescript
// Inside the action (simplified)
await authHelper.configureAuth();          // configure host SSH/HTTPS token
await authHelper.configureSubmoduleAuth(); // adds includeIf for submodules
// Git submodule commands now use the same token, even inside the container
await exec.exec('git', ['submodule', 'update', '--init', '--recursive']);

```

## Summary

- **`actions/checkout`** uses [`src/git-auth-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts) to manage submodule credentials through the `configureSubmoduleAuth()` method (lines 57-130)
- The system creates a **temporary shared credentials file** under `RUNNER_TEMP` accessible to both host and container via bind mounts
- **Dual `includeIf.gitdir` rules** target both host paths and container paths (`/github/workspace`) for each submodule
- Authentication only persists when **`persist-credentials`** is `true`, with the method exiting early at lines 61-63 if disabled
- **Automatic cleanup** via `removeAuth()` and `removeSubmoduleGitConfig()` removes all credential files and Git configuration modifications after the step completes
- Both **SSH** (`core.sshCommand`) and **HTTPS** (`insteadOf` rewriting) protocols are supported for submodule authentication

## Frequently Asked Questions

### Does actions/checkout handle submodules differently in containers versus on the host?

No, the authentication mechanism operates transparently across both environments. The helper generates parallel Git configuration entries for host-side paths and container-side paths (under `/github/workspace`), ensuring the same credential file is referenced regardless of where Git commands execute. The `includeIf.gitdir` directives handle path matching automatically based on the current working directory.

### What happens if I set persist-credentials to false?

When `persist-credentials` is set to `false`, the `configureSubmoduleAuth()` method exits immediately after the initial validation check (lines 61-63). No temporary credential files are created under `RUNNER_TEMP`, and no `includeIf` entries are written to submodule configurations. Consequently, subsequent Git operations requiring authentication against private submodules will fail with permission errors.

### How does the action handle SSH keys versus HTTPS tokens for submodules?

If you provide an `ssh-key` input, the helper configures `core.sshCommand` for each submodule using `git submodule foreach`, enabling SSH-based authentication. For HTTPS-based authentication (the default when no SSH key is provided), the helper creates Git URL rewrite rules using `insteadOf` to convert SSH URLs to HTTPS equivalents, applying these rules to both host and container Git configurations (lines 122-128).

### Where are the temporary credential files stored?

The action creates files named `git-credentials-<uuid>.config` in the directory specified by the `RUNNER_TEMP` environment variable (lines 24-30). These files contain the HTTP authentication headers and are mounted into containers at `/github/runner_temp`, allowing both environments to reference the same physical file through different absolute paths while maintaining restrictive file permissions.