Skip to content

GitHub Token Operations

Guide to GitHub token creation and management for cluster services.

PropertyValue
Actions Runner Token Pathsecret/fzymgc-house/cluster/github
Token Keyactions_runner_token
Runner Namespaceactions-runner-system
Runner Labelselfhosted-cluster
TypeUse CaseExpirySecurity
Fine-grained PATService automationConfigurableHigher (recommended)
Classic PATLegacy integrationsConfigurableLower
GitHub AppOrg-level automationNo expiryHighest

Repository access for self-hosted runners.

  • Scope: repo, workflow
  • Stored: secret/fzymgc-house/cluster/github

Repository access for GitOps sync.

  • Scope: Repository read
  • Stored: secret/fzymgc-house/cluster/argocd

VCS integration for speculative plans.

  • Type: GitHub App
  • Configuration: HCP Terraform settings
  1. Navigate to GitHub Settings

  2. Generate New Token

    • Click “Generate new token” > “Generate new token (classic)”
    • Note: Give it a descriptive name like actions-runner-controller-selfhosted-cluster
    • Expiration: Recommended: 90 days (you’ll need to rotate it)
  3. Select Scopes

    For repository-level runners, select these scopes:

    • repo (Full control of private repositories)
    • workflow (Update GitHub Action workflows)

    Important: These are the ONLY two scopes needed.

  4. Generate and Copy Token

    • Click “Generate token” at the bottom
    • IMPORTANT: Copy the token immediately - you won’t see it again
    • Token format: ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Section titled “Option 2: Fine-Grained Personal Access Token (Recommended)”
  1. Navigate to Fine-Grained Tokens

  2. Configure Token

    • Token name: actions-runner-controller-selfhosted
    • Expiration: 90 days (recommended)
    • Description: Self-hosted GitHub Actions runner
    • Resource owner: fzymgc-house
  3. Repository Access

    • Select: Only select repositories
    • Choose: fzymgc-house/selfhosted-cluster
  4. Permissions

    Under “Repository permissions”:

    • Actions: Read and write
    • Contents: Read-only
    • Metadata: Read-only (automatically selected)
    • Workflows: Read and write
  5. Generate and Copy Token

    • Click “Generate token”
    • Copy the token: github_pat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Terminal window
# Make sure you're authenticated to Vault
vault token lookup
# Store the token
vault kv put secret/fzymgc-house/cluster/github \
actions_runner_token="ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Verify it was stored
vault kv get secret/fzymgc-house/cluster/github
# Get the actual token value (if needed for debugging)
vault kv get -field=windmill_actions_runner_token secret/fzymgc-house/cluster/github

Since tokens expire, you’ll need to rotate them periodically:

  1. Generate a new token following the same steps

  2. Update Vault:

    Terminal window
    vault kv patch secret/fzymgc-house/cluster/github \
    actions_runner_token="<new-token>"
  3. ExternalSecret will automatically sync the new token

  4. Runner pods will automatically use the new token

After storing the token:

Terminal window
# Check if ExternalSecret synced the token
kubectl --context fzymgc-house get externalsecret github-token -n actions-runner-system
kubectl --context fzymgc-house get secret github-token -n actions-runner-system
# Check controller deployment
kubectl --context fzymgc-house get pods -n actions-runner-system
# Verify runner registered with GitHub
kubectl --context fzymgc-house get runnerdeployment -n actions-runner-system
kubectl --context fzymgc-house describe runnerdeployment -n actions-runner-system

Check that the runner appears in GitHub:

  1. Go to: https://github.com/fzymgc-house/selfhosted-cluster/settings/actions/runners
  2. You should see a runner listed with label: selfhosted-cluster
  3. Status should show as “Idle” (green)
Terminal window
# Check controller logs
kubectl --context fzymgc-house logs -n actions-runner-system \
-l app.kubernetes.io/name=actions-runner-controller --tail=100
# Common errors:
# - "401 Unauthorized": Token invalid or expired
# - "403 Forbidden": Insufficient permissions
# - "404 Not Found": Repository access not granted
  1. Check token scopes: Must have repo and workflow (classic) or equivalent fine-grained permissions
  2. Verify repository access: Token must have access to fzymgc-house/selfhosted-cluster
  3. Check controller status: kubectl get pods -n actions-runner-system
  4. Review logs: Look for authentication errors in controller logs
Terminal window
# Check ExternalSecret status
kubectl --context fzymgc-house describe externalsecret github-token -n actions-runner-system
# Common issues:
# - Vault path wrong: Should be secret/fzymgc-house/cluster/github
# - Vault key wrong: Should be actions_runner_token
# - ClusterSecretStore not configured: Check 'vault' ClusterSecretStore exists
FeatureClassic PATFine-Grained PAT
ScopeAll repos user has access toSpecific repositories only
PermissionsBroad (repo, workflow)Granular (Actions, Workflows, etc.)
ExpirationCustom (max 1 year)Custom (max 1 year)
SecurityLowerHigher (recommended)
SetupSimplerMore complex

Recommendation: Use Fine-Grained PAT for better security.

  • Token Storage: Never commit tokens to Git. Always use Vault.
  • Token Scope: Use minimum required scopes. Fine-grained tokens are more secure.
  • Token Expiration: Set reasonable expiration (90 days recommended).
  • Token Rotation: Have a process to rotate before expiration.
  • Access Control: Limit who can access the Vault secret.
  • Rotate immediately if compromised