How to Checkout Submodules with actions/checkout: Configuration Guide and Examples

Set the submodules input to true for top-level submodules or recursive for nested submodules when using the actions/checkout GitHub Action to automatically fetch Git submodules during your workflow.

The actions/checkout repository provides a robust solution for cloning repositories in GitHub Actions workflows, including comprehensive support for Git submodules. When you need to checkout submodules with actions/checkout, the action handles the complex Git command orchestration internally while exposing a simple configuration interface. This implementation ensures that both public and private submodules are fetched using the appropriate authentication credentials.

How the Submodules Input Is Parsed and Executed

According to the source code in src/input-helper.ts, the action parses the submodules input to determine the checkout behavior. When you set submodules: true, the flag result.submodules becomes true; when you set submodules: recursive, the additional flag result.nestedSubmodules is also set to true.

During the checkout process, src/git-source-provider.ts orchestrates the submodule synchronization. The action executes git submodule sync to ensure submodule URLs match the parent repository's authentication settings. It then runs git submodule update --init --force to clone the submodules and checkout the correct commits.

If nestedSubmodules is enabled, the action passes the --recursive flag to fetch sub-submodules. The low-level Git operations are implemented in src/git-command-manager.ts, which provides the submoduleSync, submoduleUpdate, and submoduleForeach helper methods.

Additionally, src/git-auth-helper.ts updates the Git credential helper for each submodule. This ensures that the same token or ssh-key used for the main repository is available when fetching private submodules.

Submodule Checkout Configuration Options

The submodules input accepts three distinct values that control submodule behavior:

  • true – Checkout top-level submodules only, ignoring any nested sub-submodules.
  • recursive – Checkout all submodules recursively, including nested sub-submodules.
  • (empty or omitted) – Submodules are ignored; only the main repository is checked out.

Practical Code Examples for Submodule Checkout

Basic Submodule Checkout (Top-Level Only)

Use this configuration when your repository contains only top-level submodules and you do not need nested dependencies.

- uses: actions/checkout@v7
  with:
    submodules: true          # fetch only top-level submodules

    token: ${{ secrets.GITHUB_TOKEN }}

Recursive Checkout for Nested Submodules

When your project contains nested submodules, specify recursive to ensure all levels are fetched.

- uses: actions/checkout@v7
  with:
    submodules: recursive    # fetch all submodules recursively

    token: ${{ secrets.GITHUB_TOKEN }}

Shallow Fetch with Submodules

To minimize checkout time, combine submodules with a shallow fetch depth. Note that this retrieves only the latest commit for each submodule.

- uses: actions/checkout@v7
  with:
    submodules: true
    fetch-depth: 1          # default – only the tip of each submodule

Full History for Submodules

When you need complete commit history for submodules, set fetch-depth: 0 to fetch all history for both the main repository and submodules.

- uses: actions/checkout@v7
  with:
    submodules: recursive
    fetch-depth: 0          # fetch all history for the main repo and submodules

Sparse Checkout Including Submodule Paths

Submodule fetching works with sparse-checkout, but you must explicitly include the submodule directory paths in your pattern.

- uses: actions/checkout@v7
  with:
    sparse-checkout: |
      src/
      lib/submodule/    # ensure the submodule path is listed

    submodules: true

Private Submodules with SSH Authentication

For private submodules, use an SSH key to ensure authentication propagates to submodule repositories.

- uses: actions/checkout@v7
  with:
    submodules: recursive
    ssh-key: ${{ secrets.SUBMODULE_SSH_KEY }}
    ssh-known-hosts: ${{ secrets.SSH_KNOWN_HOSTS }}

Authentication Behavior for Submodules

Submodules inherit the same authentication credentials as the main repository. The src/git-auth-helper.ts file handles credential propagation, ensuring that the token or ssh-key provided in the workflow step is used when fetching private submodules. You do not need to configure separate authentication for submodules unless they reside on different hosts requiring different credentials.

Summary

  • Set submodules: true to checkout top-level submodules, or submodules: recursive to include nested submodules.
  • The action executes git submodule sync and git submodule update --init --force via src/git-source-provider.ts and src/git-command-manager.ts.
  • Authentication automatically propagates to submodules through src/git-auth-helper.ts.
  • Combine submodules with fetch-depth to control history depth, or with sparse-checkout to limit the working tree.
  • Use ssh-key instead of token when accessing private submodules via SSH.

Frequently Asked Questions

Why are my nested submodules not being checked out?

If nested submodules are missing, you likely set submodules: true instead of submodules: recursive. The true value only fetches top-level submodules. According to the implementation in src/input-helper.ts, you must explicitly set the input to recursive to enable the nestedSubmodules flag, which adds the --recursive flag to Git commands in src/git-source-provider.ts.

How do I authenticate private submodules in actions/checkout?

Private submodules automatically use the same authentication method as the main repository. If you use a token, it propagates to submodules via the credential helper configured in src/git-auth-helper.ts. For SSH-based authentication, provide the ssh-key and optionally ssh-known-hosts inputs, and the action will use this key for all submodule URLs.

Can I use sparse-checkout with submodules?

Yes, sparse-checkout is compatible with submodules, but you must include the submodule directory paths in your sparse-checkout pattern. The submodule contents are fetched regardless of sparse-checkout settings, but the working tree will only populate the paths you specify.

Does fetching submodules work with shallow clones?

Yes, the fetch-depth setting applies to submodules when submodules is enabled. By default, the action performs a shallow fetch (fetch-depth: 1) for both the main repository and submodules. Set fetch-depth: 0 to retrieve complete history for all repositories.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →