# The runner lost communication with the server

> Fix "the runner lost communication with the server" in GitHub Actions: the agent stopped answering, and the cause is usually the host, not the link.

Source: https://latchkey.dev/learn/failures/runner-lost-communication-with-the-server  
Updated: 2026-09-20

"The runner lost communication with the server" in GitHub Actions means the agent stopped answering GitHub while your job was running, so the backend gave up on it and failed the run. The phrase points at the network and the cause usually is not: anything that kills or starves the runner process produces the same sentence.

## What this error means

The log stops in the middle of a step with no error from the command, and the job is failed with a single line naming the machine: "The self-hosted runner: gpu-runner-3 lost communication with the server. Verify the machine is running and has a healthy network connection." The hosted variant is the same sentence with a GitHub-assigned runner name. The runner often shows as Offline in settings afterwards, or reappears a minute later as though nothing happened. What is never present is a stack trace, because the process that would have written one is gone.

```Actions log, failed job (the shape GitHub prints; see actions/runner#2624)
The self-hosted runner: gpu-runner-3 lost communication with the server. Verify the machine is running and has a healthy network connection.
```

## Common causes

### The job starved or killed the machine the agent was running on

Memory exhaustion is the leading cause: the kernel out-of-memory killer picks a process under pressure and the runner agent is a candidate like any other. A full disk does the same more quietly, because the agent cannot write its own state. Check the kernel log and the disk at the failure timestamp before anything else.

### You checked the network and it was fine, because the network was not the problem

The message asks you to verify the machine is running and has a healthy network connection, so that is what everyone verifies, and it usually passes. The heartbeat stopping does not mean the route is down: it means the process that sends it stopped getting scheduled, or stopped existing. Read the runner's own diagnostic log for the last line it wrote; that timestamp tells you whether the agent died or the link did.

### Egress to GitHub was blocked or interrupted

A proxy that dropped the long-lived connection, a firewall rule applied mid-run, or a TLS-intercepting appliance with an expired certificate. This is the cause the message actually describes, and the least common of the three in our experience.

### The host went away underneath the job

A spot instance reclaimed, a node scaled in by an autoscaler, an unattended upgrade that rebooted, or a VM migrated by the hypervisor. The job was healthy and its machine was not. This reports as "The runner has received a shutdown signal" when the agent gets enough warning to say so, and as lost communication when it does not.

## How to fix it

### Read the runner diagnostic log on the host before anything else

1. Find the newest file in the runner's `_diag` directory and read its tail.
2. Compare the last line it wrote against the timestamp of the job failure, which tells you whether the agent died or kept running while the link was down.
3. Check the kernel log and disk usage for the same minute, because an out-of-memory kill or a full disk explains most of the first case.

```Terminal, on the runner host
tail -80 "$(ls -t ~/actions-runner/_diag/Runner_*.log | head -1)"
journalctl -k --since "-1 hour" | grep -iE "oom|killed process"
df -h /
```

### Give the agent headroom it cannot be squeezed out of

The runner competes for memory with the job it runs, and it loses. Cap the job rather than the machine: pin worker counts, bound container memory, leave a margin. In Kubernetes, raise the pod's memory request so the container is not evicted first.

```Runner pod spec (Actions Runner Controller)
resources:
  requests:
    memory: 4Gi
    cpu: "2"
  limits:
    memory: 8Gi
```

### Verify egress with the runner's own check, not with curl

The configuration script has a check that tests every network service the runner needs, a stronger test than reaching github.com in a browser. Run it on the failing host, as the account the service runs as.

```Terminal, on the runner host
./config.sh --check --url https://github.com/ORG/REPO --pat $GH_PAT
```

### Stop the autoscaler from taking a busy runner

If the failures cluster around scale-down events, the fix is in the scaler rather than the runner. Mark nodes running jobs as unsafe to evict, drain only idle runners, and give spot instances a fallback so a reclaim does not land on a long job.

## How to prevent it

- Alert on runners flipping to Offline, not just on failed jobs, so a sick host is found before the next job lands on it.
- Keep every runner host well clear of its memory and disk limits, and pin the worker counts of anything that forks.
- Exclude nodes with running jobs from autoscaler scale-down, and prefer on-demand capacity for long jobs.
- Run the runner as a service so a reboot brings it back, and patch on a schedule rather than unattended.
- Re-run once, then count. A recurring lost connection is a host, not an incident.

## The message describes GitHub's view, not the cause

GitHub knows one thing about your runner: whether it is still answering. When the answers stop, the only sentence it can write is that communication was lost, and it suggests the two causes it can imagine from that side. That is why it reads like a network diagnosis when the network is usually fine.

The maintainers put it more usefully. On the runner issue tracker the guidance is that anything which terminates the runner process, starves it for CPU or memory, or blocks its network access produces this error. Three faults, one sentence.

So the question is not "is the network up" but "what happened on that machine at that moment". The agent writes its own diagnostic log on the host, and that file holds the last thing it managed to say.

```Terminal, on the runner host
# on the self-hosted host, the runner's own diagnostics
ls -t ~/actions-runner/_diag/ | head -3
tail -50 ~/actions-runner/_diag/Runner_*.log

# and what the kernel did at that timestamp
journalctl -k --since "-30 min" | grep -i "out of memory"
```

## Hosted, self-hosted and Kubernetes fail for different reasons

On a GitHub-hosted runner you cannot inspect the machine, so the question is whether your job was heavy enough to take the agent with it. A step that exhausts memory, fills the disk or reconfigures DNS mid-run can starve it; that is the part of the hosted case you control. Anything else is a platform fault, and the check for it is the status page over the failure window.

On your own machine the causes are ordinary operations problems: the host rebooted for updates, a spot instance was reclaimed, the kernel killed the runner process under memory pressure, the disk filled, or an autoscaler scaled the node in while a job was on it. The last one is common enough to have its own wording, "The runner has received a shutdown signal", which a GitHub issue search for that phrase alongside Actions returned 1,507 results for on 19 September 2026.

In Kubernetes, with the Actions Runner Controller, both apply at once: the pod has resource limits the OOM killer enforces, and the controller may terminate a pod during a scale-down. Raise the pod's memory request first, then set scale-down to leave busy runners alone.

## What Latchkey does about it, and what it cannot

There is no detection pattern for this failure in Latchkey's pattern library, and that is structural rather than a gap. The self-heal wrapper runs beside your step on the runner: it reads the failure, posts it for diagnosis, and applies a fix. When the runner itself fails, the wrapper is gone with it, and nothing is left to notice or report. A failure of the reporter cannot be reported by the reporter.

What changes on managed runners is the size of the category. Latchkey runners are [ephemeral by design](/documentation/runners-overview): each is created for a single job and destroyed when it ends, so there is no long-lived host to patch, no service to survive a reboot, no disk that fills until the agent cannot write, and no autoscaler of yours reclaiming a machine mid-job. The self-hosted causes above are mostly properties of owning a fleet.

The concession is that the class does not disappear: a machine can still fail under a job, and when it does you re-run it. What you stop doing is the part after that, working out which of your hosts it was and what it was starved of.

## FAQ

### What does "lost communication with the server" actually mean?

GitHub stopped receiving the runner's heartbeat while a job was assigned, and after a threshold declared the job failed. It describes what GitHub observed rather than what went wrong, which is why the suggested check, that the machine is running and the network is healthy, so often passes.

### Is my workflow at fault, or the runner?

Both are common and distinguishable. If the step running at the cut-off was memory or disk heavy, the job most likely starved the agent, and the kernel log will say so. If the runner was idle-weight at that moment, or the host rebooted or was reclaimed, the machine went away underneath a healthy job.

### Why does the runner show as offline right after the job fails?

The same event usually produced both: the agent died, the host rebooted, or the connection was severed, and GitHub marks a runner offline when it stops answering. A runner that reappears as Idle a minute later was restarted by its service manager, which says the process died rather than the network.

### Can I recover the log from a job whose runner disappeared?

Only the part GitHub had already received. The rest was buffered on a machine that stopped talking. On a self-hosted host the runner's diagnostic directory usually holds more than the web log, which is the strongest reason to look there first.

## References

- [actions/runner#2624: the self-hosted runner lost communication with the server](https://github.com/actions/runner/issues/2624)
- [GitHub Docs: monitor and troubleshoot self-hosted runners](https://docs.github.com/en/actions/how-tos/manage-runners/self-hosted-runners/monitor-and-troubleshoot)
- [GitHub issue search for the phrase alongside GitHub Actions (958 issues, 19 September 2026)](https://github.com/search?q=%22lost+communication+with+the+server%22+%22github+actions%22&type=issues)
- [GitHub issue search: "The runner has received a shutdown signal" (1,507 issues, 19 September 2026)](https://github.com/search?q=%22The+runner+has+received+a+shutdown+signal%22+%22github+actions%22&type=issues)
- [Latchkey documentation: ephemeral runners, one job per machine](/documentation/runners-overview)

---

Latchkey runs CI/CD that repairs its own failures. Agent entry points: https://latchkey.dev/agent.txt, https://latchkey.dev/openapi.json, https://latchkey.dev/llms.txt
