When testing network-intensive applications on macOS Leopard (10.5), developers often hit the default file descriptor limit of 256. This becomes particularly problematic when:
- Stress-testing server applications
- Running high-concurrency benchmarks
- Developing scalable network services
Unlike Linux systems that use /etc/security/limits.conf
, macOS implements limits differently. The key components are:
// Check current limits in terminal:
$ ulimit -a
// Specifically for open files:
$ ulimit -n
For persistent changes, modify the system's launchd configuration:
#!/bin/bash
# Create or modify /etc/launchd.conf
echo "limit maxfiles 65536 65536" | sudo tee -a /etc/launchd.conf
After making changes, you'll need to reboot. For verification:
$ sysctl kern.maxfiles
$ sysctl kern.maxfilesperproc
For temporary increases during development sessions:
# Raise limit for current shell session
ulimit -n 4096
# Alternatively, run application with custom limits
launchctl limit maxfiles 65536
For daemons or background services, create a custom plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.your.app</string>
<key>ProgramArguments</key>
<array>
<string>/path/to/your/app</string>
</array>
<key>SoftResourceLimits</key>
<dict>
<key>NumberOfFiles</key>
<integer>65536</integer>
</dict>
</dict>
</plist>
Create a test script to verify your changes:
#!/usr/bin/perl
# filetest.pl
use IO::Handle;
my @handles;
for (my $i=0; $i<1000; $i++) {
open($handles[$i], '>', "/tmp/testfile.$i")
or die "Failed at $i: $!";
print {$handles[$i]} "test";
}
If you're coming from a Linux background like Debian where /etc/security/limits.conf
handles ulimits, macOS's approach will seem puzzling. The default 256 file descriptor limit (nofile
) often isn't enough for development or server applications.
Unlike Linux, macOS stores these settings in:
/etc/launchd.conf
/etc/sysctl.conf
But on modern macOS versions, you'll need to create a launchd property list file instead.
Create or edit this file (requires sudo):
sudo nano /Library/LaunchDaemons/limit.maxfiles.plist
Add this configuration:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>limit.maxfiles</string>
<key>ProgramArguments</key>
<array>
<string>launchctl</string>
<string>limit</string>
<string>maxfiles</string>
<string>65536</string>
<string>65536</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>ServiceIPC</key>
<false/>
</dict>
</plist>
After creating the file:
sudo chown root:wheel /Library/LaunchDaemons/limit.maxfiles.plist
sudo chmod 644 /Library/LaunchDaemons/limit.maxfiles.plist
sudo launchctl load -w /Library/LaunchDaemons/limit.maxfiles.plist
Check your current limits with:
ulimit -n
launchctl limit maxfiles
Both should now report your new higher limit (65536 in our example).
For quick testing without reboot:
ulimit -n 65536
But this only affects the current shell session.
MacOS has hard upper limits defined at kernel level. Check them with:
sysctl kern.maxfiles
sysctl kern.maxfilesperproc
To increase these, edit /etc/sysctl.conf
(create if doesn't exist):
kern.maxfiles=1048576
kern.maxfilesperproc=1048576
1. All users share these limits on macOS
2. Requires reboot for kernel-level changes
3. The launchd method persists across updates