Back to blog
DevOpsHomelabSeguridad

Buildkite checkout without persistent GitHub credentials

A practical way to clone private repositories in CI without placing PATs or SSH keys across a homelab.

By Isma4 min read

The secret that stays on the runner

In a homelab, the quickest way to access private GitHub repositories is usually a PAT or an SSH key placed on a VM, container, or pod. That works until credentials need rotating, an image is rebuilt, or nobody knows which runners still have access to which repositories. The issue is not Git itself; it is that the secret often outlives the CI job that actually needs it.

Buildkite can now provide self-hosted agents with temporary GitHub App credentials for checkout. The token is requested for the running job and has read-only access to the contents of one repository. That means a Laravel runner does not need a PAT in its environment, a private key under ~/.ssh, or a locally installed GitHub App private key. (buildkite.com)

What I would change for a Laravel queue

This feature is enabled per queue rather than across every agent in an organization. That makes it reasonable to create a dedicated queue such as laravel-ci, test a pipeline there, and leave the remaining runners untouched while reviewing hooks, submodules, and helper scripts. The baseline requirements are Buildkite Agent 3.63.1 or newer and the full-access GitHub App integration with access to the repositories being cloned. (buildkite.com)

For GitHub.com, a pipeline can keep using an SSH repository URL because Buildkite changes the checkout to HTTPS. GitHub Enterprise Server is different: its repository URL must already use HTTPS. That is a small distinction, but it is worth checking before removing SSH keys from runners. (buildkite.com)

Inventory first, then make the switch

Before enabling the setting, I would capture the Git configuration for the user that runs the agent on every reused environment. The agent adds a credential helper to global Git configuration and creates a rule that rewrites GitHub.com SSH URLs to HTTPS. Those changes remain after a job ends, so they belong in both the migration plan and the rollback plan. (buildkite.com)

git config --global --get-all credential.helper
git config --global --get-all credential.useHttpPath
git config --global --get-all url.https://github.com/.insteadOf

If the agent runs in a Proxmox VM, a persistent Docker container, or a Kubernetes node with a reused home directory, I would also document any custom checkout hook or credential helper. Enabling the setting and deleting an SSH key is not enough. You need to know which existing settings are being replaced and how to restore them if the first build fails.

Keeping it in infrastructure code

The setting can also be managed through the REST API, GraphQL, or Terraform. In REST, the field is use_github_app_token; in GraphQL, it is useGithubAppToken; and Terraform uses use_github_app_token = true in the buildkite_cluster_queue resource. The Terraform provider needs to be version 1.43.0 or later. (buildkite.com)

resource "buildkite_cluster_queue" "laravel_ci" {
  # Remaining queue configuration
  use_github_app_token = true
}

The practical benefit is avoiding manual exceptions. A queue definition can live alongside the rest of the infrastructure, while a new runner no longer needs a repository credential during provisioning. Repository access stops depending on someone remembering to copy a secret into a base image.

The details that still matter

This solves checkout authentication; it does not turn a job into a general-purpose authenticated GitHub client. The token has contents:read permission, so it cannot push commits and should not be treated as an API token for Laravel scripts, deployment tasks, or automations. Any step that creates releases, pushes tags, or comments on a pull request still needs a separate authentication method with permissions scoped to that task. (buildkite.com)

Tokens expire one hour after GitHub issues them, and Buildkite may reuse an encrypted token for 50 minutes. Completing a job does not immediately revoke an already issued token, so it remains a secret that must never appear in logs, remote URLs, or artifacts. A shorter lifetime reduces exposure, but it does not remove the need to handle credentials carefully. (buildkite.com)

There are also limits on cloning additional repositories. They must belong to the same GitHub account as the pipeline repository, the GitHub App installation must be allowed to access them, and extra clones and submodules must use HTTPS. If the managed helper cannot obtain a token, it does not silently fall back to an older SSH key. (buildkite.com)

Finally, a different queue does not isolate two agents that share the same user and home directory. For workloads with incompatible credential requirements, I would use separate system users or genuinely isolated environments. That is the difference between enabling a CI option and carrying out a credential migration that remains understandable months later. (buildkite.com)


Buildkite source

Official self-hosted agent code access documentation

Newsletter

What I learn coding with AI, every two weeks in your inbox.

Real numbers, mistakes included. No spam, unsubscribe in one click.

No spam, unsubscribe in one click. I only use your email to send you this and the newsletter. More in the privacy policy.