How to Push Commits Back Using the Built-In GITHUB_TOKEN with actions/checkout

The actions/checkout action automatically persists the built-in GITHUB_TOKEN to the local Git configuration, enabling authenticated git push commands in subsequent steps without requiring additional secrets.

When using the actions/checkout action in GitHub Actions workflows, you can push commits back to the repository using the built-in GITHUB_TOKEN without manually configuring authentication headers or passing the token to individual commands. The action handles credential persistence automatically by storing the token provided by ${{ github.token }} in a temporary Git configuration file during the checkout process.

How Credential Persistence Works

By default, actions/checkout sets the persist-credentials input to true. According to the logic implemented in action.yml and the compiled dist/index.js (lines 42190–42200), this setting instructs the action to write the authentication token to the local Git configuration under $RUNNER_TEMP. This credential helper setup allows any Git command executed later in the same job to use the token transparently. The token is removed automatically during the post-job cleanup step to prevent credential leakage between jobs or workflow runs.

Required Workflow Permissions

To push commits using the built-in token, you must explicitly grant write permissions to the workflow job. By default, the GITHUB_TOKEN restricts repository contents to read-only access. As documented in the README.md (lines 77–84), you must add the permissions block to your workflow:

permissions:
  contents: write

Without contents: write, any attempt to run git push will fail with a 403 Forbidden error because the token lacks sufficient scope.

Configuring the Commit Author

Since the GITHUB_TOKEN does not carry a real user identity, you must manually configure Git with a bot account before committing. The standard practice, as shown in the README.md examples (lines 32–49), uses the github-actions[bot] identity:

git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

This ensures commits appear as authored by the GitHub Actions bot rather than displaying generic or empty metadata in the repository history.

Handling Detached HEAD in Pull Requests

When running on pull_request events, the checkout action defaults to checking out the merge commit, placing the repository in detached HEAD mode. To push back to the pull request branch, you must explicitly checkout the head reference using github.head_ref, as documented in README.md (lines 54–65):

- uses: actions/checkout@v4
  with:
    ref: ${{ github.head_ref }}

Omitting this configuration causes push operations to fail because HEAD is not attached to a branch reference.

Complete Working Examples

Push to Default Branch

The following workflow generates a timestamp file and pushes it back to the current branch using the built-in token:

on: push
jobs:
  update:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
      - run: |
          date > timestamp.txt
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git add timestamp.txt
          git commit -m "Update timestamp"
          git push

Push to Pull Request Branch

For workflows triggered by pull requests, checkout the head ref to enable pushing back to the source branch:

on: pull_request
jobs:
  format:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.head_ref }}
      - run: |
          echo "Formatting changes" >> report.md
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git add report.md
          git commit -m "Add formatting report"
          git push

Summary

  • The actions/checkout action automatically persists the GITHUB_TOKEN to Git configuration when persist-credentials is true (the default), storing credentials in $RUNNER_TEMP and cleaning them up post-job.
  • You must grant contents: write permissions in your workflow to enable push operations using the built-in token.
  • Configure user.name and user.email with the GitHub Actions bot identity before committing to ensure proper attribution.
  • For pull request workflows, checkout github.head_ref to avoid detached HEAD state and enable pushing back to the PR branch.
  • No additional secrets are required beyond the automatically provided GITHUB_TOKEN.

Frequently Asked Questions

Why does git push fail with authentication errors despite using actions/checkout?

Authentication failures typically occur when the workflow lacks the required permissions: contents: write configuration. By default, the built-in GITHUB_TOKEN has read-only access to repository contents. Verify your workflow grants write permissions and that persist-credentials is not explicitly set to false.

Do I need to pass the token explicitly to the checkout action?

No. The action automatically uses ${{ github.token }} (the built-in GITHUB_TOKEN) when the token input is omitted or left as the default. According to action.yml, this default value provides seamless authentication for subsequent Git operations without exposing the token in workflow definitions.

How long does the token persist in the runner?

The token remains available for the duration of the job only. The action writes credentials to a temporary location under $RUNNER_TEMP during the checkout step and automatically removes them in the post-job cleanup phase. This ensures the credential does not leak to subsequent jobs or workflow runs.

Can I use the built-in token to push to a different repository?

No. The built-in GITHUB_TOKEN is scoped only to the repository where the workflow runs. To push commits to a different repository, you must use a Personal Access Token (PAT) or GitHub App token with appropriate permissions, passed explicitly via the token input or stored as a repository secret.

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 →