SYSTEMS OPERATIONAL
VPS

"Too Many Open Files" on Your VPS: Fixing ulimit Limits

Getwebup 6 min read

Your VPS was running fine for months. Then, out of nowhere, Nginx starts throwing 500s, MySQL refuses new connections, or PHP-FPM workers just stop responding — and the only clue in the logs is a cryptic line: Too many open files. Nothing crashed from lack of RAM or disk. You just hit a ceiling nobody told you existed.

What "too many open files" actually means

Every process on Linux gets a limit on how many file descriptors it can hold open at once. A file descriptor isn't just a file on disk — it's also every open network socket, every log file handle, every pipe between processes. A busy Nginx server handling hundreds of concurrent connections, or a MySQL instance with dozens of open tables and client sockets, can burn through thousands of descriptors without you noticing.

The default per-process limit on most stock Linux images is 1024. That number was fine for a lightly loaded box in 2010. It is nowhere near enough for a modern WordPress + Redis + PHP-FPM stack under real traffic.

When a process hits its ceiling, it doesn't get more graceful about it — new connections just get refused, new files can't be created, and error logs fill up with variations of EMFILE or ENFILE.

Symptoms that point to a file descriptor limit

  • Nginx error log shows socket() failed (24: Too many open files)
  • MySQL error log shows Can't open file: (errno: 24 "Too many open files") or the server silently refuses connections despite max_connections looking fine
  • PHP-FPM workers log unable to open primary script even though the file clearly exists
  • Redis logs Error accepting a client connection: accept: Too many open files
  • The problem gets worse under load and clears up (temporarily) after a restart of the affected service

That last point is what throws people off. A service restart resets its open-file count to near zero, so the symptom disappears for a while and comes back once traffic builds back up. It looks like a fluke instead of a hard limit being hit.

Check your current limits

Start by checking the system-wide and per-process ceilings. SSH into the VPS and run:

ulimit -n

This shows the soft limit for the current shell session — usually 1024. That number does not tell you what a running service is actually using. For that, find the process ID and read its limits directly from /proc:

pgrep -f nginx
cat /proc/<PID>/limits | grep "Max open files"

To see how many descriptors a process is actually holding open right now:

ls -1 /proc/<PID>/fd | wc -l

If that number is sitting close to the "Max open files" value, you've found your bottleneck. Also check the kernel-wide ceiling, which caps every process combined:

cat /proc/sys/fs/file-max
cat /proc/sys/fs/file-nr

The three numbers in file-nr are: currently allocated descriptors, currently unused, and the max. If the first number is creeping up toward the third across your whole server, several services are competing for the same shrinking pool.

The fix: raise limits at every layer

This is the part people get wrong — there isn't one setting to change. Linux enforces limits at three separate layers, and a low value at any one of them overrides a higher value set elsewhere.

1. Kernel-wide limit

Edit /etc/sysctl.conf (or drop a file in /etc/sysctl.d/) and set a generous system-wide cap:

fs.file-max = 2097152

Apply it without a reboot:

sysctl -p

2. Per-user limits via limits.conf

Edit /etc/security/limits.conf and add lines for the user that runs your services (often www-data, nginx, mysql, or a custom deploy user):

www-data soft nofile 65535
www-data hard nofile 65535
mysql    soft nofile 65535
mysql    hard nofile 65535
* soft nofile 65535
* hard nofile 65535

If your services are managed by systemd (true for most modern VPS images), limits.conf is often ignored entirely for daemon processes, because systemd applies its own limits before PAM ever gets involved. This is the step people miss most often.

3. systemd service overrides

For each service, create an override rather than editing the shipped unit file:

systemctl edit nginx

Add:

[Service]
LimitNOFILE=65535

Repeat for mysql, php8.3-fpm (or your version), and redis-server as needed. Then reload systemd and restart the service:

systemctl daemon-reload
systemctl restart nginx

Confirm it took effect:

systemctl show nginx | grep LimitNOFILE
cat /proc/$(pgrep -f "nginx: master")/limits | grep "Max open files"

Service-specific settings worth knowing

ServiceWhat to checkWhere
Nginxworker_rlimit_nofile should match or exceed the systemd limit/etc/nginx/nginx.conf
MySQL/MariaDBopen_files_limit variable/etc/mysql/mariadb.conf.d/50-server.cnf
PHP-FPMpool rlimit_files/etc/php/8.3/fpm/pool.d/www.conf
Redismaxclients plus the OS limit above it/etc/redis/redis.conf

For Nginx specifically, set worker_rlimit_nofile explicitly in the main context so it doesn't just inherit whatever the shell happened to have:

worker_rlimit_nofile 65535;

events {
    worker_connections 8192;
}

For MySQL, check what it actually negotiated at startup — it will silently cap itself lower than your requested value if the OS won't allow it:

SHOW VARIABLES LIKE 'open_files_limit';

Prevention: catch it before it's a fire

  • Baseline your descriptor usage once things are stable, so you know what "normal" looks like per service.
  • Alert on it. If you're already running Netdata or a similar monitor, add a check on open file descriptors per process, not just CPU/RAM/disk.
  • Set limits deliberately, not by accident. Don't just blanket-set everything to unlimited — a runaway process leaking file handles should still hit a wall and get logged, rather than quietly consuming the entire kernel table and taking every other service down with it.
  • Re-check after OS or package upgrades. A distro upgrade or a fresh systemd unit file from a package update can silently reset your overrides back to defaults.

When this shows up after scaling traffic

If you've recently added a CDN, launched a marketing campaign, or moved several sites onto one VPS, this is exactly the kind of limit that only bites once real concurrency shows up in testing or staging never triggers it. Budget descriptor headroom the same way you budget RAM and CPU when you scale — it's cheap to raise and expensive to debug blind at 2 a.m.

Frequently asked questions

Why does restarting the service temporarily fix the error?

A restart resets the process's open file descriptor count to near zero, so it has headroom again. The underlying limit hasn't changed, so the error comes back once the process ramps back up to the same load. That's why it looks intermittent instead of a hard ceiling.

Is raising fs.file-max alone enough to fix this?

No. fs.file-max only raises the kernel-wide total across every process on the server. Each individual service also has its own per-process limit, set either through /etc/security/limits.conf (for non-systemd-managed processes) or a systemd LimitNOFILE override. You typically need to raise both layers.

Why doesn't editing /etc/security/limits.conf change anything for my Nginx or MySQL service?

If the service is started by systemd, which is true on most current VPS images, systemd applies its own resource limits before PAM's limits.conf ever takes effect for that process. You need a systemd override (systemctl edit <service> with a LimitNOFILE line) instead.

What's a safe value to set for LimitNOFILE?

65535 is a reasonable default for most small-to-mid VPS workloads and leaves plenty of headroom over the old 1024 default. Very high-traffic servers running many services can go higher, but pair any increase with monitoring so a leaking process gets flagged instead of silently eating the whole pool.

Can this happen even if my VPS has plenty of free RAM and disk space?

Yes. File descriptor limits are a completely separate resource from memory and disk. A server can have gigabytes of free RAM and disk and still hit this wall purely because of connection or file-handle concurrency, which is why it's easy to misdiagnose as a memory or disk issue at first.

#vps #ulimit #too-many-open-files #file-descriptors #systemd #linux

Keep reading

Chat with Support