Fix 'bash: fork: retry: Resource temporarily unavailable' โ€” Max User Processes Limit on Linux

intermediate๐Ÿง Linux2026-05-05| Linux (Ubuntu, Debian, CentOS, RHEL, Rocky Linux) โ€” any distro with PAM/ulimit, typically triggered on servers running many concurrent processes (web servers, CI runners, Java apps)

Error Message

bash: fork: retry: Resource temporarily unavailable
#ulimit#fork#process#limits#pam

What happened

You're running a script, starting a service, or SSH-ing into a server โ€” and suddenly everything seizes up with this:

bash: fork: retry: Resource temporarily unavailable
bash: fork: Resource temporarily unavailable

New processes can't start. SSH connections time out. Cron jobs silently fail. RAM is fine, CPU is idle, the server is clearly up โ€” but nothing will fork. You've hit the max user processes (nproc) limit.

Linux tracks how many processes each user owns at any given moment. Hit that ceiling and fork() returns EAGAIN โ€” which is exactly what you're seeing.

Diagnose first

Check the current limits

# See limits for your current shell session
ulimit -a

# Specifically max user processes
ulimit -u

On older systems, the default is painfully low:

max user processes              (-u) 1024

Modern systemd distros tend to default higher (63503 on Ubuntu 22.04, for example), but services running under their own dedicated users often inherit a much lower PAM limit โ€” sometimes still 1024.

Count how many processes the affected user is running

# Replace 'www-data' with the actual user
ps -u www-data --no-header | wc -l

# Or see all users sorted by process count
ps aux | awk '{print $1}' | sort | uniq -c | sort -rn | head -20

If that number is at or near the ulimit value, you've confirmed the problem.

Check the PAM-configured limit (the real ceiling)

grep -r nproc /etc/security/limits.conf /etc/security/limits.d/

You might see something like:

*    soft    nproc    1024
*    hard    nproc    4096

The soft limit is what processes start with. The hard limit is the maximum a process can raise itself to. Both being low is your culprit.

Check what systemd enforces (for services)

Here's a gotcha: if the affected process is a systemd service, /etc/security/limits.conf often doesn't apply at all โ€” systemd manages its own limits separately.

# Check the effective limits of a running service (e.g. nginx)
systemctl show nginx | grep -i task

# Or check /proc for a specific PID
cat /proc/$(pgrep -o nginx)/limits | grep processes

Fix it

Option 1: Raise the limit for the current session (temporary)

Good for quick testing or one-off scripts. It resets the moment the shell closes.

# Raise soft limit to 8192
ulimit -u 8192

# Verify
ulimit -u

You can only raise up to the hard limit. Raising the hard limit requires root.

Option 2: Set permanent limits via PAM (for login users and daemons)

Add a new file under /etc/security/limits.d/ โ€” it's cleaner than editing limits.conf directly:

sudo nano /etc/security/limits.d/99-nproc.conf

Add these lines (adjust values to your actual workload):

# Raise nproc for all users
*    soft    nproc    65536
*    hard    nproc    65536

# Or target a specific user
www-data    soft    nproc    32768
www-data    hard    nproc    32768

# root is often excluded from the * wildcard โ€” set explicitly if needed
root    soft    nproc    unlimited
root    hard    nproc    unlimited

Important: PAM limits only apply to new login sessions. Existing processes keep their old limits. Restart the service or log out and back in to pick up the change.

Option 3: Fix limits for systemd services

PAM limits don't reach services started by systemd. Override per-service with a drop-in file:

sudo systemctl edit nginx

Add:

[Service]
TasksMax=infinity
LimitNPROC=65536

Then reload and restart:

sudo systemctl daemon-reload
sudo systemctl restart nginx

To raise the global default for all systemd services, edit /etc/systemd/system.conf:

sudo nano /etc/systemd/system.conf
[Manager]
DefaultTasksMax=infinity
sudo systemctl daemon-reload

Option 4: Check for a process leak (fix the root cause)

Raising the limit treats the symptom. If a zombie process or a runaway spawning loop is the real problem, no limit will save you for long.

# List zombie processes
ps aux | awk '$8 == "Z"'

# Watch if a specific user's count keeps climbing
watch -n 2 "ps -u myuser --no-header | wc -l"

# Find the parent spawning too many children
ps -eo pid,ppid,user,comm | awk '{print $2}' | sort | uniq -c | sort -rn | head -5

Count climbing continuously and never dropping? That's a leak. Fix the application.

Verify the fix worked

# After applying limits.conf changes, open a new session and check:
ulimit -u

# For a systemd service, confirm the new limit is live:
cat /proc/$(pgrep -o nginx)/limits | grep processes

# Stress test โ€” spawn 100 background processes:
for i in $(seq 1 100); do sleep 1 & done
# Should complete without 'Resource temporarily unavailable'
jobs | wc -l
kill $(jobs -p)

Quick reference: common defaults by distro

  • CentOS 6 / RHEL 6: default nproc = 1024 for non-root users โ€” Java apps and Ruby on Rails deployments blow past this constantly
  • CentOS 7 / RHEL 7+: ships /etc/security/limits.d/20-nproc.conf with 4096 โ€” check and override this specific file, not limits.conf
  • Ubuntu 20.04+: systemd-managed, TasksMax defaults to 15% of the system max (roughly 4915 on a typical VPS) โ€” services with high concurrency still hit it
  • Docker containers: inherit the host's ulimits by default; pass --ulimit nproc=65535:65535 when running containers with many concurrent processes

Real-world culprit: CI runners

The most common place I've seen this: a Jenkins or GitLab Runner instance running 20 parallel builds, where each job forks a shell, a compiler, and a test suite. That's 60+ processes per job โ€” multiplied by 20 jobs, you blow past 1024 in seconds.

For any user that runs services, set nproc to at least 65536. There's no cost โ€” it's just a kernel accounting limit, not a memory reservation.

One more thing: on systems where PAM is configured but systemd manages your services, fixing limits.conf alone won't help. You need both.

Related Error Notes