DriversRecommendedOutdated drivers can make a good PC feel brokenScan driver issues before chasing fixes manually.Scan NowOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PC×
Skip to content
VGSources
Blog

How to Tune ulimit on a Linux Server (Shells, PAM Sessions, and systemd Services)

A practical guide to Linux ulimit tuning, covering temporary shell tests, persistent PAM login limits, systemd service directives, verification through /proc, kernel ceilings, and high-file-descriptor compatibility.
Length11 min Posted Quest giverVGSources Team
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Limits 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.

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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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 nofile or nproc.
  • 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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:

  1. The process lacks permission to raise its hard limit.
  2. The requested open-file limit exceeds the kernel’s fs.nr_open ceiling.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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/hard fields 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

“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

  1. Identify the failing process. Record its PID, user, parent or service unit, and the resource error in logs.
  2. Measure before changing. Capture /proc/<pid>/limits, current descriptor or process counts, and the relevant hard limit.
  3. Reproduce with a temporary shell test. Raise only the soft value up to the existing hard value and start an equivalent test instance.
  4. Select the launch layer. Use a shell for a short experiment, PAM for login-launched workloads, or systemd for a systemd unit.
  5. 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.
  6. Assess compatibility. Test descriptor APIs, especially if the soft open-file value will exceed 1024.
  7. Apply at the correct lifecycle point. Start a new login session or run daemon-reload followed by a service restart.
  8. Verify the new process. Read /proc/<pid>/limits and confirm the workload now has the intended value.
  9. Observe after the change. Watch descriptor counts, process creation failures, logs, latency, and memory during peak behavior.
  10. 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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More quests from Patch Notes

  1. How to Set Up a RedM (RDR2) Server in 2026: License Key, txAdmin and server.cfg, Step by StepBlog11min
  2. Best RedM (RDR2) Server Hosting in 2026: Comparing Four Hosts on Slots, Memory and PriceBlog10min
  3. How to Host a Mindustry Server in 2026: server-release.jar, Port 6567 and the Commands That MatterBlog7min
Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.