Git & CI/CD Advanced

GitHub Actions checkout action repository authentication failed on Debian 12 Bookworm

Resolve GitHub Actions 'authentication failed' errors on Debian 12 self-hosted runners, commonly due to insufficient PAT scopes or SSH key misconfigurations. Optimize your CI/CD workflows.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

Resolve GitHub Actions 'authentication failed' errors on Debian 12 self-hosted runners, commonly due to insufficient PAT scopes or SSH key misconfigurations. Optimize your CI/CD workflows.

GitHub Actions checkout Action Repository Authentication Failed on Debian 12 Bookworm

When operating GitHub Actions with self-hosted runners on Debian 12 Bookworm, encountering repository authentication failures during the actions/checkout step can halt your CI/CD pipelines. This issue typically manifests when the runner attempts to clone a private repository, fetch submodules from another private repository, or perform actions requiring elevated Git permissions, and the underlying authentication mechanism (either SSH or a Personal Access Token) is misconfigured or lacks the necessary scope.

This guide provides a highly technical, step-by-step approach to diagnose and resolve these authentication errors, ensuring your Debian 12 self-hosted runners can seamlessly interact with your GitHub repositories.

Symptom & Error Signature

The primary symptom is a failed GitHub Actions workflow run, with the actions/checkout step reporting an authentication error. You will typically see messages similar to these in your workflow logs:

Run actions/checkout@v4
  with:
    repository: your-org/your-repo
    ref: main
    token: ***
    submodules: false
    clean: true
    fetch-depth: 1
  env:
    ...
Initialized empty Git repository in /home/actions-runner/_work/your-repo/your-repo/.git/
fatal: authentication failed for 'https://github.com/your-org/another-private-repo/'
Error: The process '/usr/bin/git' failed with exit code 128

Or, if using SSH for cloning:

Run actions/checkout@v4
  with:
    repository: your-org/your-repo
    ref: main
    token: ***
    submodules: false
    clean: true
    fetch-depth: 1
  env:
    ...
Initialized empty Git repository in /home/actions-runner/_work/your-repo/your-repo/.git/
Host key verification failed.
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
Error: The process '/usr/bin/git' failed with exit code 128

You might also see variations like: remote: Support for password authentication was removed ERROR: Repository not found. The SSH key you are using does not have write access.

Root Cause Analysis

The "authentication failed" error during a checkout action on a Debian 12 self-hosted runner stems from several common underlying issues:

  1. Insufficient GITHUB_TOKEN Permissions: The default GITHUB_TOKEN provided by GitHub Actions has limited permissions. It is scoped only to the repository where the workflow is running and typically has read-only access for most events. If your workflow needs to:

    • Clone a different private repository.
    • Fetch submodules from a different private repository.
    • Perform write operations (e.g., push changes) to the current repository.
    • The default GITHUB_TOKEN will be insufficient.
  2. Incorrect Personal Access Token (PAT) Configuration:

    • Missing or Invalid PAT: If you intend to use a PAT, it might not be correctly provided to the actions/checkout action.
    • Insufficient PAT Scopes: The PAT generated might lack the necessary permissions (e.g., repo scope for private repository access, read:packages for package registries).
    • Expired or Revoked PAT: PATs have expiry dates and can be revoked. An expired or revoked PAT will cause authentication to fail.
  3. SSH Key Misconfiguration on Self-Hosted Runner: If your runner is configured to use SSH for Git operations (e.g., [email protected]:your-org/your-repo.git), common issues include:

    • Missing Private Key: The SSH private key required to authenticate with GitHub is not present on the runner's filesystem for the user running the actions-runner service.
    • Incorrect Key Permissions: Private keys must have strict file permissions (e.g., 0600). Incorrect permissions will cause SSH to reject the key.
    • Key Not Loaded in ssh-agent: The ssh-agent might not be running or the private key might not be added to it, preventing automatic authentication.
    • known_hosts Issues: The runner might not have github.com added to its ~/.ssh/known_hosts file, leading to "Host key verification failed" errors.
    • Incorrect GIT_SSH_COMMAND: If a custom SSH command is used, it might be misconfigured.
  4. Network or Firewall Restrictions: While less common to manifest as an "authentication failed" message (usually timeouts or connection refused), a firewall blocking outbound SSH (port 22) or HTTPS (port 443) traffic to github.com could indirectly prevent successful Git operations.

Step-by-Step Resolution

This section details the necessary steps to diagnose and resolve repository authentication issues on your Debian 12 self-hosted runner.

1. Analyze Runner Logs for Specific Clues

Before making changes, thoroughly inspect the full workflow run logs, especially the output from the actions/checkout step. Look for specific Git or SSH error messages, as they often pinpoint the exact cause. If the runner is managed by systemd, you can also check its logs:

sudo journalctl -u actions.runner.your-org.your-runner-name --no-pager

This will show output from the runner service itself, which might contain more granular errors if the issue occurs before the actions/checkout action even starts properly.

2. Ensure Correct actions/checkout Configuration for Submodules

If your repository uses submodules, especially private ones, you must configure actions/checkout to handle them. The default GITHUB_TOKEN cannot authenticate against submodules in different private repositories.

- name: Checkout repository and submodules
  uses: actions/checkout@v4
  with:
    repository: your-org/your-repo
    # ... other checkout options
    submodules: true # Or 'recursive' for nested submodules
    # Ensure to provide a PAT if submodules are in different private repos
    token: ${{ secrets.YOUR_PAT_WITH_REPO_SCOPE }}

When submodules: true is used, actions/checkout attempts to clone submodules. If these submodules are in different private repositories, the default GITHUB_TOKEN will fail. You must provide a Personal Access Token (PAT) with appropriate repo scope to the token parameter.

3. Using a Personal Access Token (PAT) for HTTPS-based Authentication

This is the most common and robust solution for cross-repository or elevated-permission scenarios.

a. Generate a GitHub Personal Access Token (PAT)
  1. Navigate to your GitHub profile settings: Settings > Developer settings > Personal access tokens > Tokens (classic).
  2. Click Generate new token and select Generate new token (classic).
  3. Provide a clear Note (e.g., "CI/CD Token for Debian 12 Runner").
  4. Set Expiration: Choose an appropriate expiration (e.g., 90 days, 1 year, or custom). Avoid "No expiration" for security reasons.
  5. Select Scopes:
    • For cloning private repositories (including the main repo or submodules): Check repo (all sub-scopes) or at least repo:status, repo:public_repo, repo:invite, repo:webhook. repo scope generally simplifies things but grants broad access.
    • If interacting with packages: read:packages, write:packages.
    • If pushing changes back: workflow (for workflow_dispatch).
  6. Click Generate token.
  7. Copy the token immediately. You will not be able to see it again.

A PAT with repo scope grants significant control over your repositories. Treat it like a password. Ensure it has the minimum necessary scopes and an expiration date. Do not hardcode it directly in your workflow files.

b. Add the PAT as a GitHub Secret
  1. In your GitHub repository (or organization) where the workflow runs, go to Settings > Secrets and variables > Actions > Repository secrets (or Organization secrets).
  2. Click New repository secret.
  3. Name it (e.g., CI_CD_PAT).
  4. Paste your copied PAT into the Value field.
  5. Click Add secret.
c. Configure actions/checkout to Use the PAT

Modify your workflow .yml file to use the newly created secret:

- name: Checkout repository with PAT
  uses: actions/checkout@v4
  with:
    repository: your-org/your-repo
    # For cloning a specific branch
    ref: ${{ github.ref }} 
    # Use the secret for authentication
    token: ${{ secrets.CI_CD_PAT }} 
    # If you have submodules in other private repos, enable and ensure PAT has repo scope
    submodules: true 
    fetch-depth: 0 # Important for some Git operations, e.g., git describe or full history

4. Troubleshooting SSH-based Authentication for Self-Hosted Runners

If your workflow or actions/checkout is configured to use SSH, or if you're explicitly using SSH URLs ([email protected]:...), follow these steps on your Debian 12 self-hosted runner.

a. Verify SSH Key Presence and Permissions

The private key (id_rsa, id_ed25519, etc.) must be present in the .ssh directory of the user running the actions-runner service. Assuming the runner user is actions-runner:

# Switch to the user running the runner service
sudo -u actions-runner bash

# Check for SSH directory and private key
ls -l ~/.ssh/id_rsa
# Expected: -rw------- 1 actions-runner actions-runner 2602 Aug 10 10:00 /home/actions-runner/.ssh/id_rsa

# Verify permissions
chmod 600 ~/.ssh/id_rsa
chmod 700 ~/.ssh

# Exit the runner user shell
exit

SSH private keys must have 0600 permissions. Any less restrictive permission (e.g., 0644) will cause SSH to reject the key with a "Permissions are too open" error.

b. Ensure ssh-agent is Running and Key is Loaded

The actions-runner service needs access to ssh-agent to use the keys automatically.

  1. Check if ssh-agent is running for the runner user:

    sudo -u actions-runner bash -c "ssh-add -l"
    

    If it returns "Could not open a connection to your authentication agent.", ssh-agent is not running or not configured for the user.

  2. Configure ssh-agent on startup for the runner user: Modify the systemd service file for your runner. Edit /etc/systemd/system/actions.runner.your-org.your-runner-name.service (replace with your actual service name). Add Environment variables to source the ssh-agent setup script.

    --- a/etc/systemd/system/actions.runner.your-org.your-runner-name.service
    +++ b/etc/systemd/system/actions.runner.your-org.your-runner-name.service
    @@ -10,6 +10,10 @@
     ExecStart=/home/actions-runner/start.sh
     KillMode=process
     WorkingDirectory=/home/actions-runner
    +
    +# Ensure SSH Agent is loaded for the runner user
    +Environment="SSH_AUTH_SOCK=/tmp/ssh-agent-${USER}/agent.$PPID"
    +Environment="SSH_AGENT_PID=$PPID"
     # If you want to load a specific key on service start, it's more complex.
     # Consider running ssh-add as part of the start.sh or relying on `actions/checkout`'s `ssh-key` input.
     # For most cases, having ssh-agent running for the interactive user then stopping it for the service might be easier.
    

    Alternatively, and often simpler, you can use the actions/checkout ssh-key input:

    - name: Checkout repository using SSH key
      uses: actions/checkout@v4
      with:
        repository: your-org/your-repo
        ssh-key: ${{ secrets.SSH_PRIVATE_KEY }} # Store private key as a GitHub secret
        # Optionally, specify the host keys if not relying on known_hosts
        # ssh-known-hosts: 'github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOmcKHM0Jc+c1QnN...etc'
    

    Storing an SSH private key as a GitHub secret (secrets.SSH_PRIVATE_KEY) is generally more secure than distributing it directly to many self-hosted runner machines. The actions/checkout action will handle loading it into a temporary ssh-agent for the duration of the step.

c. Add GitHub to known_hosts

If ssh-agent is working but you get "Host key verification failed", the host key for github.com is missing from ~/.ssh/known_hosts for the runner user.

# Switch to the user running the runner service
sudo -u actions-runner bash

# Add GitHub's public key to known_hosts
ssh-keyscan github.com >> ~/.ssh/known_hosts

# Verify the entry
grep github.com ~/.ssh/known_hosts

# Exit the runner user shell
exit

5. Verify Network Connectivity

Ensure your Debian 12 runner can reach GitHub's Git servers.

  1. Test HTTPS connectivity:

    curl -I https://github.com
    

    You should see a 200 OK status.

  2. Test SSH connectivity:

    sudo -u actions-runner bash -c "ssh -T [email protected]"
    

    This should return: Hi YOUR_USERNAME! You've successfully authenticated, but GitHub does not provide shell access. If it asks for a password or fails with "Connection refused", check firewall rules.

6. Restart the Runner Service

After applying any configuration changes to the runner's environment (e.g., SSH keys, systemd unit files), always restart the actions-runner service for changes to take effect:

sudo systemctl daemon-reload
sudo systemctl restart actions.runner.your-org.your-runner-name.service

By systematically working through these steps, you should be able to identify and resolve the authentication issues preventing your GitHub Actions checkout action from successfully cloning repositories on your Debian 12 self-hosted runners.

👨‍💻

Johnathon Wheeler

Senior Systems Architect & DevOps Engineer • Austin, TX

Connect on LinkedIn →

Johnathon has over 16 years of hands-on experience designing, debugging, and scaling Linux web hosting stacks, container clusters, and high-availability database architectures. Every guide on ButItWorkedLocal is independently tested against Debian 12, Ubuntu 24.04/22.04 LTS, Rocky Linux, and Docker environments to guarantee reproducibility in production.

🛡️

Our Production Verification Guarantee

Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.