Self-hosted runner offline - no jobs picked up
Workflow runs queued with 'Waiting for a runner to pick up this job'
Focused incident mode — strips away everything but this runbook.
Symptoms
- Workflow runs queued with 'Waiting for a runner to pick up this job'
- Self-hosted runner shows as offline in Settings > Actions > Runners
- Jobs stuck in queue for hours with no runner assigned
Self-hosted runners need outbound HTTPS to api.github.com and *.actions.githubusercontent.com. Verify firewall rules before restarting the service.
Evidence to grab
Collect these artifacts before you change anything — they prove (or rule out) each cause.
Runner registration status
gh api repos/<OWNER/REPO>/actions/runners --jq '.runners[] | {name, status, labels}'Runner process on the host
systemctl status actions.runner.<OWNER>.<REPO>.<RUNNER_NAME>.service
Diagnostic flow
Run the steps in order, marking each claim confirmed or refuted. The evidence you gather feeds the ranked causes below.
Guided decision tree is ready.
Steps with commands, expected output, and pass/fail branches.
Ranked root causes
Evidence checklist
- systemctl shows the service as inactive/dead
- journalctl shows an unhandled exception or segfault
Confirm before fixing
ssh <RUNNER_HOST> systemctl is-active actions.runner.<OWNER>.<REPO>.<RUNNER_NAME>.serviceCheck if the service is running
Fix
ssh <RUNNER_HOST> sudo systemctl start actions.runner.<OWNER>.<REPO>.<RUNNER_NAME>.serviceRestart the runner service
Expected: Active: active (running)
Resolution runbook
Apply from top to bottom after the cause is confirmed. Review every command before running it — risk tags are shown on each.
gh api repos/<OWNER/REPO>/actions/runners --jq '.runners[] | {name, status}'Confirm which runners are offline
ssh <RUNNER_HOST> sudo systemctl restart actions.runner.<OWNER>.<REPO>.<RUNNER_NAME>.serviceRestart the runner service
ssh <RUNNER_HOST> journalctl -u actions.runner.<OWNER>.<REPO>.<RUNNER_NAME>.service --since '5 min ago' --no-pagerVerify the runner came back online
gh run list --limit 5Confirm queued jobs start executing
Prevention
- Monitor runner heartbeat via the Actions API and alert on offline status > 5 minutes.
- Use a process supervisor (systemd, supervisor) that auto-restarts the runner on crash.
- Keep runner tokens rotated; use GitHub App tokens for long-lived self-hosted runners.
- Run 'runnerctl diagnose' periodically to validate connectivity.
Related runbooks
Same stack, same failure family — worth a skim.
Workflow runs show 'Queued' status for many minutes before starting
Error: Resource not accessible by integration
Commands marked READ CHANGE DESTRUCTIVE — never run DESTRUCTIVE commands against production data you cannot restore. Copy → verify → execute.