Fix "Cannot Assign Requested Address" in Linux (Errno 99)

Deepak Prasad
Tested on RHEL 10.2 (Coughlan)
Package iproute 6.17.0-2.el10.x86_64
podman 5.6.0-6.el10_1.x86_64
Applies to RHEL, Rocky Linux, AlmaLinux, Fedora, Debian, Ubuntu, and other Linux distributions using the standard socket API
Privilege Normal user to inspect addresses and sockets; sudo to change service config, sysctl, or restart daemons
Scope Diagnose and fix Cannot assign requested address (EADDRNOTAVAIL, errno 99) for bind() and connect() failures. Covers wrong local IP, boot ordering, Docker host publish binds, ephemeral port exhaustion, IPv4/IPv6 mismatches, and intentional non-local binds. Does not treat port conflicts (EADDRINUSE) or SELinux denials (EACCES) as primary causes of errno 99.
Related guides ss command
sysctl command
Check open ports in Linux
systemctl command
sysctl config for high-performance servers

Cannot assign requested address is Linux error EADDRNOTAVAIL (errno 99). When an application gets errno 99 from bind(), it usually means it requested a local IP address that is not assigned to the host or an interface that does not exist. When an outbound connect() fails with the same message, Linux may also be unable to allocate an ephemeral source port under connection pressure. The fixes differ, so the first step is deciding which path you are on.


Quick diagnosis: why you get errno 99

Situation Most likely cause First check
Nginx, Apache, or another daemon fails on startup Configured listen IP is not local ip -br addr
docker run -p IP:PORT:PORT or Podman publish fails Host does not own IP ip -br addr
Works after boot but failed during early boot Address or interface not ready yet journalctl -b -u SERVICE, ip -br addr
SSH tunnel or local forward reports bind failure Wrong local IP or IPv4/IPv6 bind mismatch ssh -vvv, ip -br addr
Client, proxy, or API worker fails only under heavy load Ephemeral ports exhausted sysctl net.ipv4.ip_local_port_range, ss -s
Port already occupied Different error: normally EADDRINUSE (errno 98) ss -lntp — see check open ports
SELinux blocks a socket Different error class: normally EACCES audit logs — not a primary errno 99 cause
text
bind() failed with errno 99  →  check local IP / interface
connect() failed with errno 99  →  check ephemeral ports / connection churn

What does "Cannot assign requested address" mean?

Linux maps EADDRNOTAVAIL to the user-visible string Cannot assign requested address. For bind(), the kernel documents this as an attempt to use an address that is not local or does not exist on an interface on this machine.

That is a different failure from a busy port:

Errno Name Typical message Meaning
99 EADDRNOTAVAIL Cannot assign requested address Local address unavailable for this socket
98 EADDRINUSE Address already in use Port (or address/port pair) already bound

Applications surface the same wording in logs and stack traces. Nginx may print bind() to 192.168.1.100:80 failed (99: Cannot assign requested address). Python reports [Errno 99] Cannot assign requested address. Podman and Docker print bind: cannot assign requested address when the host publish address is wrong.


Check whether the IP address exists on the server

This is the main fix for server-side bind() failures. List addresses on every interface:

bash
ip -br addr

Sample output on a lab host with two interfaces:

output
lo               UNKNOWN        127.0.0.1/8 ::1/128 
enp0s3           UP             10.0.2.15/24 fd17:625c:f037:2:a00:27ff:fe2d:cd83/64 fe80::a00:27ff:fe2d:cd83/64 
enp0s8           UP             192.168.56.116/24 fe80::71bc:541f:4300:7820/64

Suppose a service is configured to listen on 192.168.1.100 but that address does not appear in the output above. The kernel rejects the bind because this host does not own that IP.

Your options:

  • Assign the correct address on the intended interface (static config, DHCP reservation, or cloud secondary IP).
  • Change the application to a local address that already exists, such as 192.168.56.116 in the example.
  • Listen on all IPv4 interfaces with 0.0.0.0 or omit the address and bind only the port — only when exposing the service on every interface matches your security design.

0.0.0.0 is not a universal fix. It changes which interfaces accept traffic and may expose a daemon more widely than a single-IP listen directive.


Fix application bind addresses

The socket rule is the same for Nginx, Apache, MySQL, Redis, SSH, and other daemons: the configured address must exist locally before bind() succeeds.

Nginx and web servers

A common misconfiguration:

nginx
server {
    listen 192.168.1.100:80;
    server_name example.com;
}

Compare 192.168.1.100 against ip -br addr. If it is missing, set listen to a local IP or to port-only form when appropriate:

nginx
server {
    listen 80;
    server_name example.com;
}

Validate syntax before reload:

bash
sudo nginx -t

Then restart or reload through systemctl. Apache uses Listen in the same way; databases use bind-address (MySQL) or bind (Redis). SSH uses ListenAddress in /etc/ssh/sshd_config. The file path changes; the address check does not.

SSH tunnels and local forwards

Local forward bind failures often trace to the same rule: the -L local address must be local. IPv6-only listeners ([::1], link-local scopes) need matching ip -6 addr output. Use ssh -vvv when the error does not name the address clearly.


Fix Docker and Podman "bind: cannot assign requested address"

Container publish syntax binds on the host first. If the host IP in -p is not assigned, the runtime fails before the container starts.

On RHEL 10.2 with Podman, publishing to a non-local host IP produces:

output
Error: cannot listen on the TCP port: listen tcp4 192.168.1.100:18080: bind: cannot assign requested address

Confirm which addresses the host owns:

bash
ip -br addr

Then either publish on an IP that appears in that list or omit the host IP when any interface is acceptable:

bash
podman run --rm -p 8080:80 quay.io/rockylinux/rockylinux:10

The same rule applies to docker run -p 192.168.50.20:8080:80 when 192.168.50.20 is not on the host.


Address exists but the service fails during boot

A valid static IP in config can still fail if the daemon starts before the interface or address is usable. The symptom is errno 99 in early boot logs, followed by success after systemctl restart SERVICE once networking is up.

Inspect boot-time service logs:

bash
journalctl -b -u nginx

Replace nginx with your unit name. Compare the failure timestamp with ip -br addr at login. If the address appears only after NetworkManager or cloud-init finishes, fix startup ordering: make the unit depend on network readiness, use a drop-in After= / Wants= on network-online.target where appropriate, or move address configuration earlier. The exact unit names vary by distribution; the diagnostic pattern is the same.


Check for ephemeral port exhaustion on outbound connections

connect() can return EADDRNOTAVAIL when Linux must pick an automatic source port but none is free in the ephemeral range. This shows up under high outbound connection rates: reverse proxies, load generators, API clients, and database pools opening many short-lived TCP sockets.

Read the local ephemeral range:

bash
sysctl net.ipv4.ip_local_port_range

Sample output:

output
net.ipv4.ip_local_port_range = 32768	60999

The default range contains about 28,000 candidate local port numbers for automatic TCP and UDP source-port allocation. Actual connection capacity depends on the destination addresses and ports, socket reuse behavior, reserved ports, and sockets already occupying usable local ports.

Summarize socket pressure:

bash
ss -s

Sample output:

output
Total: 695
TCP:   24 (estab 9, closed 5, orphaned 0, timewait 1)

Transport Total     IP        IPv6
RAW	  2         0         2        
UDP	  14        10        4        
TCP	  19        14        5

Count sockets in TIME-WAIT when you suspect churn:

bash
ss -tan state time-wait | wc -l

A high TIME-WAIT count indicates heavy connection churn, but the count alone does not prove ephemeral-port exhaustion. Correlate it with errno 99 failures, the configured ephemeral range, and connections to the affected destinations. Also inspect connections to the affected destination with ss -tan and the ss command filters you already use for port troubleshooting.


Fix ephemeral port exhaustion correctly

Start with application design, not sysctl:

  • Reuse TCP connections instead of opening one socket per request.
  • Enable HTTP keep-alive, connection pooling, or HTTP/2 where the protocol allows.
  • Cap parallel outbound workers and queue work when backends are saturated.
  • Review why sockets sit in TIME-WAIT — high counts are often normal after bursts, but endless one-shot connects are not.

Only after connection behavior is sane, review kernel limits. Check reserved ports before widening the range:

bash
sysctl net.ipv4.ip_local_reserved_ports

Sample output when nothing is reserved:

output
net.ipv4.ip_local_reserved_ports =

Reserved ports shrink the pool available to automatic source-port selection. Expanding ip_local_port_range to 1024 65535 on every host is a blunt instrument; tune only with measurement and document the change. See sysctl config for high-performance servers for broader tuning context.


Bind to a non-local IP with ip_nonlocal_bind (advanced)

By default Linux rejects binds to addresses that are not local:

bash
sysctl net.ipv4.ip_nonlocal_bind

Sample output:

output
net.ipv4.ip_nonlocal_bind = 0

Setting net.ipv4.ip_nonlocal_bind = 1 allows processes to bind IPv4 sockets to non-local addresses. That is useful in deliberate HA, proxy, or load-balancer designs where a floating or anycast IP will become local — not as a shortcut for a wrong listen IP in Nginx or Docker publish config. The kernel documents that enabling it can break applications that assume only local addresses succeed.


Troubleshoot IPv4 and IPv6 bind errors

An IPv6 bind such as [::1]:PORT fails with errno 99 when the address family or scope does not match the host. List IPv6 addresses:

bash
ip -6 addr show scope global

Sample output:

output
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
    inet6 fd17:625c:f037:2:a00:27ff:fe2d:cd83/64 scope global dynamic noprefixroute 
       valid_lft 86397sec preferred_lft 14397sec

See which services listen on IPv6:

bash
ss -lnt6

Sample output:

output
State  Recv-Q Send-Q                      Local Address:Port Peer Address:Port
LISTEN 0      128                                  [::]:22           [::]:*

When an application forces IPv6 but the environment is IPv4-only for that bind, align the config or use ssh -4 only for SSH cases where verbose logs show an IPv6 bind attempt — not as a blanket fix for every errno 99.


Identify bind() vs connect() with strace

When logs do not say which syscall failed, trace socket setup:

bash
strace -f -e trace=bind,connect python3 -c "import socket; socket.socket().bind(('192.168.1.100', 8080))"

Sample output:

output
bind(5, {sa_family=AF_INET, sin_port=htons(8080), sin_addr=inet_addr("192.168.1.100")}, 16) = -1 EADDRNOTAVAIL (Cannot assign requested address)

A bind line with EADDRNOTAVAIL points to local IP or interface configuration. A connect line with the same errno points toward ephemeral port exhaustion or source-address selection under load. Run the same filter against the real service binary or interpreter wrapper.


Common mistakes

Mistake Why it is wrong
Treating errno 99 as "port already in use" Busy ports normally return EADDRINUSE (errno 98)
Disabling SELinux first Denials are typically EACCES, not EADDRNOTAVAIL
Raising somaxconn for a missing IP Listen backlog does not create a local address
Setting ip_nonlocal_bind=1 to hide bad config Masks misconfiguration; use only for intentional designs
Widening ephemeral ports without fixing connection churn Symptom may return under the same load
Killing TIME_WAIT sockets Normal TCP teardown; not a substitute for pooling
Restarting the service repeatedly Does not fix a wrong bind IP or exhausted port range

References


Summary

Cannot assign requested address is errno 99 (EADDRNOTAVAIL). For listening services, compare the configured IP against ip -br addr and correct Nginx, SSH, database, or Docker publish bindings to addresses the host actually owns. When the IP is valid but boot fails, inspect journalctl -b against interface readiness. For outbound connect() failures under load, measure ephemeral port use with sysctl net.ipv4.ip_local_port_range and ss before tuning the kernel. Use strace when you need to separate bind() from connect(), and reserve ip_nonlocal_bind for architectures that intentionally bind before an address is local.


Frequently Asked Questions

1. What is errno 99 on Linux?

Errno 99 is EADDRNOTAVAIL. The kernel maps it to the message Cannot assign requested address. It means the requested local address is not available for the socket operation, not that the port is already occupied.

2. Is errno 99 the same as address already in use?

No. Address already in use is errno 98 (EADDRINUSE). Errno 99 (EADDRNOTAVAIL) usually means the IP is not assigned to this host, the interface is not ready, IPv6 setup does not match the bind attempt, or connect() could not allocate an ephemeral source port.

3. Why does Nginx report bind() failed (99 Cannot assign requested address)?

The listen directive names an IP that is not on any local interface. Run ip -br addr, correct the listen address to a local IP or to all interfaces on the intended port, then run nginx -t and restart the service.

4. Why does Docker say bind cannot assign requested address?

Published host binding uses an IP the host does not own, for example docker run -p 192.168.50.20:8080:80 when 192.168.50.20 is not on ip addr. Use an address assigned to the host or publish only the port with -p 8080:80.

5. Can connect() return EADDRNOTAVAIL?

Yes. Under heavy outbound connection load, Linux may fail to pick a free ephemeral source port from net.ipv4.ip_local_port_range and return errno 99. Fix connection reuse and pooling before widening the port range.

6. Should I enable net.ipv4.ip_nonlocal_bind to fix errno 99?

Only for intentional designs such as HA floating IPs or proxies that bind before an address moves. Do not enable it merely to mask a wrong listen IP in application config.
Omer Cakmak

Linux Administrator

Highly skilled at managing Debian, Ubuntu, CentOS, Oracle Linux, and Red Hat servers. Proficient in bash scripting, Ansible, and AWX central server management, he handles server operations on OpenStack, KVM, Proxmox, and VMware.

  • Debian
  • Ubuntu
  • Linux
  • Red Hat Enterprise Linux
  • Shell Script
  • System Administration