Diagnosing Offline GitHub Actions Self‑Hosted Runners
A step‑by‑step guide to identify why a self‑hosted runner shows as offline and how to restore it.
12 Sept 2026, 22:42 UTC

Recognizable condition
In the repository or organization settings under Settings → Actions → Runners, a self‑hosted runner appears as Offline or never picks up jobs, even though the host machine is reachable.
Short cause/diagnostic table
| Observed symptom | Likely cause |
|---|---|
| Runner service not active | Service crashed, failed to start, or was stopped |
| Runner process runs but no jobs | Outbound traffic to GitHub blocked (firewall, proxy, DNS) |
| Runner reports version mismatch | Out‑of‑date runner binary |
| Registration fails with token error | Expired, revoked, or insufficient‑scope token |
| Runner stays busy after a job | Long‑running step or infinite loop preventing new work |
Ordered checks
-
Verify the runner service status
On Linux:
sudo systemctl status actions.runner.*Look for
active (running). On Windows, open Services MMC, find theGitHub Actions Runner service and confirm its state isRunning.Required permission: root/sudo on Linux or Administrator on Windows.
Risk: Restarting the service will interrupt any job currently executing on that runner.
-
Test outbound connectivity to GitHub services
From the runner host, run:
curl -v https://github.com curl -v https://pipelines.actions.githubusercontent.comOn Windows PowerShell:
Test-NetConnection -ComputerName github.com -Port 443 Test-NetConnection -ComputerName pipelines.actions.githubusercontent.com -Port 443Expect a TLS handshake and an HTTP 200 response (or equivalent TCP success). If the connection times out or fails, investigate firewall rules, proxy settings, or DNS resolution.
Required permission: none beyond normal user access to network tools.
-
Check the runner application version
Navigate to the runner directory (e.g.,
/home/user/actions-runner) and run:./config.sh --versionCompare the printed version with the latest release listed on GitHub Actions runner releases. A significant lag (more than one minor version) may cause registration failures.
Required permission: read access to the runner directory.
-
Validate the authentication token
Attempt to re‑register the runner in interactive mode (do not use
--unattended) to see if the token is accepted:./config.sh --url --token <TOKEN>If the script prompts for errors such as
Bad credentialsorMissing required scope, the token is invalid or lacksadmin:repo(for repository‑level runners) oradmin:org(for organization‑level runners).Required permission: ability to create a new token with the appropriate scope in GitHub.
Risk: Exposing the token in terminal output; ensure the screen is not shared and rotate the token if leakage is suspected.
-
Look for a stuck job
Check the runner’s internal logs:
cat /home/user/actions-runner/_diag/*.logor on Windows:
Get-Content C:\actions-runner\_diag\*.logSearch for patterns like
Running stepwithout a subsequentStep completedor evidence of an infinite loop. If a job appears to be hanging, you can safely stop the runner service, which will terminate the process, then restart it.Required permission: same as service control.
Risk: Stopping the service aborts the current job; ensure it is safe to do so (e.g., the job is not holding external resources that need cleanup).
Fixes tied to findings
- Service not running:
(on Windows: restart the service via Services MMC). After restart, refresh the runner page; it should showsudo systemctl restart actions.runner.*Onlinewithin a minute. - Network blocked:
Adjust outbound firewall rules to allow TCP 443 togithub.comandpipelines.actions.githubusercontent.com. If a proxy is required, setHTTP_PROXYandHTTPS_PROXYenvironment variables for the runner service or configure the proxy in the runner’s.Runnerfile. Verify connectivity again with thecurlorTest-NetConnectioncommands. - Outdated runner:
Download the latest runner binary for your OS/architecture from the releases page, extract it into a new directory, and runconfig.sh(orconfig.cmd) with the existing URL and token. Do not overwrite the existing directory while the service is running; stop the service first, then replace the binaries. - Invalid token:
Generate a new token with the required scope (admin:repofor a repository runner,admin:orgfor an organization runner). In the runner directory, run./config.sh --removeto unregister the old runner, then./config.sh --url <URL> --token <NEW_TOKEN>to register again. Start the service afterward. - Stuck job:
Stop the runner service, verify that no critical processes remain (ps -ef | grep Runner.Listeneron Linux, or Task Manager on Windows), then start the service again. The runner will appear online and be ready to accept new jobs.
Escalation criteria
If after performing all checks and applying the corresponding fix the runner still shows as Offline:
- Collect the runner’s diagnostic logs (
_diagfolder) and the system logs (journalctl -u actions.runner.*on Linux, or Event Viewer → Windows Logs → Application on Windows). - Verify that the host’s system time is synchronized (NTP); a large clock skew can cause TLS handshake failures.
- Open a support ticket with GitHub Support, providing the logs, runner version, host OS details, and a summary of the steps already taken.
Limitations and practical verification
This guide assumes a standard self‑hosted runner installed via the official actions/runner binary. Custom wrappers or containerized runners may require additional steps (e.g., checking container network mode).
To confirm that the runner is truly operational after a fix:
- Refresh the repository’s runner settings page; the status should change to
Online. - Trigger a minimal workflow (e.g., a job that runs
echo hello) and verify that the job is picked up and completes successfully. - Check the runner’s own log for a line similar to
Listening for Jobsfollowed byJob receivedwhen the test workflow runs.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.