What does EADDRNOTAVAIL actually mean?
Seeing Error: connect EADDRNOTAVAIL in your logs usually means your application is moving faster than your operating system can keep up with. In plain English: the OS has run out of local ports to assign to new outgoing connections. This bottleneck is technically called ephemeral port exhaustion.
By default, Linux sets aside a specific range of ports for outgoing traffic. If your app makes 5,000 rapid-fire API calls or database queries, those connections don't vanish instantly when finished. They linger in a TIME_WAIT state for 60 seconds. If you exhaust the available pool before the old ones expire, your app hits a wall.
Step 1: Diagnose the Port Usage
Don't start changing system variables blindly. First, verify that TIME_WAIT sockets are actually the culprit. Use the ss toolβit's much faster than the older netstat for high-traffic servers.
# Get a quick summary of socket statistics
ss -s
# Count exactly how many connections are stuck in TIME_WAIT
ss -tan | grep TIME-WAIT | wc -l
If the count is hovering around 25,000 or higher, you've definitely hit the limit. At this volume, the kernel simply cannot recycle ports fast enough to satisfy new requests.
Step 2: Expand the Ephemeral Port Range
Most Linux distributions ship with a conservative range, typically 32768 to 60999. This only gives you about 28,000 concurrent ports. On a busy microservice, you can burn through these in seconds.
Check your current limits with this command:
cat /proc/sys/net/ipv4/ip_local_port_range
To fix this, we can open the range from 1024 up to 65535. This more than doubles your capacity to roughly 64,000 ports. Apply it temporarily to test:
sudo sysctl -w net.ipv4.ip_local_port_range="1024 65535"
To make this change survive a reboot, add the following line to /etc/sysctl.conf:
net.ipv4.ip_local_port_range = 1024 65535
Step 3: Enable Safe TCP Socket Reuse
Expanding the range helps, but it doesn't solve the 60-second hang time for closed sockets. You can tell the kernel to reuse a socket in the TIME_WAIT state if it is safe from a protocol standpoint. This is a game-changer for high-concurrency environments.
Add this setting to /etc/sysctl.conf:
net.ipv4.tcp_tw_reuse = 1
Then, reload the settings:
sudo sysctl -p
A word of caution: You might see older tutorials suggesting net.ipv4.tcp_tw_recycle. Avoid it. It was actually removed from Linux kernels after version 4.12 because it breaks connections for users behind NAT (like mobile users or office networks). Stick to tcp_tw_reuse.
Step 4: Fix the Root Cause in Your Code
Infrastructure tuning is often just a band-aid. If your code opens and closes a new connection for every single task, you are wasting CPU cycles and networking resources. You should implement Connection Pooling or Keep-Alive headers instead.
- Node.js: Don't use the default global agent for high volume. Create a custom
http.AgentwithkeepAlive: trueandmaxSockets: Infinity. - Databases: Instead of connecting/disconnecting on every query, use a pooler like PgBouncer for Postgres or the built-in pooling logic in your ORM.
- Nginx: If you use Nginx as a reverse proxy, ensure you use
proxy_http_version 1.1;and clear theConnectionheader to keep upstream pipes open.
How to Verify the Fix
Once you've tuned the kernel and updated your app, keep an eye on the socket count. You want to see the error logs stay clean while the socket count stabilizes.
# Watch the count of TIME_WAIT connections update every second
watch -n 1 "ss -tan | grep TIME-WAIT | wc -l"
If the number stays well below your new 64,511 limit during peak traffic, you've successfully cleared the bottleneck.
Pro-Tips for Prevention
Networking issues often shift as you scale. When your application logic gets faster, the OS kernel becomes the new ceiling. If you are building complex architectures with multiple VPCs, keep a close eye on your IP planning.
I frequently use the Subnet Calculator on ToolCraft to map out CIDR blocks. It ensures network segments are large enough to handle thousands of hosts without IP exhaustion or overlapping ranges.
Consider these two final tweaks for extreme cases:
- Reduce the FIN-WAIT timeout: Drop
net.ipv4.tcp_fin_timeoutfrom 60 to 15. This forces the OS to release closed sockets much faster. - IP Aliasing: If one IP isn't enough, assign a second IP to your network interface. This gives you a whole new set of 64,000 ports to play with.

