GitHub Actions Tutorial — GITHUB_TOKEN
GITHUB_TOKEN
Every workflow run automatically receives a GITHUB_TOKEN — a short-lived token that authenticates with the GitHub API. You don’t need to create or store it; GitHub manages it for you.
How It Works
Workflow starts
↓
GitHub generates GITHUB_TOKEN
↓
Token scoped to the repository
↓
Available as: secrets.GITHUB_TOKEN
↓
Workflow ends → token expires
Accessing the Token
steps:
- name: Use token
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh api repos/${{ github.repository }}
Or pass it to an action:
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
Default Permissions
The token’s default permissions depend on your repository settings (Settings → Actions → General → Workflow permissions):
| Setting | Read | Write |
|---|---|---|
| Read and write (default for older repos) | ✅ All | ✅ All |
| Read-only (recommended) | ✅ Contents, metadata | ❌ |
Best practice: Set default to read-only and grant specific permissions per workflow.
Setting Permissions in the Workflow
permissions:
contents: read # read repo contents
pull-requests: write # comment on PRs
issues: write # create/edit issues
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Comment on PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: '✅ Build passed!'
})
Available Permission Scopes
| Scope | Read | Write |
|---|---|---|
contents |
Clone, read files | Push commits, create releases |
pull-requests |
Read PRs | Comment, review, merge |
issues |
Read issues | Create, comment, label |
packages |
Download packages | Publish packages |
deployments |
Read deployments | Create deployments |
statuses |
Read commit statuses | Create commit statuses |
actions |
Read workflow runs | Cancel/re-run workflows |
id-token |
— | Request OIDC token |
Permission at Job Level
jobs:
read-job:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
write-job:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- run: echo "This job can push"
GITHUB_TOKEN Limitations
| Limitation | Alternative |
|---|---|
| Can’t trigger other workflows | Use a Personal Access Token (PAT) |
| Can’t access other repos | Use a PAT or GitHub App token |
| Can’t push to protected branches | Use a PAT with admin scope |
| Expires when workflow ends | Use PAT for long-lived needs |
Why It Can’t Trigger Workflows
To prevent infinite loops, pushes made with GITHUB_TOKEN don’t trigger new workflow runs:
steps:
# This push won't trigger another workflow
- run: |
git commit --allow-empty -m "trigger"
git push
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
To trigger another workflow, use a PAT or GitHub App token.
Common Mistakes
| Mistake | Fix |
|---|---|
| 403 Forbidden when using token | Add required permissions: to the workflow |
| Push doesn’t trigger next workflow | Use a PAT instead of GITHUB_TOKEN |
| Token doesn’t work for other repos | GITHUB_TOKEN is scoped to current repo only |
| Token expired | GITHUB_TOKEN is valid for the workflow run duration only |
Quick Reference
| Concept | Detail |
|---|---|
| Access | ${{ secrets.GITHUB_TOKEN }} or ${{ github.token }} |
| Lifetime | Duration of the workflow run |
| Scope | Current repository only |
| Default | Set in repo Settings → Actions |
| Override | permissions: in workflow YAML |
| Can’t do | Trigger workflows, access other repos |
← Previous: Secrets
Next: Environments →