GitHub Actions out of memory: exit code 137 and JavaScript heap out of memory

Updated · prices and specs read on 24 Oct 2026

Exit code 137 means the process was killed with SIGKILL (128 + 9), on a GitHub Actions runner almost always by the Linux out-of-memory killer because the job used more memory than the machine has: GitHub's standard runner in a private repository has 8 GB. "JavaScript heap out of memory" is different: Node.js hit its own heap limit before the machine ran out.

Which error you have

What the log says What happened Where to look
Process completed with exit code 137 The kernel killed the process (SIGKILL): the machine ran out of memory The machine’s memory, your job’s peak
FATAL ERROR: ... JavaScript heap out of memory Node.js reached its own heap limit; the machine may still have had free memory Node’s --max-old-space-size
The runner has received a shutdown signal or a lost runner The whole runner went away, sometimes because the machine was out of memory The job’s peak, or the runner’s host

To confirm the out-of-memory killer, add a step that runs even after a failure:

- name: Why did it stop?
  if: failure()
  run: sudo dmesg | grep -i -E "killed process|out of memory" || echo "no OOM kill in the kernel log"

Measure the peak first

Size a job by its measured peak, never by guesswork. Three ways:

# 1. GNU time around the heavy command: "Maximum resident set size" is the peak of that process tree (in KB)
/usr/bin/time -v npm test 2>&1 | grep "Maximum resident"

# 2. What the machine has, before and after
free -m

# 3. On a cgroup v2 machine, the peak of everything in the job's cgroup
cat /sys/fs/cgroup/memory.peak

On FlameWill every job’s log ends with its peak process memory and its peak including the page cache, so the number is there without adding a step.

Five ways to make it fit

1. Run fewer things at once. Most test runners start one worker per core. Cap them: jest --maxWorkers=2, Vitest’s --maxWorkers, Playwright’s --workers, make -j2. Fewer workers hold less memory at once, for a little more time; measure the peak again after the change.

2. Give Node.js the heap it needs, only if the machine has it. For “JavaScript heap out of memory” on a machine with spare memory:

env:
  NODE_OPTIONS: --max-old-space-size=6144

Leave at least 1.5 GB for the rest of the machine: on an 8 GB runner, 6144 MB is the most that is safe.

3. Add swap as a safety net. A slow job beats a killed one while you fix the cause:

- run: |
    sudo fallocate -l 4G /swapfile && sudo chmod 600 /swapfile
    sudo mkswap /swapfile && sudo swapon /swapfile

4. Split the job. A test suite that runs as one job can run as a matrix of shards (--shard=1/4 in Jest, Vitest and Playwright); each shard’s peak is lower.

5. Use a runner with more memory. On GitHub, more memory means more cores: 16 GB is a 4-core larger runner at $0.008 a minute on arm64 ($0.012 on x64), and larger runners are never covered by included minutes. Runners sized by memory cost less for the same headroom: FlameWill’s flamewill-2vcpu-16gb-ubuntu-2404 has 16 GB on 2 vCPU for $0.0015 a minute.

jobs:
  test:
    runs-on: flamewill-2vcpu-16gb-ubuntu-2404

Pick the size from the peak

Leave about 1.5 GB above the peak for the operating system and the runner itself:

Job’s measured peak Runner memory
up to 6.5 GB 8 GB
up to 14.5 GB 16 GB
up to 30.5 GB 32 GB

If you change runner, measure again on the new one: the peak can move with the number of cores.

Questions

How much memory does a GitHub Actions runner have?

GitHub's standard Linux runner has 8 GB in a private repository (2 vCPU) and 16 GB in a public one (4 vCPU); ubuntu-slim has 5 GB. Larger runners have 4 GB per vCPU, from 8 GB (2 cores) to 256 GB (64 cores, x64).

Does increasing --max-old-space-size fix exit code 137?

No. It raises Node.js's own heap limit, which fixes "JavaScript heap out of memory" only when the machine has the memory to spare. If the machine is already full, a higher limit turns the Node.js error into an exit code 137 kill instead.

Can I add swap on a GitHub Actions runner?

Yes, the runner user has passwordless sudo, so a step can create and enable a swap file with fallocate, mkswap and swapon. It turns a crash into a slower job; it does not make the job fit.

Why did my job get exit code 137 only sometimes?

Memory use depends on how much runs at once. Test runners start one worker per core by default, and a build can run several compilers in parallel, so the peak changes with timing and with the machine's core count.

Sources

Read on 24 Oct 2026. Prices change: check each vendor's own page before you decide.

This page as Markdown: /guides/github-actions-out-of-memory.md