GitHub token
The fleet uses one token, ACCESS_TOKEN, to mint runner registration tokens. It must be able to manage self-hosted runners for every organization in orgs.conf.
Which token type?
| Option | Works for | Required scope / permission |
|---|---|---|
| Classic PAT (recommended for multiple orgs) | Every organization you administer | admin:org |
| Fine-grained PAT | One organization per token | Organization permissions → Self-hosted runners: Read and write |
| GitHub App installation token | One organization; short-lived | Self-hosted runners: Read and write (not supported by the CLI yet) |
Fine-grained tokens are scoped to a single resource owner (you or one organization), while the fleet takes one token for all organizations. Use a classic PAT with admin:org when orgs.conf has more than one organization; to use fine-grained tokens, run one build server (one orgs.conf) per organization.
Create a classic PAT
- GitHub → your avatar → Settings → Developer settings → Personal access tokens → Tokens (classic).
- Generate new token (classic) and name it, for example
build-server runners. - Set an expiration you will remember (see Expiry and rotation).
- Select the
admin:orgscope. - Generate token and copy it now — GitHub shows it only once.
- If an organization uses SAML SSO, open the token and click Configure SSO → Authorize for each organization in
orgs.conf.
Store it on the build host:
cp .env.example .env
# edit .env
ACCESS_TOKEN=ghp_....env is gitignored; keep it 0600. bootstrap.sh asks for the token and writes this file.
Fine-grained alternative (one organization)
- Settings → Developer settings → Personal access tokens → Fine-grained tokens.
- Resource owner: the organization (it must allow fine-grained tokens).
- Repository access: none required for runner registration; set an expiration.
- Organization permissions → Self-hosted runners: Read and write.
- Generate the token and use it in
.envexactly like a classic one.
GitHub Enterprise Server
Perform the same steps on your GHES instance. The generator derives the API base from the organization's url= (https://<host>/api/v3), so no extra configuration is needed.
Verify the token
curl -fsS -X POST -H "Authorization: token $ACCESS_TOKEN" \
https://api.github.com/orgs/<org>/actions/runners/registration-tokenA JSON body with a token field means the fleet can register runners for that organization. 404 usually means the token cannot see the organization; 401 means it is invalid or expired.
Cloning over SSH instead
ACCESS_TOKEN mints runner registration tokens, but the repository fetch can use SSH:
bash bootstrap.sh --ssh # reuse ~/.ssh/id_rsa
bash bootstrap.sh --generate-ssh-key # create a key and install it on GitHubTo let the installer upload the new public key, the token also needs the classic write:public_key scope (fine-grained: Git SSH keys: Read and write). Without it, the installer prints the public key and the URL to add it manually.
Expiry and rotation
- Registration tokens are minted each time a runner container starts, so an expired
ACCESS_TOKENbreaks runners only after a container restart. Put a reminder on the expiration date. - To rotate: create the replacement, update
.env, then run./fleet.sh restart. - Treat the token as the fleet's most sensitive credential: it is present in every runner container's environment, where any job can read it. Use private repositories only — see the Security model.