The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →The correct way to tune ulimit is to change the layer that launches your workload. Use a shell command for a temporary experiment, PAM limits for interactive or login sessions, and systemd Limit* directives for services started by systemd. Check the limit on the actual process, keep the soft value at or below the hard value, and stay within the kernel ceiling. Configuration files do not alter processes that are already running.
Need a server to try this on? Antihost offers Linux VPS plans with NVMe storage, full root access and a 14-day free trial.
What ulimit controls
ulimit is a shell interface to a process’s resource limits. On Linux, those limits are represented by the RLIMIT_* values managed by the kernel. They include the number of open file descriptors, process and thread counts, stack size, core-dump size, locked memory, and other per-process resources.
As an Amazon Associate I earn from qualifying purchases.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsLimits belong to a process, not to the machine as a whole. A child created with fork() inherits its parent’s limits, and the values survive an execve() when the child replaces its program. This is why the command used to launch a program matters: a server receives the limits of the shell, login manager, container entrypoint, or service manager that started it.
There are two values for most resources:
- Soft limit: the value the process currently enforces. An unprivileged process can normally lower it and can raise it only as far as its hard limit.
- Hard limit: the ceiling for the soft value. Raising a hard limit requires the appropriate privilege, and the kernel may impose an additional maximum.
The most common setting is nofile, the maximum number of file descriptors a process may have open. Sockets, log files, pipes, event descriptors, and regular files all consume descriptors, so a busy network daemon can exhaust this limit even when CPU and memory remain available.
#1 Best Overall
Choose the layer that launches the workload
| Method | Scope | Persistence | When it takes effect | Privilege and inheritance |
|---|---|---|---|---|
Shell ulimit |
Current shell and children it launches | Temporary; ends with the shell | Immediately for that shell and subsequently started children | Soft value can be raised only up to the existing hard value; children inherit it |
/etc/security/limits.conf or /etc/security/limits.d/*.conf |
A login session handled by PAM | Persistent configuration, but per session rather than global | At the next login; existing sessions are unchanged | pam_limits.so must be in the relevant PAM stack; limits pass to processes launched in that session |
systemd unit Limit* |
The service process tree started by that unit | Persistent unit configuration | After daemon reload and service restart | Set by the service manager; inspect the resulting daemon rather than an unrelated terminal |
A value shown by ulimit -n in your terminal says nothing definitive about a daemon launched by systemd, a supervisor, a container runtime, or a separate login session. Always inspect the process that is failing.
Measure the current and effective limits first
Inspect an interactive shell
Run these commands in the same shell that will start the test program:
ulimit -a
ulimit -Sn # soft open-file limit
ulimit -Hn # hard open-file limit
-S selects the soft value and -H the hard value. The abbreviated -n form refers to open files. ulimit -a prints the shell’s other supported resource limits so you can identify a different bottleneck instead of raising nofile by guesswork.
Inspect a running service
For a process already running, read the kernel’s view directly:
cat /proc/<pid>/limits
Replace <pid> with the daemon’s process ID. The Max open files row shows soft and hard values as applied to that process. This check catches the common mistake of changing a login shell while the production process is launched elsewhere.
For a systemd unit, obtain its main process and inspect that exact process:
Free tools Windows power users keep installed
One-click scans. No signup required.
pid=$(systemctl show -p MainPID --value your-service.service)
cat /proc/$pid/limits | grep -i 'open files'
If the unit has multiple worker processes, inspect a worker as well; a supervisor may alter limits before an individual worker starts. Compare the observed values with the unit configuration and with the application’s own startup log.
Run a temporary shell test
A temporary change is the safest way to prove that a limit is the cause of a failure. In the test shell, raise the soft open-file limit to a value no higher than the current hard limit, then launch the program from that shell:
ulimit -n 65536
exec ./your-server
exec replaces the shell with the server, preserving the shell’s limits while avoiding an extra wrapper process. The setting affects that shell and every child it launches. Closing the terminal, starting a new shell, or launching the program through another manager returns you to that manager’s limits.
If the first command reports “operation not permitted,” check the hard limit before trying larger numbers:
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →ulimit -Hn
You cannot raise an unprivileged shell’s soft value above its hard value. A temporary test that works only after a privileged launch does not prove that an ordinary login or service account will receive the same setting; configure the actual launch layer before treating the result as production-ready.
Set limits for login sessions with PAM
Create a focused limits file
Linux-PAM’s pam_limits module reads /etc/security/limits.conf and files in /etc/security/limits.d/ when a configured login session is created. A dedicated drop-in is easier to audit than editing the main file. For an account named appuser, create /etc/security/limits.d/90-example.conf with:
appuser soft nofile 65536
appuser hard nofile 65536
The four fields are, in order, <domain> <type> <item> <value>:
- Domain: a user, a group prefixed with
@, or another domain form supported by the limits file. - Type:
soft,hard, or an applicable combined form. - Item: the resource name, such as
nofileornproc. - Value: the numeric limit or a supported special value.
Use the narrowest account or group that needs the capacity. A broad wildcard can unintentionally change limits for administrators, batch jobs, and unrelated applications.
Confirm PAM actually loads the file
The file is effective only when the login path includes pam_limits.so. Check the PAM configuration used by the relevant access method—such as a local console, SSH, or another display/login service—and confirm a line invoking that module exists. Different login paths can use different PAM stacks, so a setting that works over SSH may not apply to a service account started by a scheduler or systemd.
Start a new session
Log out completely and log in again before testing:
ulimit -Sn
ulimit -Hn
Opening another terminal window inside an existing graphical session may reuse the original session and therefore the old limits. Existing shells, daemons, and processes are never rewritten when a limits file changes. The settings apply per login session and last only for that session; they are not global kernel defaults.
Set limits for a systemd service
Use a unit drop-in
For a systemd-managed service, configure the unit rather than relying on a user’s login limits. Create a drop-in with:
sudo systemctl edit your-service.service
Add:
[Service]
LimitNOFILE=65536
LimitNOFILE= maps to the service’s RLIMIT_NOFILE, the same resource exposed as ulimit -n. Related directives include LimitNPROC= for process and thread limits, LimitSTACK= for stack size, LimitCORE= for core-dump size, and LimitMEMLOCK= for locked memory. Set only the directive that corresponds to the observed constraint.
Recommended Free Tools
Reload, restart, and verify
Apply the unit change and restart the service:
sudo systemctl daemon-reload
sudo systemctl restart your-service.service
pid=$(systemctl show -p MainPID --value your-service.service)
cat /proc/$pid/limits | grep -i 'open files'
daemon-reload makes systemd reread unit files; it does not change an already-running process. The restart is what gives the new limit to the service. If the service starts a separate worker manager, verify the worker’s /proc/<pid>/limits too.
A unit may also specify a value through a vendor unit file, another drop-in, or a generated configuration. Use systemctl cat your-service.service to see the assembled unit and systemctl show your-service.service to inspect manager properties, then treat /proc/<pid>/limits as the final authority for the running process.
Understand hard limits and kernel ceilings
There are two independent reasons a requested value can be refused:
Rank #4
- The process lacks permission to raise its hard limit.
- The requested open-file limit exceeds the kernel’s
fs.nr_openceiling.
Inspect both values when a large request fails:
ulimit -Hn
cat /proc/sys/fs/nr_open
An unprivileged process cannot raise its hard limit. Even a privileged process cannot set RLIMIT_NOFILE above /proc/sys/fs/nr_open. Raising a shell value also does not help a service that is launched with a lower hard limit; the new value must be delivered by the service’s manager or another privileged launch step.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11The kernel ceiling is distinct from the number of descriptors one process should use. A very large ceiling does not make an application safe or efficient. Keep the configured value close to the capacity the workload needs, with headroom for bursts and housekeeping descriptors.
Size a limit from workload evidence
There is no universal “correct” nofile or nproc number. Choose a value from measurements and the application’s documented behavior.
For open files
- Count steady-state descriptors for listening sockets, client connections, log files, pipes, event mechanisms, and helper processes.
- Measure the peak during realistic concurrency, deployment, log rotation, backups, and reconnect storms.
- Add operational headroom without jumping straight to
unlimited. - Check both the soft and hard values on the process that reaches the peak.
A limit that is barely above the normal count can fail during a short burst; a limit many orders of magnitude larger can mask a file-descriptor leak or an unexpectedly high connection count. Record the observed peak and the chosen margin so a later operator can distinguish intentional capacity from a temporary workaround.
For processes and threads
LimitNPROC= and the corresponding shell resource constrain process or thread creation. Account for the main daemon, worker pools, helper commands, crash handlers, and rolling-restart overlap. A service can hit this limit even when its open-file count is healthy, especially when several units share the same user or when a login account starts many jobs.
For stack, core, and locked memory
Use LimitSTACK=, LimitCORE=, and LimitMEMLOCK= only when the application’s behavior and diagnostics show those resources are constrained. Increasing every limit together makes it harder to identify the real bottleneck and can remove useful safety boundaries.
High descriptor numbers and select(2) compatibility
Raising the soft nofile limit above 1024 has a compatibility consequence on Linux: the select(2) API cannot operate on descriptors above 1023. A process may receive a high limit successfully and still fail or behave incorrectly if its code, a library, or an embedded component relies on select() with those descriptors.
Best Value
Before setting a high value, confirm that the application uses descriptor APIs and libraries that support the intended range, such as poll- or epoll-based mechanisms where appropriate. Test the real binary under realistic connection counts; a successful ulimit command alone does not demonstrate application compatibility.
Systemd’s documentation notes a historical hard nofile default of 524288 in modern configurations. That number is a documented default, not a recommendation for every service and not a substitute for measuring the workload. The 1024/1023 boundary is an API limitation, not a statement that every daemon must stay below 1024 descriptors.
Diagnose common failed changes
“I edited limits.conf, but ulimit is unchanged”
- Verify that the account is entering through a PAM stack containing
pam_limits.so. - Log out and create a genuinely new login session.
- Check for a later file in
/etc/security/limits.d/that overrides the value. - Confirm the rule’s domain, item name, and
soft/hardfields are correct.
Do not expect an existing shell or daemon to change in place.
“My terminal shows 65536, but the service still fails”
The service is probably not a child of that terminal. Inspect /proc/<pid>/limits, identify the launcher, and configure its limits. For systemd, use a unit drop-in, run daemon-reload, restart the unit, and verify the new main process.
“ulimit -n 1000000 says operation not permitted”
Compare the request with ulimit -Hn and cat /proc/sys/fs/nr_open. If the request exceeds the hard value, use a privileged launch configuration to raise both soft and hard limits. If it exceeds fs.nr_open, the kernel ceiling must be addressed first, and the resulting value still needs to be appropriate for the application.
“The service restarted, but the value reverted”
Inspect the effective unit with systemctl cat and check whether an override was created for the wrong unit name, a generated unit replaced it, or another drop-in sets a later value. Verify the new process after every restart; looking only at an old PID can produce a false result.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
“Increasing nofile introduced application errors”
Check whether the program or one of its libraries uses select(). Reproduce the problem with the same descriptor numbers and concurrency, then move to compatible polling APIs or keep the soft limit within the application’s supported range. Do not treat a lower limit as a fix for a descriptor leak; investigate the leak separately.
A repeatable production change procedure
- Identify the failing process. Record its PID, user, parent or service unit, and the resource error in logs.
- Measure before changing. Capture
/proc/<pid>/limits, current descriptor or process counts, and the relevant hard limit. - Reproduce with a temporary shell test. Raise only the soft value up to the existing hard value and start an equivalent test instance.
- Select the launch layer. Use a shell for a short experiment, PAM for login-launched workloads, or systemd for a systemd unit.
- Set both bounds deliberately. Keep the soft value no higher than the hard value and verify the requested open-file value is within
fs.nr_open. - Assess compatibility. Test descriptor APIs, especially if the soft open-file value will exceed 1024.
- Apply at the correct lifecycle point. Start a new login session or run
daemon-reloadfollowed by a service restart. - Verify the new process. Read
/proc/<pid>/limitsand confirm the workload now has the intended value. - Observe after the change. Watch descriptor counts, process creation failures, logs, latency, and memory during peak behavior.
- Document the reason and margin. Record the measured peak, selected limit, launch mechanism, and rollback method.
Rollback and safe operating habits
Keep configuration changes small and reversible. A systemd drop-in can be edited or removed, followed by daemon-reload and a restart. A PAM drop-in can be restored from version control or removed before starting a new login session. For an emergency shell test, close the shell or start a fresh one rather than trying to “undo” a child process’s inherited value.
- Change one resource at a time so the resulting behavior is attributable.
- Prefer a named service account or unit over a global wildcard.
- Do not set every resource to
unlimited; limits provide protection against leaks and runaway concurrency. - Keep the command output and the effective process values together in the change record.
- Recheck limits after package upgrades, unit-file changes, migration to a new supervisor, or a change in login method.
The durable rule is simple: tune the launcher, not an unrelated shell; verify the running process, not just the configuration text; and raise a limit only as far as measured demand and application compatibility justify.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




