Skip to content
Products
AI Website Builder New VPS Hosting Cloud Servers Web Hosting Dedicated Servers Domains
Company
About Documentation Support Center Contact Get Started Call +91 75795 45488
Login
Hosting Panel — cPanel & Billing Console Panel — VPS Management
ALL SYSTEMS OPERATIONAL
VPS

VPS "fork: Resource Temporarily Unavailable": Fix nproc Limits

Getwebup 6 min read

You SSH into your VPS to restart a service and get slapped with -bash: fork: retry: Resource temporarily unavailable. Or a customer's PHP script that used to work fine suddenly can't spawn a subprocess. Nothing in top looks unusual — CPU is fine, RAM has headroom, disk isn't full. You've hit a completely different ceiling: the number of processes a single user is allowed to run at once.

Symptom: "fork: retry: Resource temporarily unavailable"

This one shows up in a handful of disguises depending on what triggered it:

  • Logging in over SSH gives fork: retry: Resource temporarily unavailable or the session just hangs and drops
  • A cron job fails silently, or its log shows -bash: fork: Cannot allocate memory (misleading — it's not actually a memory problem)
  • PHP-FPM or a Node app throws EAGAIN when trying to spawn a child process
  • WHM/cPanel shows the account hitting its "Number of Processes" limit in the Resource Usage graph, or CloudLinux logs an LVE nproc hit
  • Running ps -u yourusername | wc -l returns a number suspiciously close to a round figure like 100, 512, or 1024

The confusing part is that free -m and df -h both look healthy. That's the tell: this isn't memory or disk, it's a process-count limit.

Cause: you've hit the nproc limit, not the open-files limit

Linux enforces several independent ceilings per user, and it's easy to confuse them. If you've read our post on too many open files errors, this is the sibling problem — same family of controls, completely different resource:

LimitControlsCheck withTypical error
ulimit -nOpen file descriptors (sockets, log files, pipes)ulimit -nToo many open files, EMFILE
ulimit -uNumber of processes/threads a user can runulimit -ufork: retry: Resource temporarily unavailable
Kernel pid_maxTotal PIDs across the whole servercat /proc/sys/kernel/pid_maxServer-wide fork failures, rare
CloudLinux LVE nprocPer-cPanel-account process capWHM → Edit LVE / lveinfoAccount-specific hangs, other accounts unaffected

On a plain Ubuntu/Debian/AlmaLinux VPS, the process limit comes from PAM's limits.conf mechanism, plus a per-service override if the process was started by systemd. On a cPanel VPS running CloudLinux, there's a second layer on top: each cPanel account runs inside an LVE (Lightweight Virtual Environment) with its own nproc ceiling, independent of the OS-level limit.

The usual triggers:

  • A WordPress site with a runaway cron (WP-Cron firing hundreds of overlapping requests because a previous run never finished)
  • A backup script or import job that spawns a subprocess per file instead of reusing one
  • A PHP-FPM pool set to a huge pm.max_children that's technically legal for the OS but blows past the cPanel account's LVE nproc cap
  • An actual fork bomb — a compromised script or a malicious cron doing :(){ :|:& };:-style recursive forking
  • Node.js or Python worker pools (Celery, PM2 cluster mode) configured with more workers than the box was sized for

Fix: find what's forking, then raise (or correct) the limit

1. Identify who's eating your process budget

Before touching any limit, find out what's actually running:

ps -eLf | awk '{print $2}' | sort | uniq -c | sort -nr | head -20
ps -u www-data --no-headers | wc -l
ps -u yourcpaneluser --no-headers | wc -l

The first command counts threads/processes grouped by parent PID so you can spot which one is spawning hundreds of children. The next two count processes owned by a specific user — on cPanel, run the second one as the account's own user via su - accountuser -c "ps -u accountuser | wc -l".

2. Check the current limit

ulimit -u
cat /etc/security/limits.conf
ls /etc/security/limits.d/

For a systemd-managed service (Nginx, PHP-FPM, MySQL), the PAM limit is often irrelevant — systemd sets its own via DefaultTasksMax and per-unit TasksMax:

systemctl show php8.2-fpm | grep -i tasksmax
systemctl show -p DefaultTasksMax

3. Raise the limit properly

For a shell user or an application account on plain Linux, add a drop-in file rather than editing limits.conf directly (easier to track and remove later):

cat > /etc/security/limits.d/90-appuser-nproc.conf <<EOF
appuser soft nproc 4096
appuser hard nproc 8192
EOF

Log the user out and back in for it to apply — ulimit -u changes don't retroactively apply to an already-open session.

For a systemd service, don't touch PAM limits at all — override the unit instead:

systemctl edit php8.2-fpm.service
[Service]
TasksMax=2000
systemctl daemon-reload
systemctl restart php8.2-fpm

4. On cPanel/CloudLinux, raise the LVE nproc cap

If ulimit -u looks generous but the account still stalls, the LVE is the actual bottleneck. In WHM:

  • Go to WHM → Edit LVE and search for the account
  • Increase the Number of Processes (NPROC) value — a typical shared-hosting default is 20–25, which is far too low for a WordPress site running WP-CLI, cron, and PHP-FPM workers concurrently
  • Confirm the change took effect from the account's own shell: lveinfo $(whoami) or check WHM → Resource Usage

Don't just max this out for every account, though — a genuinely runaway process on a shared box will happily consume whatever ceiling you give it and starve its neighbours. Raise it enough to cover legitimate peak usage, not to make the symptom disappear forever.

5. If it's a fork bomb or malware, kill it before raising anything

ps -u accountuser -o pid,ppid,cmd --sort=start_time | tail -50
kill -9 <offending PID>
# or, to nuke everything owned by one user in an emergency:
pkill -9 -u accountuser

Then check crontab, wp-content, and any recently modified PHP files for the thing that spawned it — raising the nproc limit on top of active malware just buys the attacker more headroom.

Prevention

  • Cap PHP-FPM sanely. Set pm.max_children based on available RAM per worker, not an arbitrary big number — this keeps you from ever approaching the LVE cap under normal load.
  • Fix overlapping WP-Cron runs. If a site's cron takes longer than the interval between visits, disable the default WP-Cron and drive it from a real system cron job once a minute instead — it prevents the pile-up that eats process slots.
  • Monitor process counts, not just CPU/RAM. Add a simple check to your monitoring (Netdata, a cron + mail script, whatever you already run) that alerts when a user's process count crosses 70% of its limit.
  • Review batch scripts. Any script that does system(), exec(), or spawns a subprocess per row/file should reuse a small worker pool instead of forking once per item.
  • Set per-service TasksMax deliberately on general-purpose VPS boxes so one misbehaving service can't starve the others even before the OS-wide ceiling is reached.

FAQ

Frequently asked questions

Is "too many open files" the same problem as this one?

No. Open files (<code>ulimit -n</code>) limits file descriptors — sockets, log handles, pipes. This is <code>ulimit -u</code>, which caps the number of processes/threads a user can run. They're configured in the same <code>limits.conf</code> file but are completely independent ceilings, and the fixes don't overlap.

I raised ulimit -u but the error still happens on my cPanel account. Why?

On CloudLinux servers, cPanel accounts run inside an LVE with its own nproc cap set in WHM, separate from the OS-wide PAM limit. Raise the NPROC value under WHM → Edit LVE for that account, not just the shell ulimit.

Will raising the process limit fix my server permanently?

Only if the process count was legitimately too low for real traffic. If a fork bomb, malware, or an overlapping cron loop caused the spike, raising the limit just delays the same crash at a higher number — find and fix the root cause first.

Why does ulimit -u show a different number when I check it vs. what's in limits.conf?

limits.conf changes only apply to new login sessions created after the file is edited. If you're checking from a shell that was already open, or from a service started before the change, you'll see the old value until you log out/in or restart the service.

Does this affect systemd services the same way as SSH logins?

Not directly. Systemd-managed services (Nginx, PHP-FPM, MySQL) generally ignore PAM's limits.conf and use their own TasksMax setting instead, inherited from DefaultTasksMax unless overridden per-unit. Check both when a service — not a login shell — hits the limit.

#vps #nproc #ulimit #fork-error #cloudlinux #process-limit

Keep reading

Chat with Support