When trying to reuse an upstream group with different ports in multiple server blocks, Nginx throws the error: upstream "production" may not have port 1234
. This occurs despite the documentation suggesting variables should work for server names and ports in proxy_pass directives.
The key misunderstanding lies in how Nginx processes variables in proxy_pass when using upstream groups. While the documentation states variables can be used, there's an important nuance:
# This WON'T work as expected with upstream groups
set $upstream_host production;
set $upstream_port 1234;
proxy_pass http://$upstream_host:$upstream_port;
When variables are used, Nginx bypasses the upstream group resolution and tries to resolve the hostname through DNS instead.
Option 1: Separate Upstream Blocks
The most straightforward solution is to define separate upstream blocks for each port:
upstream production_1234 {
server 10.240.0.26:1234;
server 10.240.0.27:1234;
}
upstream production_4321 {
server 10.240.0.26:4321;
server 10.240.0.27:4321;
}
server {
listen 80;
server_name some.host;
location / {
proxy_pass http://production_1234;
}
}
server {
listen 80;
server_name other.host;
location / {
proxy_pass http://production_4321;
}
}
Option 2: Rewrite with Maps
For more dynamic configurations, you can use the map module:
map $host $backend {
some.host "10.240.0.26:1234,10.240.0.27:1234";
other.host "10.240.0.26:4321,10.240.0.27:4321";
}
server {
listen 80;
server_name some.host other.host;
location / {
set $target_backend $backend;
proxy_pass http://$target_backend;
}
}
If you're using OpenResty or have the Lua module installed, you can implement more complex routing:
server {
listen 80;
set_by_lua $backend_port '
if ngx.var.host == "some.host" then
return "1234"
else
return "4321"
end
';
location / {
proxy_pass http://production:$backend_port;
}
}
Note that this requires additional module configuration and isn't suitable for all environments.
While the separate upstream blocks approach might seem redundant, it actually offers better performance:
- Nginx can optimize connection pooling per upstream group
- Health checks can be configured per port
- Load balancing metrics remain separate for different services
Watch out for these issues when implementing multi-port upstreams:
- Ensure all servers in the upstream block are actually listening on the specified ports
- Remember that Nginx won't automatically retry failed connections to different ports
- Be consistent with protocol (http/https) across all servers in an upstream group
Many developers encounter this error when trying to reuse upstream definitions with different ports:
upstream "production" may not have port 1234
The fundamental limitation stems from how Nginx handles upstream groups - the port is considered part of the server definition rather than being a runtime parameter.
When using variables in proxy_pass, Nginx behaves differently than expected in the documentation. The key observation:
*16 no resolver defined to resolve production
This occurs because Nginx performs DNS resolution when:
- The upstream name comes from a variable
- The port is specified separately
Option 1: Separate Upstream Definitions
upstream production_1234 {
server 10.240.0.26:1234;
server 10.240.0.27:1234;
}
upstream production_4321 {
server 10.240.0.26:4321;
server 10.240.0.27:4321;
}
Option 2: Dynamic Port Mapping with Map Module
map $host $backend_port {
"some.host" 1234;
"other.host" 4321;
default 8080;
}
upstream production {
server 10.240.0.26;
server 10.240.0.27;
}
server {
location / {
proxy_pass http://production:$backend_port;
}
}
If you absolutely need variable-based port selection, you must:
resolver 8.8.8.8; # Specify DNS resolver
set $upstream_host "production";
set $upstream_port "1234";
location / {
proxy_pass http://$upstream_host:$upstream_port;
}
Each solution has trade-offs:
- Multiple upstreams: More memory usage but best performance
- Resolver approach: Adds DNS lookup overhead
- Map module: Cleanest syntax with minimal overhead
For production environments, Option 1 (separate upstreams) typically provides the most reliable performance.