Skip to content
Latchkey

Why a GitHub App Token Beats a Personal Access Token in CI

A GitHub App installation token is scoped to chosen repos, expires in an hour, and belongs to the org, not a person.

A personal access token inherits a user's access and lives for months. A GitHub App installation token is minted per run, scoped to the repos the App is installed on, and revoked automatically when it expires, which shrinks the blast radius if it leaks.

How they differ

PropertyUser PATApp installation token
OwnerA personThe App / org
LifetimeDays to no expiry~1 hour, auto-revoked
ScopeAll the user can seeOnly installed repos + granted permissions
Rate limit5,000 req/hour (user)Scales with installation size
Offboarding riskBreaks when user leavesUnaffected by staff changes

Mint one in a workflow

.github/workflows/ci.yml
permissions:
  contents: read
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/create-github-app-token@v1
        id: app-token
        with:
          app-id: ${{ vars.APP_ID }}
          private-key: ${{ secrets.APP_PRIVATE_KEY }}
      - uses: actions/checkout@v4
        with:
          token: ${{ steps.app-token.outputs.token }}

When a PAT is still fine

  • A throwaway one-off script where setting up an App is overkill.
  • A fine-grained PAT scoped to a single repo with a short expiry is a reasonable middle ground.
  • Prefer short-lived scoped tokens over long-lived PATs whenever the token touches other repos.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →