Use nginx upstream group with multiple ports

I would like to use the same upstream definition with multiple ports:

upstream production {
    server 10.240.0.26;
    server 10.240.0.27;
}
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;
    }
}

Using that configuration nginx -t throws: upstream "production" may not have port 1234 and returns with exitcode 1.

When I try to define the proxy_pass URL (or parts of) as a variable like:

set $upstream_url "http://production:1234"
set $upstream_host production;
set $upstream_port 1234;
proxy_pass $upstream_url;
# or
proxy_pass http://production:$upstream_port;
# or
proxy_pass http://$upstream:$upstream_port;

Nginx tries to resolve the name of my upstream via resolver:

 *16 no resolver defined to resolve production, client: ...., server: some.host, request: "GET / HTTP/1.1", host: "some.host"

For me, the proxy_pass doc sounds as if exactly that should not happen;

A server name, its port and the passed URI can also be specified using variables:

proxy_pass http://$host$uri; or even like this:

proxy_pass $request; In this case, the server name is searched among the described server groups, and, if not found, is determined using a resolver.

Tested with nginx versions:

  • 1.6
  • 1.9
  • 1.10
  • 1.11

Am I missing something? Is there a way to work around this?


Solution 1:

You must define port in every server entry in upstream. If you don't nginx will set it to 80. So server 10.240.0.26; actually means server 10.240.0.26:80;.

You could define several upstream blocks though:

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;
    }
}

Another option is to configure your local DNS to resolve hostname production to several IPs, and in this case nginx will use them all.

http://nginx.org/r/proxy_pass: If a domain name resolves to several addresses, all of them will be used in a round-robin fashion.

server {
    listen 80;
    server_name some.host;
    location / {
        proxy_pass http://production:1234;
    }
}

Solution 2:

When you use upstreams, the ports are defined in the upstream blocks:

upstream production {
    server 10.240.0.26:8080;
    server 10.240.0.27:8081;
}

In other words, nginx resolves proxy_pass argument either to a upstream group or a host:port pair. When you use a variable as argument, it only resolves to host:port pair.