When needing to proxy an entire range of ports (say 9000-9999) to equivalent ports on a backend server, the naive approach would be writing individual server blocks for each port. This quickly becomes unmanageable:
# Painful manual configuration example - DON'T DO THIS
server {
listen 9000;
proxy_pass backend:9000;
}
server {
listen 9001;
proxy_pass backend:9001;
}
# ...and 999 more blocks!
Nginx's stream module (available since version 1.9.0) supports port ranges in listen directives:
stream {
server {
listen 9000-9999;
proxy_pass backend:$server_port;
}
}
The magic happens with $server_port which dynamically captures the incoming port number and reuses it for the backend connection.
Here's a full working configuration with important optimizations:
worker_processes auto;
error_log /var/log/nginx/error.log;
events {
worker_connections 1024;
}
stream {
# TCP/UDP proxy for port range
server {
listen 9000-9999;
proxy_pass backend_server:$server_port;
proxy_timeout 60s;
proxy_connect_timeout 2s;
}
# Optional: UDP specific configuration
server {
listen 9000-9999 udp;
proxy_pass backend_server:$server_port;
proxy_timeout 60s;
proxy_responses 1;
}
}
http {
# Regular HTTP configuration
# ... (your existing HTTP config)
}
When dealing with 1000+ ports:
- Set
worker_rlimit_nofileto a high value (like 65535) in main context - Increase
worker_connectionsin events block - Consider separating TCP and UDP traffic if both are needed
- Monitor
netstat -tulnto verify all ports are bound
For more complex routing logic, you can use OpenResty's Lua module:
server {
listen 9000-9999;
access_by_lua_block {
ngx.var.backend_port = ngx.var.server_port
}
proxy_pass http://backend:$backend_port;
}
After configuration:
# Check bound ports
ss -tulnp | grep nginx
# Test connectivity
for port in {9000..9005}; do
echo "Testing port $port"
curl -v http://localhost:$port
done
When dealing with large port ranges (e.g., 9000-9999), manually configuring each port in Nginx would be tedious and inefficient. The goal is to proxy all traffic from ports 9000-9999 on the local machine to the same ports on a different IP address without writing thousands of lines of configuration.
The ngx_stream_core_module (introduced in Nginx 1.9.0) is perfect for this scenario. It allows TCP/UDP proxying without requiring individual server blocks for each port.
load_module /usr/lib/nginx/modules/ngx_stream_module.so;
Here's how to configure Nginx to handle the entire port range dynamically:
stream {
server {
listen 9000-9999;
proxy_pass backend_server:$server_port;
}
upstream backend_server {
server destination_ip:9000-9999;
}
}
For more complex scenarios where you need additional processing, you can use variables:
stream {
map $remote_port $backend_port {
default $remote_port;
}
server {
listen 9000-9999;
proxy_pass destination_ip:$backend_port;
}
}
When proxying large port ranges:
- Monitor connection limits (
worker_connections) - Consider using
reuseportfor better performance - Adjust kernel parameters for large numbers of sockets
Remember that:
- All ports in the range will be exposed
- Firewall rules should be configured accordingly
- Consider rate limiting if applicable
After implementing, test with:
nginx -t
systemctl restart nginx
Then verify connectivity to any port in the range (e.g., 9000, 9500, 9999).