LearnDevOpsHands-On Labs
Troubleshooting Matrix/fork: Cannot allocate memory
LinuxCritical Severity

fork: Cannot allocate memory

Fix "fork: Cannot allocate memory": run free -m, count threads, then check cgroup limits, ulimit -u and vm.overcommit_memory. Often not real memory exhaustion.

Observed Error Signature / Log Pattern
bash: fork: Cannot allocate memory

Incident Overview

Fix "fork: Cannot allocate memory": run free -m, count threads, then check cgroup limits, ulimit -u and vm.overcommit_memory. Often not real memory exhaustion.

hard fix·5 min read

By Vishvesh Patel·DevOps Engineer·Updated 21 September 2026

BASHDiagnostic / remediation command
bash: fork: Cannot allocate memory

fork: retry: Resource temporarily unavailable

OSError: [Errno 12] Cannot allocate memory

java.lang.OutOfMemoryError: unable to create new native thread

Do this first3 steps

Run these in order. Each one tells you what its output means before you change anything.

• Check whether memory is genuinely exhausted

MemAvailable is the number that matters, not free. Plenty available while forks fail means the limit is on processes or threads rather than memory, which is the more common case.

• Count processes and threads against the limits

ps -eLf counts threads, not just processes, and threads are what exhaust these limits. A count approaching pid_max or threads-max is the answer.

• Check the cgroup limit, which applies per service and per container

systemd sets TasksMax on units, defaulting to a percentage of the system maximum. A service hitting its own cgroup limit sees fork failures while the host has ample capacity.

Cannot allocate memory from fork() is ENOMEM, and the kernel returns it for several reasons that are not "the machine is out of RAM". Establish which before changing anything.

BASHDiagnostic / remediation command
free -h && cat /proc/meminfo | grep -E "MemAvailable|Committed_AS|CommitLimit"

Is memory actually the problem?

Read available, not free. Page cache in buff/cache is reclaimable, so 1Gi free with 22Gi available is a healthy machine. Only a low available means genuine memory pressure.

With 22Gi available and forks failing, memory is not the constraint.

BASHDiagnostic / remediation command
free -h

Count threads, not processes

Every thread consumes a PID from the same space as processes. A JVM or a Go service with thousands of threads exhausts pid_max long before it exhausts memory, and java.lang.OutOfMemoryError: unable to create new native thread is the same failure surfaced by the JVM.

Find the biggest consumers:

If one process holds thousands of threads, that is a leak worth fixing rather than a limit to raise.

Raise the limits if the count is legitimate:

4194304 is the current maximum on 64-bit Linux.

BASHDiagnostic / remediation command
ps -eLf | wc -l           # the L is threads
cat /proc/sys/kernel/pid_max
cat /proc/sys/kernel/threads-max

The cgroup limit

This is the one people miss, because the host looks fine.

systemd sets TasksMax on every unit, defaulting to 15% of kernel.pid_max:

At the limit, the service cannot fork while the host has capacity to spare.

TasksMax=infinity removes the limit, and removes the protection against a fork bomb taking the machine down with it.

In a container the same limit is pids.max:

Kubernetes has no per-Pod field. The kubelet's podPidsLimit sets it node-wide, defaulting to 4096 on recent versions:

Inside a container, check what you are actually subject to:

BASHDiagnostic / remediation command
systemctl show myservice -p TasksMax -p TasksCurrent

RLIMIT_NPROC

A per-user limit, separate from everything above:

nproc counts every process for that uid across the whole system, not per session. A service account running many workers hits it while other users are unaffected.

As ever, this does not apply to systemd services. Those use LimitNPROC= in the unit.

BASHDiagnostic / remediation command
ulimit -u
cat /proc/$$/limits | grep processes

Overcommit

With 2, allocations are refused once Committed_AS reaches CommitLimit, which produces ENOMEM while free -h shows memory available:

Mode 2 is a deliberate choice for predictability, at the cost of refusing allocations processes would never have touched. fork() is particularly affected, because it nominally duplicates the parent's address space even though copy-on-write means it rarely uses it. A 16GB process forking to run a 1KB shell script needs 16GB of commit under strict accounting, which is why large JVMs and databases hit this.

That specific case is what vm.overcommit_ratio and, for databases, posix_spawn instead of fork exist to address.

BASHDiagnostic / remediation command
cat /proc/sys/vm/overcommit_memory

When it is genuinely memory

An OOM killer entry names the process and its score. In a container, OOMKilled and exit code 137 are the equivalent signals.

BASHDiagnostic / remediation command
grep -i "killed process" /var/log/syslog
sudo journalctl -k | grep -i "out of memory"

A checklist

Frequently Asked Questions

Why does fork fail when free shows plenty of memory?

Because ENOMEM from fork() covers several limits that are not memory. The most common are the PID space, which threads consume as well as processes, and cgroup pids.max, which systemd sets per unit at 15% of the system maximum by default. Strict overcommit accounting can also refuse a fork while memory is free, because fork() nominally duplicates the parent's entire address space. Check the thread count and the cgroup limit before concluding you need more RAM.

What is the difference between free and available in free -h?

free is memory doing nothing at all; available is memory the kernel can give to a new allocation, which includes reclaimable page cache. A healthy Linux machine deliberately uses most of its RAM for cache, so free being small is normal and not a problem. available is the number to watch, and only a low value indicates real pressure. Monitoring or alerting built on free rather than available produces constant false alarms.

Why does my service hit a limit when the host has capacity?

Because it is confined by its own cgroup. systemd applies TasksMax to every unit, defaulting to 15% of kernel.pid_max, so a service can exhaust its allowance on a machine with plenty of headroom. systemctl show <unit> -p TasksMax -p TasksCurrent shows both numbers. Raise it with a drop-in via systemctl edit, and prefer a specific larger value over infinity, which also removes the protection that stops a runaway process taking the whole machine down.

How do I set a PID limit for a Kubernetes Pod?

There is no per-Pod field. The kubelet applies podPidsLimit to every Pod on the node, defaulting to 4096 on recent versions, and changing it is a node configuration change rather than something you can express in a manifest. Inside a container, cat /sys/fs/cgroup/pids.max shows the limit you are actually subject to. If one workload genuinely needs far more threads than the rest, a dedicated node pool with a higher kubelet setting is the usual way to accommodate it.

Should I set vm.overcommit_memory to 2?

Only deliberately, and knowing the cost. Mode 2 refuses allocations once committed memory reaches CommitLimit, which gives predictable behaviour and avoids the OOM killer, but it accounts for memory processes may never touch. fork() suffers most, because it nominally needs a copy of the parent's whole address space even though copy-on-write means it rarely uses any of it, so a large JVM or database forking a small child can fail outright. The default heuristic mode is the right choice for most systems.

Reference and practice

Learn the underlying concept

Other Linux errors

Free, comprehensive DevOps tutorials and learning roadmaps. Master Docker, Kubernetes, CI/CD, and more.

Get notified about new tutorials and features.

• Roadmaps

• Interview Prep

• Certifications

• Company

© 2026 DevOpsLesson. All rights reserved.

Need to practice this scenario?

Simulate this error condition and practice recovery commands safely in our in-browser terminal.

Open Linux Sandbox