What limits concurrent connections on Linux?

Check process descriptors, application pools, listen queues, source ports, and memory before raising limits.

Best practices
Illustration of application modules working together

There is no single Linux setting for maximum connections. The effective limit comes from the application, its processes, the network path, and the resources each connection uses. First distinguish established connections from new-connection bursts and outbound connection creation.

Inspect the running process's descriptor limits

A socket held open by an application normally occupies a file descriptor, alongside files, pipes, and other descriptors:

ulimit -n
cat /proc/1234/limits
cat /proc/sys/fs/file-max
cat /proc/sys/fs/file-nr

Replace 1234 with the service PID. ulimit reports the current shell's limit, which may differ from the running service's. A temporary shell change does not update an existing process.

PAM login limits and systemd service limits also differ. For a systemd-managed service, inspect LimitNOFILE and use an appropriate unit override when a measured descriptor limit needs to change. Reload the unit definitions and restart the affected process under the new limit; daemon-reload alone does not change a running process's limits. Verify the result in /proc/<pid>/limits.

Distinguish queues from established-connection capacity

Read the relevant kernel values with sysctl:

sysctl net.core.somaxconn
sysctl net.ipv4.tcp_max_syn_backlog
sysctl net.core.netdev_max_backlog
sysctl net.ipv4.ip_local_port_range

The accept backlog, incomplete-handshake backlog, and network input backlog serve different stages. Increasing one does not create a universal capacity increase. Compare drops, retransmissions, and application accept behavior with the observed workload before tuning.

Outbound connections can exhaust the available source-address/source-port combinations for a destination, particularly with heavy churn or NAT. Established TCP connections are distinguished by their endpoint tuple; a listening server is not limited to 65,535 clients simply because its listening port is a 16-bit value.

Avoid changing tcp_tw_reuse blindly. Its meaning and defaults are kernel-dependent, and reuse must preserve TCP correctness. Connection pooling, keep-alive, and reducing needless churn are often the first application-level checks.

Account for the server and upstream connections

Nginx's worker_connections counts connections handled by each worker, including upstream connections. A proxied request can consume both client and upstream sockets, plus other descriptors. Multiplying workers by worker_connections is therefore not a direct estimate of supported users.

Apache capacity depends on its MPM and worker limits. PHP-FPM pools, database pools, application queues, and downstream APIs impose their own bounds. An event-driven Node.js process is not limited to one connection simply because JavaScript execution is typically on one thread.

Measure resource and path limits

Memory usage depends on socket buffers, TLS state, protocol, and application data. CPU, scheduling, bandwidth, connection tracking, load balancers, and NAT devices can impose earlier limits. There is no reliable universal memory figure for 100,000 connections.

Start with ss -s, application pool metrics, process descriptor usage, and the original error. Check the component returning the failure, then work outward through server limits and infrastructure. Test a justified change under representative load and keep rollback values. A larger limit can simply move the failure to memory exhaustion or an overloaded database.

References: Nginx worker_connections, systemd resource limits, and Linux IP sysctls.

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting