# actions/checkout Submodules Default Behavior: A Complete Guide

> actions/checkout submodules default behavior: Discover how actions/checkout handles submodules by default and learn to enable them with simple configurations for your Git workflow.

- Repository: [GitHub Actions/checkout](https://github.com/actions/checkout)
- Tags: deep-dive
- Published: 2026-07-17

---

**By default, `actions/checkout` ignores submodules unless explicitly configured with `submodules: true` or `submodules: recursive`.**

The `actions/checkout` repository provides the official GitHub Action for cloning repositories in CI/CD workflows. Understanding how this action handles Git submodules is critical for projects that depend on external libraries or nested repositories, as the default configuration skips submodule initialization entirely to optimize checkout speed.

## The Three Submodule Modes

The `submodules` input accepts three distinct values that control fetch behavior:

- **`false`** (default) — Submodules are completely ignored; only the parent repository is checked out.
- **`true`** — Top-level submodules are fetched and updated, but nested submodules (submodules within submodules) are skipped.
- **`recursive`** — All submodules are fetched recursively, including unlimited nesting levels.

## Implementation Details

### Input Parsing in [`input-helper.ts`](https://github.com/actions/checkout/blob/main/input-helper.ts)

The action normalizes the `submodules` input in [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts) (lines 127-138), converting the string value into boolean flags that control the checkout flow:

```typescript
// src/input-helper.ts
const submodulesString = (core.getInput('submodules') || '').toUpperCase()
if (submodulesString == 'RECURSIVE') {
  result.submodules = true
  result.nestedSubmodules = true
} else if (submodulesString == 'TRUE') {
  result.submodules = true
}

```

When `submodules` is set to `recursive`, both `submodules` and `nestedSubmodules` flags are enabled. Any other value (including empty string or `false`) leaves `submodules` as false, triggering the default behavior where submodule operations are skipped.

### Checkout Flow in [`git-source-provider.ts`](https://github.com/actions/checkout/blob/main/git-source-provider.ts)

The main orchestration logic resides in [`src/git-source-provider.ts`](https://github.com/actions/checkout/blob/main/src/git-source-provider.ts) (lines 74-97), where submodule operations execute only when `settings.submodules` evaluates to true:

```typescript
// src/git-source-provider.ts
if (settings.submodules) {
  core.startGroup('Setting up auth for fetching submodules')
  await authHelper.configureGlobalAuth()
  core.endGroup()

  core.startGroup('Fetching submodules')
  await git.submoduleSync(settings.nestedSubmodules)
  await git.submoduleUpdate(settings.fetchDepth, settings.nestedSubmodules)
  await git.submoduleForeach(
    'git config --local gc.auto 0',
    settings.nestedSubmodules
  )
  core.endGroup()
}

```

This conditional block ensures that when using the default `submodules: false`, the action performs no submodule-specific Git operations, avoiding unnecessary network requests and authentication complications.

### Git Command Execution in [`git-command-manager.ts`](https://github.com/actions/checkout/blob/main/git-command-manager.ts)

Actual Git operations are abstracted in [`src/git-command-manager.ts`](https://github.com/actions/checkout/blob/main/src/git-command-manager.ts) through three key methods:

1. **`submoduleSync(recursive: boolean)`** — Executes `git submodule sync` to align submodule URLs with the parent repository's configuration.
2. **`submoduleUpdate(fetchDepth: number, recursive: boolean)`** — Runs `git submodule update --init --force`, optionally adding `--depth` for shallow clones and `--recursive` for nested submodules.
3. **`submoduleForeach(command: string, recursive: boolean)`** — Applies configuration commands (such as disabling garbage collection) across all initialized submodules.

### Submodule Detection and Cleaning

In [`src/git-directory-helper.ts`](https://github.com/actions/checkout/blob/main/src/git-directory-helper.ts), the action uses `git submodule status` (lines 84-85) to detect existing submodules when determining whether to clean the working directory. This prevents accidental deletion of submodule content during incremental checkouts, even when the `submodules` input is disabled.

## Authentication and Credentials

When `persist-credentials` is enabled, the action extends authentication to submodules after fetching. In [`src/git-source-provider.ts`](https://github.com/actions/checkout/blob/main/src/git-source-provider.ts) (lines 92-96):

```typescript
if (settings.persistCredentials) {
  core.startGroup('Persisting credentials for submodules')
  await authHelper.configureSubmoduleAuth()
  core.endGroup()
}

```

This ensures that subsequent steps in your workflow can push to submodule remotes using the same authentication tokens configured for the parent repository.

## Usage Examples

### Default Behavior (Submodules Ignored)

By default, submodules are not fetched. This is the most efficient option for repositories without submodules:

```yaml
- uses: actions/checkout@v4
  # submodules defaults to false

```

### Top-Level Submodules Only

Fetch immediate submodules without recursion:

```yaml
- uses: actions/checkout@v4
  with:
    submodules: true

```

### Recursive Submodule Checkout

Fetch all nested submodules with full history:

```yaml
- uses: actions/checkout@v4
  with:
    submodules: recursive
    fetch-depth: 0

```

### Persist Credentials for Submodules

Enable authentication persistence for submodule operations:

```yaml
- uses: actions/checkout@v4
  with:
    submodules: recursive
    persist-credentials: true

```

## Summary

- **Default behavior** (`submodules: false`) ignores all submodules to optimize checkout performance.
- **Top-level fetching** (`submodules: true`) initializes only immediate submodules using `git submodule update --init`.
- **Recursive fetching** (`submodules: recursive`) passes the `--recursive` flag to fetch unlimited nesting levels.
- Implementation spans [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts) for parsing, [`src/git-source-provider.ts`](https://github.com/actions/checkout/blob/main/src/git-source-provider.ts) for orchestration, and [`src/git-command-manager.ts`](https://github.com/actions/checkout/blob/main/src/git-command-manager.ts) for Git CLI execution.
- Authentication configuration propagates to submodules when `persist-credentials` is enabled.

## Frequently Asked Questions

### What is the default behavior for submodules in actions/checkout?

By default, `actions/checkout` sets `submodules` to `false`, meaning Git submodules are completely ignored during the checkout process. Only the parent repository content is fetched, which minimizes network usage and checkout time for repositories that do not require external dependencies.

### How do I fetch nested submodules recursively?

Set the `submodules` input to `recursive` in your workflow configuration. According to the source code in [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts), this value sets both the `submodules` and `nestedSubmodules` flags to true, which triggers `git submodule update --init --recursive` through the `GitCommandManager.submoduleUpdate` method.

### Why are my submodules not updating with actions/checkout?

If submodules are not updating, verify that you have explicitly set `submodules: true` or `submodules: recursive`. The default behavior skips submodule initialization entirely. Additionally, ensure your workflow has appropriate permissions and that the `fetch-depth` parameter accommodates your submodule history requirements.

### Does actions/checkout persist credentials for submodules?

Yes, when `persist-credentials` is set to `true`, the action configures authentication for submodules via `authHelper.configureSubmoduleAuth()` in [`src/git-source-provider.ts`](https://github.com/actions/checkout/blob/main/src/git-source-provider.ts). This allows subsequent workflow steps to interact with submodule remotes using the same credentials established for the parent repository.