How Do SO_REUSEADDR and SO_REUSEPORT Differ?
You're writing a server application and you keep seeing SO_REUSEADDR and SO_REUSEPORT socket options. What's the difference between them, and when should you use each one?
TL;DR
SO_REUSEADDR allows binding to a port that's in TIME_WAIT state after a previous connection closed, and lets multiple sockets bind to the same port if they're on different IP addresses. SO_REUSEPORT allows multiple processes to bind to the exact same IP:port combination, with the kernel load-balancing connections between them. Use SO_REUSEADDR for quick server restarts and binding to multiple interfaces. Use SO_REUSEPORT for multi-process servers that want to share a port.
These socket options solve different problems in network programming, and understanding them helps you build more robust and efficient servers.
When you bind a socket to a port, the operating system tracks which ports are in use. These socket options modify the rules about port reuse.
SO_REUSEADDR: Reusing Ports in TIME_WAIT
When a TCP connection closes, it enters a TIME_WAIT state (typically 60-120 seconds) to handle delayed packets. During this time, the port is still technically "in use."
Without SO_REUSEADDR:
import socket
# First run works fine
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('0.0.0.0', 8080))
server.listen(5)
# ... server runs and then stops
# Second run immediately after fails:
# socket.error: [Errno 98] Address already in use
With SO_REUSEADDR:
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('0.0.0.0', 8080))
server.listen(5)
# Works immediately, even if port is in TIME_WAIT
This is the most common use case: allowing you to restart your server without waiting for TIME_WAIT to expire.
SO_REUSEADDR: Binding to Multiple Addresses
SO_REUSEADDR also allows binding to the same port on different IP addresses:
# Server 1: Bind to specific interface
sock1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock1.bind(('192.168.1.100', 8080))
# Server 2: Bind to different interface, same port
sock2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock2.bind(('192.168.1.101', 8080))
# Both succeed
This is useful when you have multiple network interfaces and want to run different services on each.
SO_REUSEPORT: Multiple Processes Sharing a Port
SO_REUSEPORT (Linux 3.9+) solves a different problem: allowing multiple processes to bind to the exact same address and port.
Without SO_REUSEPORT:
# Process 1
sock1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock1.bind(('0.0.0.0', 8080)) # Works
# Process 2
sock2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock2.bind(('0.0.0.0', 8080)) # Fails: Address already in use
With SO_REUSEPORT:
# Process 1
sock1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
sock1.bind(('0.0.0.0', 8080)) # Works
# Process 2
sock2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
sock2.bind(('0.0.0.0', 8080)) # Also works!
The kernel distributes incoming connections across all processes listening on the port.
Load Balancing with SO_REUSEPORT
When multiple processes use SO_REUSEPORT, the kernel automatically load-balances:
# worker.py
import socket
def start_worker(worker_id):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
sock.bind(('0.0.0.0', 8080))
sock.listen(100)
print(f"Worker {worker_id} listening on port 8080")
while True:
client, addr = sock.accept()
print(f"Worker {worker_id} handling connection from {addr}")
# Handle client...
client.close()
if __name__ == '__main__':
import sys
worker_id = sys.argv[1]
start_worker(worker_id)
Run multiple workers:
python worker.py 1 &
python worker.py 2 &
python worker.py 3 &
python worker.py 4 &
Incoming connections are distributed across all four workers by the kernel's load-balancing algorithm (usually based on a hash of the connection 4-tuple).
Practical Example: Web Server with Worker Processes
A simple multi-process HTTP server:
#!/usr/bin/env python3
import socket
import os
import sys
def handle_request(client_socket):
request = client_socket.recv(1024).decode()
response = (
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"\r\n"
f"Hello from process {os.getpid()}\n"
)
client_socket.sendall(response.encode())
client_socket.close()
def start_worker():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
server.bind(('0.0.0.0', 8080))
server.listen(100)
print(f"Worker {os.getpid()} started")
while True:
client, addr = server.accept()
handle_request(client)
if __name__ == '__main__':
num_workers = int(sys.argv[1]) if len(sys.argv) > 1 else 4
for _ in range(num_workers):
pid = os.fork()
if pid == 0: # Child process
start_worker()
sys.exit(0)
# Parent waits
os.wait()
Run it:
python3 multiprocess_server.py 4
Each HTTP request is handled by a different worker process.
When to Use Each Option
Use SO_REUSEADDR when:
- You want to restart your server quickly without waiting for TIME_WAIT
- You need to bind to the same port on different IP addresses
- You're writing any server that needs reliable restarts
Use SO_REUSEPORT when:
- You want multiple processes to share load on the same port
- You're implementing a multi-process server architecture
- You want kernel-level load balancing instead of accept serialization
Combining Both Options
You can use both together:
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
server.bind(('0.0.0.0', 8080))
This gives you both benefits: quick restarts and multi-process capability.
Platform Differences
SO_REUSEADDR behavior varies by operating system:
Linux:
- Allows binding to TIME_WAIT ports
- Allows binding to same port on different IPs
BSD/macOS:
- More permissive - can cause unexpected behavior
- Be cautious with wildcard binds (0.0.0.0)
Windows:
- Different semantics entirely
- Use
SO_EXCLUSIVEADDRUSEfor exclusive binding
SO_REUSEPORT is Linux-specific (and some BSD variants). It doesn't exist on Windows.
Security Considerations
SO_REUSEPORT has a security implication: any process can bind to a port if the first process used SO_REUSEPORT, potentially intercepting traffic.
To prevent this, the kernel requires:
- Same user ID (UID) for all processes using the port
- Or capabilities/privileges to override
Example of the problem:
# Malicious process trying to steal connections
evil_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
evil_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
evil_socket.bind(('0.0.0.0', 8080))
# Fails if not the same UID as the legitimate server
Performance Benefits
SO_REUSEPORT can improve performance by:
- Reducing lock contention: Each worker has its own accept queue
- CPU cache efficiency: Connections stick to the same CPU/process
- Parallel accept: Multiple processes can accept simultaneously
Benchmark comparison:
Single process with SO_REUSEADDR:
Requests/sec: 15,000
Four processes with SO_REUSEPORT:
Requests/sec: 58,000
The improvement comes from parallel processing and reduced serialization.
Example: Node.js Cluster with SO_REUSEPORT
Node.js cluster module uses SO_REUSEPORT under the hood (on Linux):
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
} else {
http.createServer((req, res) => {
res.writeHead(200);
res.end(`Hello from worker ${process.pid}\n`);
}).listen(8080);
}
Each worker binds to port 8080 using SO_REUSEPORT, and the kernel distributes connections.
Debugging Socket Options
Check if a port is using these options:
# On Linux, check socket options
ss -tlnp | grep 8080
# More detailed socket info
lsof -i :8080
# See socket details
cat /proc/net/tcp
In code, verify the options are set:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Check if option is set
reuse = sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)
print(f"SO_REUSEADDR: {reuse}") # 1 if enabled
Common Mistakes
Mistake 1: Forgetting SO_REUSEADDR in servers
Without it, you'll wait up to 2 minutes between restarts.
Mistake 2: Using SO_REUSEPORT without understanding load distribution
Connections aren't evenly distributed - they're hashed. Some workers may get more traffic.
Mistake 3: Assuming SO_REUSEPORT works everywhere
It's Linux 3.9+ specific. Check for availability:
import socket
if hasattr(socket, 'SO_REUSEPORT'):
print("SO_REUSEPORT available")
else:
print("SO_REUSEPORT not available")
SO_REUSEADDR lets you restart servers quickly and bind to multiple interfaces. SO_REUSEPORT lets you run multiple processes on the same port for parallel processing. Use SO_REUSEADDR for almost all servers, and add SO_REUSEPORT when you need multi-process load balancing.
We earn commissions when you shop through the links below.
DigitalOcean
Cloud infrastructure for developers
Simple, reliable cloud computing designed for developers
DevDojo
Developer community & tools
Join a community of developers sharing knowledge and tools
SMTPfast
Developer-first email API
Send transactional and marketing email through a clean REST API. Detailed logs, webhooks, and embeddable signup forms in one dashboard.
QuizAPI
Developer-first quiz platform
Build, generate, and embed quizzes with a powerful REST API. AI-powered question generation and live multiplayer.
Want to support DevOps Daily and reach thousands of developers?
Become a SponsorFound an issue?
Related Posts
Also worth your time on this topic
Can Two Different Sockets Share a TCP Port?
Understand how TCP port sharing works with SO_REUSEADDR and SO_REUSEPORT, when multiple sockets can bind to the same port, and the limitations you need to know.
TCP/IP Fundamentals
Explain the difference between TCP and UDP. When would you use each?
junior
Nginx Load Balancing and Reverse Proxy
Master load balancing and reverse proxy patterns with Nginx to build resilient, scalable infrastructure.
75 minutes