Why this matters
Unix systems cap the number of open files per process. Since every network connection counts as an open file, k6 hits this limit at scale:
WARN[0127] Request Failed error="Get http://example.com/: dial tcp: socket: too many open files"
Estimate your required limit before changing anything: max VUs × HTTP requests per iteration gives a baseline connection count.
Warning
Change settings conservatively and verify the effect before/after with
netstatorss. Document what you changed and why — these settings affect all processes on the machine, not just k6.
Open file limits
Linux
View current limits:
ulimit -Sa # soft limits (per-session, user-configurable)
ulimit -Ha # hard limits (absolute max, root only)The default soft limit for open files is typically 1024. The hard limit is often 1048576.
Raise for the current session:
ulimit -n 250000Persist across sessions (add to ~/.bashrc):
echo "ulimit -n 250000" >> ~/.bashrcRaise the hard limit (root required) — edit /etc/security/limits.conf:
alice soft nofile 5000
alice hard nofile 1048576
# or for all non-root users:
* hard nofile 1048576
Changes take effect after logging out and back in.
macOS
View current limits:
launchctl limit maxfiles # per-process soft + hard limits
sysctl kern.maxfiles # system-wide total
sysctl kern.maxfilesperproc # per-process limitRaise for the current session:
sudo launchctl limit maxfiles 65536 200000Persist across reboots — create /Library/LaunchDaemons/limit.maxfiles.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>limit.maxfiles</string>
<key>ProgramArguments</key>
<array>
<string>launchctl</string><string>limit</string>
<string>maxfiles</string>
<string>64000</string>
<string>524288</string>
</array>
<key>RunAtLoad</key><true/>
<key>ServiceIPC</key><false/>
</dict>
</plist>Note
macOS requires disabling System Integrity Protection (SIP) before editing these daemon files. Boot into Recovery Mode (hold Cmd+R), open Terminal, run
csrutil disable, reboot. Re-enable withcsrutil enableafter changes are applied.
Local port range
Each outgoing TCP connection consumes one ephemeral port. The default range on Linux (~28k ports) can be exhausted at high VU counts.
Linux
View:
sysctl net.ipv4.ip_local_port_range
# default: 32768 60999Expand:
sysctl -w net.ipv4.ip_local_port_range="1024 65535"Persist by adding to /etc/sysctl.conf:
net.ipv4.ip_local_port_range=16384 65000
macOS
View:
sysctl net.inet.ip.portrange.first net.inet.ip.portrange.last
# default: 49152–65535 (16384 ports)Expand to match Linux default:
sudo sysctl -w net.inet.ip.portrange.first=32768Warning
Expanding the range below 49152 means your machine may use ports like 32768–49151 as source ports on outgoing connections. Some firewalls only allow traffic whose source port falls within the IANA dynamic range (49152–65535) and will silently drop the rest. This is only a concern when testing through a firewall to a remote network (e.g. corporate network to staging). On a local network or within the same VPC it’s a non-issue.
TCP TIME_WAIT
After a connection closes, the kernel holds the socket in TIME_WAIT for 15s–2min to handle late-arriving packets. Under heavy load, sockets pile up in this state and prevent new connections.
Enable fast reuse of TIME_WAIT sockets (Linux):
sysctl -w net.ipv4.tcp_tw_reuse=1
sysctl -w net.ipv4.tcp_timestamps=1 # required for tw_reuse to work safelyAlternative — avoid TIME_WAIT congestion without kernel changes:
Spread requests across multiple server ports (:8080, :8081, :8082, …).
A socket is uniquely identified by a 4-tuple: (local IP, local port, remote IP, remote port). When connections close, each 4-tuple sits in TIME_WAIT and can’t be reused until the timer expires.
If all requests go to the same remote port (e.g. :8080), every new connection must use a different local port — you burn through your ephemeral port pool and it fills with TIME_WAIT sockets. By varying the remote port, the same local port can be reused: local port 45000 → :8080 and local port 45000 → :8081 are two distinct 4-tuples that don’t interfere with each other, effectively multiplying your available socket pool.
Memory
- Simple scripts: 1–5 MB per VU (1,000 VUs ≈ 1–5 GB)
- File upload scripts: tens of MB per VU — each VU gets its own copy of the file
To estimate: run the test locally at 100 VUs, measure memory consumed, scale up linearly.
To reduce memory usage, add --compatibility-mode=base (disables some JS features but cuts RAM significantly).
To share large data across VUs without copying, use SharedArray.
See also
- k6-large-tests — hardware thresholds, script optimisations, distributed execution
- k6-vu-saturation — diagnosing when the load generator is the bottleneck