Why GitHub Actions Workflows Don't Re-trigger — GITHUB_TOKEN, PAT, and GitHub Apps

2026-03-03 hit count image

Learn why pushing with GITHUB_TOKEN in GitHub Actions doesn't trigger other workflows, and compare PAT vs GitHub Apps solutions with a step-by-step GitHub Apps setup guide.

github_actions

The Problem

In a monorepo based on Yarn Workspaces, Dependabot only updates package.json without regenerating yarn.lock. To solve this, we had a workflow that automatically updates the yarn.lock file on Dependabot PRs. This workflow updates the lockfile and pushes a commit, but after the push, CI workflows (lint, build, test, etc.) were not re-triggered.

For more details on running Dependabot in a monorepo, see the previous posts.

The core part of the problematic workflow looked like this:

steps:
  - name: Checkout repository
    uses: actions/checkout@v4
    with:
      ref: ${{ github.head_ref }}
      token: ${{ secrets.GITHUB_TOKEN }} # Pushing with this token won't trigger other workflows

  # ... update yarn.lock ...

  - name: Commit updated lockfile
    run: |
      git add yarn.lock
      git diff --staged --quiet || git commit -m "chore: update yarn.lock"
      git push  # This push doesn't trigger CI

Root Cause

When you push using the default GITHUB_TOKEN provided by GitHub Actions, other workflows will not be triggered. This is an intentional design by GitHub to prevent infinite recursive execution between workflows.

When you use the repository’s GITHUB_TOKEN to perform tasks, events triggered by the GITHUB_TOKEN will not create a new workflow run.

GitHub Docs: Triggering a workflow from a workflow

In other words, commits pushed with GITHUB_TOKEN do not generate push events or the synchronize type of pull_request events, so CI workflows are not executed.

Comparing Solutions

To solve this problem, you need to use a different authentication method instead of GITHUB_TOKEN. The two main options are PAT (Personal Access Token) and GitHub Apps.

ItemPATGitHub Apps
OwnerIndividualOrganization
Permission ScopeTied to user’s access rightsOnly permissions set on the app
Token LifetimeManual (up to 1 year)Auto-issued, expires in 1 hour
Offboarding RiskYesNone
Audit TrailPersonal account-basedShown as dedicated bot account
Rate LimitShared with personal accountApp-dedicated (higher limits)

Option 1: PAT (Personal Access Token)

This is the quickest method to apply.

Setup

PATs can be created and configured as follows:

  1. GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens
  2. Click Generate new token
  3. Configure:
    • Resource owner: Select your Organization
    • Repository access: Select only the target repository
    • Permissions:
      • Contents: Read and write
      • Pull requests: Read and write (if needed)
  4. Register the generated token in Repository Settings → Secrets → Actions

Workflow Update

Pass the PAT to actions/checkout, and other workflows will be triggered normally on push.

- name: Checkout repository
  uses: actions/checkout@v4
  with:
    ref: ${{ github.head_ref }}
    token: ${{ secrets.PAT_TOKEN }} # Changed to PAT

Drawbacks of PATs

While PATs can quickly resolve the issue, they carry risks for long-term use in production:

ItemDescription
Personal account dependencyCI breaks if the token owner leaves or the account is deactivated
Broad permission scopeEven with fine-grained tokens, permissions are tied to the individual
Manual renewal requiredTokens with expiration dates need periodic renewal
Audit trailCommits are tracked under the personal account

This method involves creating an Organization-owned GitHub App to issue tokens. It addresses all the drawbacks of PATs and is the recommended approach for production environments.

GitHub Apps Setup Guide

Here is the step-by-step process for setting up GitHub Apps.

Step 1: Create a GitHub App

Creating an Organization’s GitHub App requires Organization Owner permissions. If you have the required permissions, follow these steps:

  1. Navigate to your Organization page on GitHub
  2. SettingsDeveloper settingsGitHub AppsNew GitHub App

Input fields:

FieldValueDescription
GitHub App namemy-ci-botUnique name within the Organization
Homepage URLRepository URLRequired, but the repo URL suffices for internal use
WebhookUncheck ActiveNot needed since this is for CI token issuance

Permissions (Principle of Least Privilege)

The minimum permissions required to solve this problem are as follows. All other permissions are unnecessary, so only grant what is needed:

Repository permissions:

PermissionAccessPurpose
ContentsRead and writePush code
MetadataRead-onlyAutomatically selected
Pull requestsRead and writeIf PR-related operations needed

Organization permissions / Account permissions: None required

Installation Scope

Since this problem occurs in a specific repository, restrict the GitHub App’s installation scope to Only on this account. This prevents the App from being installed on other repositories.

  • Where can this GitHub App be installed?Only on this account

Click Create GitHub App to complete the creation.

Step 2: Generate a Private Key

To use a GitHub App, you need an App ID and a Private Key. The App ID can be found at the top of the App settings page, and the Private Key can be generated as follows:

  1. Note the App ID on the App settings page (displayed at the top)
  2. Scroll to the Private keys section at the bottom → Click Generate a private key
  3. A .pem file will be automatically downloaded

These two values (App ID and Private Key) will be used to issue tokens in the workflow.

Step 3: Install the App on the Repository

The created GitHub App must be installed on the repository where it will be used. Follow these steps:

  1. App settings page left menu → Install App
  2. Select the Organization → Install
  3. Only select repositories → Select the target repository
  4. Click Install

Step 4: Register Secrets

Register the App ID and Private Key as Secrets so they can be used in GitHub Actions.

Repository Settings → Secrets and variables — register the same Secrets in both Actions and Dependabot.

  • Actions: Settings → Secrets and variables → ActionsNew repository secret
  • Dependabot: Settings → Secrets and variables → DependabotNew repository secret
Secret NameValue
MY_CI_BOT_APP_IDApp ID from Step 2
MY_CI_BOT_PRIVATE_KEYFull contents of the .pem file
Note

Why register in both places?

GitHub Actions references different secret stores depending on what triggered the workflow. Workflows triggered by Dependabot run in a restricted security context for supply chain attack prevention and can only access Dependabot secrets. Meanwhile, workflows triggered by regular events (manual push, standard PRs, workflow_dispatch, etc.) reference Actions secrets. Therefore, to ensure the same workflow works correctly regardless of what triggered it, you need to register secrets in both locations.

GitHub Docs: Using secrets with Dependabot

The .pem file content looks like this. Copy the entire content as-is:

-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
...
-----END RSA PRIVATE KEY-----
Tip

If you use the same App across multiple repositories, register the secrets in Organization Settings → Secrets to share them.

Step 5: Update the Workflow

Use the actions/create-github-app-token action to generate a token at runtime.

steps:
  # 1. Checkout with GITHUB_TOKEN (read) — don't leave token in .git/config
  - name: Checkout repository
    uses: actions/checkout@v4
    with:
      ref: ${{ github.head_ref }}
      persist-credentials: false

  # 2. Install dependencies (no credential in .git/config at this point → safe)
  - name: Setup node
    uses: actions/setup-node@v4
    with:
      node-version-file: '.nvmrc'

  - name: Update lockfile
    run: yarn install --no-immutable

  # 3. Generate App token only right before push → minimize exposure time
  - name: Generate GitHub App token
    id: app-token
    uses: actions/create-github-app-token@v1
    with:
      app-id: ${{ secrets.MY_CI_BOT_APP_ID }}
      private-key: ${{ secrets.MY_CI_BOT_PRIVATE_KEY }}

  # 4. Set remote URL with token and push
  - name: Commit and push
    run: |
      git config user.name "my-ci-bot[bot]"
      git config user.email "<APP_ID>+my-ci-bot[bot]@users.noreply.github.com"
      git remote set-url origin "https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/${{ github.repository }}.git"
      git add .
      git diff --staged --quiet || git commit -m "chore: automated update"
      git push

The key points are:

  • actions/create-github-app-token@v1 issues an installation token using the App ID and Private Key
  • This token is valid for approximately 1 hour and is newly issued for each workflow run
  • Pushing with this token triggers other workflows normally
  • The git commit author uses the <app-name>[bot] format

Security: Minimizing Token Exposure

For security, simply replacing GITHUB_TOKEN with a GitHub App token is not enough. You also need to consider the time and scope of token exposure.

actions/checkout defaults to persist-credentials: true, which saves the provided token in .git/config’s extraheader. If you checkout with an App token and then run yarn install, a dependency’s post-install script could read .git/config and steal the token with write permissions.

Dependabot PRs update external packages, making them particularly vulnerable to this supply chain attack scenario.

[Dangerous configuration]
checkout (App token) → token saved in .git/config → yarn install → post-install script can steal the token

[Safe configuration]
checkout (persist-credentials: false) → no token in .git/config → yarn install (safe) → generate App token → push

To prevent this, the following configuration is recommended:

  1. persist-credentials: false: Don’t save credentials in .git/config during checkout
  2. Move token generation to just before push: Generate the token after running external code like yarn install
  3. Use git remote set-url to set the token only for push: Configure the token only when it’s needed

A Note on Permissions

When switching to a GitHub App token, you should also review the job’s permissions block. permissions controls the GITHUB_TOKEN permissions, so if GITHUB_TOKEN is not used for pushing in the job, there’s no need to grant contents: write.

# Before: write needed because GITHUB_TOKEN handles push
permissions:
  contents: write

# After: push is handled by GitHub App token, so read is sufficient
permissions:
  contents: read

Following the principle of least privilege, avoid granting unnecessary permissions to tokens that are not actually used.

Testing

To verify that the GitHub App token works correctly, you can create the following test workflow:

name: Test GitHub App Token

on:
  push:
    branches:
      - test/app-token # Only run on the test branch

jobs:
  test-token:
    name: Test App Token
    runs-on: ubuntu-latest
    timeout-minutes: 5
    steps:
      - name: Generate GitHub App token
        id: app-token
        uses: actions/create-github-app-token@v1
        with:
          app-id: ${{ secrets.MY_CI_BOT_APP_ID }}
          private-key: ${{ secrets.MY_CI_BOT_PRIVATE_KEY }}

      - name: Verify token was generated
        run: |
          if [ -z "${{ steps.app-token.outputs.token }}" ]; then
            echo "::error::Token generation failed"
            exit 1
          fi
          echo "Token generated successfully"

      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          token: ${{ steps.app-token.outputs.token }}

      - name: Test push (empty commit on temp branch)
        run: |
          git config user.name "my-ci-bot[bot]"
          git config user.email "<APP_ID>+my-ci-bot[bot]@users.noreply.github.com"
          git checkout -b test/app-token-verify
          git commit --allow-empty -m "test: verify GitHub App token push"
          git push origin test/app-token-verify
          echo "Push succeeded"

      - name: Cleanup temp branch
        if: always()
        run: git push origin --delete test/app-token-verify || true

Test Procedure

Add this test workflow to the test/app-token branch and push it. You can verify the setup through the following steps:

  1. Add the workflow file above to the test/app-token branch and push
  2. Check the workflow execution results in the Actions tab
  3. Verify the setup based on each step’s success or failure:
    • Generate token succeeds → Secrets are configured correctly
    • Checkout succeeds → Token can access the repo
    • Push succeeds → Token can push
  4. Delete the workflow file after testing is complete

Troubleshooting

Error MessageCauseSolution
could not create workflow dispatch event: HTTP 404App is not installed on the repositoryCheck Install App in Step 3
The permissions requested are not granted to this installationInsufficient App permissionsAdd permissions in App Settings → Permissions
Could not resolve to a valid refIncorrect Private KeyVerify the full .pem content is registered as a Secret
Resource not accessible by integrationInstallation scope issueVerify the App is installed on the target repository

Conclusion

The fact that pushing with GITHUB_TOKEN in GitHub Actions doesn’t trigger other workflows is an intentional design to prevent infinite recursion. There are two main ways to solve this:

MethodBest For
PATWhen a quick temporary fix is needed
GitHub AppsRecommended for production

GitHub Apps have a higher initial setup barrier since Organization Owner permissions are required, but once configured, they provide secure, manageable token handling that isn’t tied to any individual. Especially for workflows that deal with external packages like Dependabot PRs, it’s recommended to also consider minimizing the token exposure scope.

References

Was my blog helpful? Please leave a comment at the bottom. it will be a great help to me!

App promotion

You can use the applications that are created by this blog writer Deku.
Deku created the applications with Flutter.

If you have interested, please try to download them for free.



SHARE
Twitter Facebook RSS