# Submodule Checkout Modes in actions/checkout: A Complete Guide

> Explore the three submodule checkout modes in actions/checkout: false, true, and recursive. Learn how to manage your submodules effectively with this complete guide.

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

---

**The `actions/checkout` GitHub Action supports three distinct submodule checkout behaviors—**`false`** (default), **`true`** (non-recursive), and **`recursive`**—controlled via the `submodules` input parameter.**

When you use the official `actions/checkout` action to clone a repository containing Git submodules, you can configure exactly how those nested repositories are fetched and initialized. Understanding these modes ensures your CI/CD workflows handle complex repository structures correctly without unnecessary overhead.

## The Three Submodule Checkout Modes

According to the `actions/checkout` source code, the `submodules` input accepts three distinct states that determine whether Git runs `git submodule update` and with which flags.

### No Submodule Checkout (Default)

When you omit the `submodules` input or provide any value other than `true` or `recursive`, the action skips submodule initialization entirely. This is the fastest option and suitable for repositories that do not contain submodules or where you explicitly want to ignore them.

The action leaves both `result.submodules` and `result.nestedSubmodules` flags as `false` in the internal settings object.

### Non-Recursive Submodule Checkout

Setting `submodules: true` enables **non-recursive checkout**. In this mode, the action executes:

```bash
git submodule update --init --force

```

This command initializes and fetches only **top-level submodules** directly referenced in your main repository. Any submodules nested inside those submodules remain ignored. This mode strikes a balance between functionality and performance when you need immediate dependencies but not the entire dependency tree.

### Recursive Submodule Checkout

Setting `submodules: recursive` enables full **recursive checkout**. The action executes:

```bash
git submodule update --init --recursive --force

```

This fetches **all submodules**, including nested sub-submodules, preserving the complete tree structure defined in your `.gitmodules` files. Use this mode for complex projects with deep dependency hierarchies, though be aware it increases clone time and network usage proportionally to the submodule depth.

## Implementation in Source Code

The mode selection logic resides in [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts). The action reads the `submodules` input, converts it to uppercase, and sets boolean flags accordingly:

- If the value equals `"RECURSIVE"`, both `result.submodules` and `result.nestedSubmodules` become `true`
- If the value equals `"TRUE"`, only `result.submodules` becomes `true`
- Any other value leaves both flags `false`

Later, [`src/git-source-provider.ts`](https://github.com/actions/checkout/blob/main/src/git-source-provider.ts) consumes these flags to conditionally execute Git commands:

```typescript
if (settings.submodules) {
  core.startGroup('Fetching submodules')
  await git.submoduleSync(settings.nestedSubmodules)
  await git.submoduleUpdate(settings.fetchDepth, settings.nestedSubmodules)
  // …
}

```

This implementation ensures that the `nestedSubmodules` boolean directly controls whether the `--recursive` flag is appended to Git commands.

## Configuration Examples

### Default Behavior (No Submodules)

```yaml
steps:
  - uses: actions/checkout@v4
    # submodules input omitted — nested repositories are ignored

```

### Non-Recursive Checkout

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

```

### Recursive Checkout

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

```

## Summary

- **Default mode**: Submodules are ignored entirely for maximum speed and simplicity.
- **`true` mode**: Fetches only top-level submodules using `git submodule update --init --force`.
- **`recursive` mode**: Fetches the complete submodule tree including nested dependencies using the `--recursive` flag.
- **Implementation**: [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts) parses the input into boolean flags, while [`src/git-source-provider.ts`](https://github.com/actions/checkout/blob/main/src/git-source-provider.ts) executes the appropriate Git commands based on those flags.

## Frequently Asked Questions

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

By default, `actions/checkout` does not initialize or fetch any submodules. If you omit the `submodules` input or set it to any value other than `true` or `recursive`, the action skips all submodule-related Git commands entirely.

### Does recursive submodule checkout impact CI performance?

Yes. Recursive checkout significantly increases network transfer and disk usage because it fetches the entire submodule graph. If your workflow only requires top-level dependencies, use `submodules: true` instead of `recursive` to reduce clone time and storage consumption.

### Can I use fetch-depth with submodules?

Yes. When `fetch-depth` is specified, the action passes this parameter to the submodule update commands in [`src/git-source-provider.ts`](https://github.com/actions/checkout/blob/main/src/git-source-provider.ts). However, shallow submodule clones require Git 2.36+ for reliable operation with nested submodules.

### How do I troubleshoot submodule fetch failures?

Check that your workflow has explicit token permissions to access submodule repositories, especially when submodule URLs point to private repos. The action uses the same credentials for submodules as the main repository, so ensure your `persist-credentials` setting and `token` input grant appropriate access to all nested repositories.