The ulimit command in Linux doesn’t actually set limits; it displays or modifies them for the current shell and its child processes.
Let’s see it in action. Imagine you’re running a web server that’s crashing because it’s opening too many files. You suspect a file descriptor limit.
# Check current open file descriptor limit for the current shell
ulimit -n
# Output might be something like:
# 1024
# Now, let's increase it for this shell and its children
ulimit -n 4096
# Verify the change
ulimit -n
# Output should now be:
# 4096
This command tells the operating system that for any process started from this specific shell session, the maximum number of file descriptors it can have open simultaneously is 4096. It’s a soft limit, meaning the process can request more up to the hard limit.
The problem ulimit solves is preventing runaway processes from consuming all system resources, like memory, CPU time, or file descriptors, which can destabilize the entire system. It acts as a safety net, defining boundaries for individual processes.
Internally, these limits are managed by the kernel. When a process attempts to perform an action that would exceed a limit (like opening a new file when the file descriptor limit is reached), the kernel intervenes and returns an error, typically EAGAIN or ENFILE for file descriptors, or ENOMEM for memory. The ulimit command is essentially a user-space interface to modify these kernel-imposed constraints.
There are two main types of limits: soft and hard.
- Soft limit: This is the limit that the kernel enforces for a process. A process can increase its soft limit up to the hard limit without special privileges.
- Hard limit: This is the ceiling for the soft limit. Only the superuser (root) can increase the hard limit. For unprivileged users, the hard limit acts as an absolute maximum.
You can see both by using the -H and -S flags:
# Check hard limit for open files
ulimit -Hn
# Check soft limit for open files
ulimit -Sn
If you run ulimit -n and get a value, that’s your soft limit. The hard limit is often higher and can be set by the system administrator.
To change the hard limit (which you usually can’t do unless you’re root), you’d use:
# As root, increase the hard limit for open files to 65536
ulimit -Hn 65536
After this, you could then set the soft limit for the current shell to any value up to 65536.
The ulimit command supports many resource types, not just file descriptors:
-c: core file size-d: data segment size-f: file size (maximum size of files that can be created)-i: number of pending signals-l: locked-in-memory address space-m: resident set size (memory usage)-n: number of open file descriptors-p: pipe size-q: POSIX message queues-r: real-time priority-s: stack segment size-t: CPU time (seconds)-u: number of processes (threads)-v: address space limit (virtual memory)-x: file locks
For example, to limit the number of processes a user can run:
# Limit to 256 processes
ulimit -u 256
And to see the maximum memory a process can allocate:
# Check virtual memory limit in kilobytes
ulimit -v
These limits are inherited. If a parent process sets a limit, its child processes will inherit that limit. However, a child process can only increase its soft limits up to the parent’s hard limits. It cannot increase its hard limits.
The most common way to make these ulimit settings persistent across reboots and for specific users or services is by editing the /etc/security/limits.conf file. This file allows you to define default limits for users and groups.
For instance, to set the maximum number of open files for all users in the developers group to 4096, you’d add:
@developers hard nofile 4096
@developers soft nofile 4096
And for a specific user, say nginx:
nginx hard nofile 65536
nginx soft nofile 65536
Here, nofile is the keyword for the open file descriptor limit. The hard and soft keywords specify which limit is being set, and 65536 is the value.
It’s crucial to understand that ulimit in the shell affects only that shell and its descendants. Changes made via /etc/security/limits.conf are typically applied by the PAM (Pluggable Authentication Modules) system when a user logs in or a service starts, and these are the preferred method for system-wide or service-specific configurations.
A subtle point is that a process can only raise its soft limit up to the value of its hard limit. If you set a hard limit of 1024 and a soft limit of 512, the process can later increase its soft limit to 1024, but not beyond. If you want a process to be able to increase its limits significantly, you need to ensure its hard limits are also set appropriately, usually by root.
The next thing you’ll likely encounter is how these limits interact with systemd services, as systemd has its own way of managing resource limits that can override or complement limits.conf.