The default credentials available in GitHub Actions do not allow access to other repositories; therefore, submodules cannot be checked out. GitHub Actions uses a GitHub App installed on the repository to generate an installation access token, which expires when a job completes or after 24 hours, whichever comes first. This token is a secret variable accessible at secrets.GITHUB_TOKEN or in the GitHub context at github.token. Because the token is generated by an app that is installed on the repository, it only has access to that repository.

This is a process for using a Personal Access Token with least-privileged access to enable submodule checkouts in GitHub Actions.

Enabling Personal Access Tokens in your Organization Link to heading

This step is only necessary if your repositories belong to an organization and the organization disables personal access token creation (which is by default).

  1. Navigate to your organization’s settings page
  2. On the left navigation, under Third-party Access, select Personal access tokens and then settings.
  3. Under Fine-grained personal access tokens, select Allow access via fine-grained personal access tokens
    1. I’d recommend enabling the Require approval of fine-grained personal access tokens option
    2. I’d also recommend selecting Restrict access via personal access tokens (classic) since the access control on classic PATs is too coarse

Organization PAT Settings

Creating a Personal Access Token Link to heading

  1. Navigate to your user settings
  2. At the bottom of the left navigation, select Developer settings, then, under Personal access tokens, select Fine-grained tokens
  3. Click the Generate new token button.
    1. The token names cannot include spaces like the classic PATs could. Instead, use the description field and keep the name concise
    2. Set the expiration date to whatever you’d like, but the maximum allowable life is one year
    3. Under Resource owner, select your organization. If you don’t see your organization, see the previous section
    4. Under Repository Access, select Only select repositories
    5. Select the repository under which GitHub Actions will run, plus every repository used as a submodule
    6. Under Repository permissions, select Read-only access for both Contents and Metadata (default)
  4. Click Generate token and copy the generated token

This will ensure that only the minimum access necessary to checkout the repositories is enabled.

Add the Personal Access Token to GitHub Actions Link to heading

  1. Navigate to your repository settings
  2. In the left hand navigation, under Security, select Secrets and variables, then Actions
  3. Create a new Repository secret, perhaps called PAT_TOKEN and paste the token from the previous section as the secret value

Using the Token in GitHub Actions Link to heading

Specify the token variable from the previous step in your checkout settings:

jobs:
  example:
    name: Example Job
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          submodules: 'recursive'
          token: ${{ secrets.PAT_TOKEN }} # otherwise defaults to ${{ github.token }}

That’s it!

Security Considerations Link to heading

The personal access token created here is treated like a secret in the same way as the default installation access token generated by the GitHub Actions app. However, the installation access token expires as soon as the job completes, or after 24 hours. The personal access token, on the other hand, is valid until its expiration date, which is at most one year. As a long-lived credential, there is some risk of misuse if the credential can be leveraged outside of its intended context. That being said, secrets in GitHub Actions do have some safeguards. For example, they are redacted from any logs (so long as they aren’t structured JSON, or otherwise larger than 48 kB). If you want to retain short-lived credentials, consider creating a GitHub App to generate installation access tokens instead.