Skip to content

Commit bfbedd6

Browse files
authored
Enable optional runtime DNS resolver for rotating upstream hosts (#31)
1 parent f081339 commit bfbedd6

File tree

5 files changed

+76
-1
lines changed

5 files changed

+76
-1
lines changed

Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ RUN apk add --no-cache \
1010
COPY src /
1111
ENV KEEPALIVE_TIMEOUT=65
1212
ENV PROXY_UWSGI=0
13+
ENV NGINX_RESOLVER=
14+
ENV UPSTREAM_RESOLVE=0
1315
ENV LISTEN_PORT=80
1416
ENV STATUS_LISTEN_PORT=8091
1517
ENV HEALTHCHECK_PATH="/lb-status/"

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ Pair nginx-proxy with your favorite upstream server (wsgi, uwsgi, asgi, et al.)
2020
| `STATUS_LISTEN_PORT` | nginx status port | No | 8091 | |
2121
| `UPSTREAM_SERVER` | Upstream server | Yes | | myapp:8080 fail_timeout=0, unix://mnt/server.sock |
2222
| `PROXY_REVERSE_URL` | Upstream server URL (Deprecated, please use UPSTREAM_SERVER) | No | | http://myapp:8080 |
23+
| `NGINX_RESOLVER` | Value for nginx `resolver` directive | No | auto* | `169.254.169.253 valid=60s` |
24+
| `UPSTREAM_RESOLVE` | Enable dynamic DNS resolution for the upstream (`resolve` parameter) | No | 0 | 1 |
2325
| `SERVER_NAME` | Allowed server names (hostnames) | Yes | | |
2426
| `SILENT` | Silence entrypoint output | No | | |
2527
| `STATIC_LOCATIONS` | Static asset mappings | No | | |
@@ -30,6 +32,20 @@ Pair nginx-proxy with your favorite upstream server (wsgi, uwsgi, asgi, et al.)
3032
| `LOG_ONLY_5XX` | only log 5XX HTTP status access events | No | 0 | 1 |
3133
| `WORKER_CONNECTIONS` | Set the number of allowed worker connections | No | 1024 | 2048 |
3234

35+
* Defaults to 169.254.169.253 valid=60s when `UPSTREAM_RESOLVE=1` and no custom resolver is provided.
36+
37+
### Handling DNS Changes
38+
39+
If your upstream service rotates IP addresses (for example when fronted by a load
40+
balancer), configure nginx to re-resolve the upstream host name by setting
41+
`NGINX_RESOLVER` to the DNS servers you want nginx to use and enabling
42+
`UPSTREAM_RESOLVE=1`. The value of `NGINX_RESOLVER` is passed directly to the
43+
[`resolver` directive][nginx-resolver], so you can include modifiers such as
44+
`valid=60s` or `ipv6=off`. When using a unix socket (`UPSTREAM_SERVER=unix://…`)
45+
the resolve option is ignored.
46+
If you leave `NGINX_RESOLVER` unset, nginx-proxy defaults to AWS's metadata
47+
resolver (`169.254.169.253 valid=60s`) whenever `UPSTREAM_RESOLVE=1`.
48+
3349
### Hosting Static Assets
3450

3551
Static files can be hosted from your proxied application by sharing a volume
@@ -93,3 +109,4 @@ Notable differences from the official [nginx container][]
93109
[gomplate]: https://docs.gomplate.ca/
94110
[uwsgi]: https://uwsgi-docs.readthedocs.io/en/latest/
95111
[nginx status]: https://nginx.org/en/docs/http/ngx_http_stub_status_module.html
112+
[nginx-resolver]: https://nginx.org/en/docs/http/ngx_http_core_module.html#resolver

src/etc/nginx/conf.d/default.conf.template

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ server {
99
{{ end }}
1010

1111
upstream app {
12-
server {{ .Env.UPSTREAM_SERVER }};
12+
{{- $upstream := .Env.UPSTREAM_SERVER -}}
13+
{{- $isUnixSocket := strings.HasPrefix $upstream "unix://" -}}
14+
{{- $resolve := and (eq .Env.UPSTREAM_RESOLVE "1") (not $isUnixSocket) -}}
15+
# Enable nginx runtime DNS re-resolution when targeting hostnames; unix sockets do not support `resolve`.
16+
server {{ $upstream }}{{ if $resolve }} resolve{{ end }};
1317
}
1418

1519
server {

src/etc/nginx/nginx.conf.template

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ http {
5353
default 0;
5454
}
5555

56+
{{- $explicitResolver := ne .Env.NGINX_RESOLVER "" -}}
57+
{{- $shouldResolve := eq .Env.UPSTREAM_RESOLVE "1" -}}
58+
{{ if or $explicitResolver $shouldResolve }}
59+
# Configure DNS servers for runtime upstream re-resolution.
60+
resolver {{ if $explicitResolver }}{{ .Env.NGINX_RESOLVER }}{{ else }}169.254.169.253 valid=60s{{ end }};
61+
{{ end }}
62+
5663
{{ if (eq .Env.LOG_ONLY_5XX "1") }}
5764
access_log /dev/stdout json_analytics if=$is5xx;
5865
{{ else }}

test/test.sh

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,51 @@ function fail {
77
exit 1
88
}
99

10+
RESOLVER=$(awk '/^nameserver/ {print $2; exit}' /etc/resolv.conf)
11+
if [ -n "$RESOLVER" ]; then
12+
rendered_default=$(LISTEN_PORT=8080 \
13+
SERVER_NAME="example.com" \
14+
HEALTHCHECK_PATH="/health" \
15+
NO_ACCESS_LOGS=0 \
16+
PROXY_UWSGI=0 \
17+
STATIC_LOCATIONS= \
18+
UPSTREAM_SERVER="service.internal:8080" \
19+
UPSTREAM_RESOLVE=1 \
20+
NGINX_RESOLVER="$RESOLVER valid=60s" \
21+
gomplate < /etc/nginx/conf.d/default.conf.template)
22+
23+
echo "$rendered_default" | grep -F "server service.internal:8080 resolve;" \
24+
|| fail "expected resolve parameter when UPSTREAM_RESOLVE=1"
25+
26+
rendered_nginx=$(LISTEN_PORT=8080 \
27+
SERVER_NAME="example.com" \
28+
HEALTHCHECK_PATH="/health" \
29+
NO_ACCESS_LOGS=0 \
30+
PROXY_UWSGI=0 \
31+
STATIC_LOCATIONS= \
32+
UPSTREAM_SERVER="service.internal:8080" \
33+
UPSTREAM_RESOLVE=1 \
34+
NGINX_RESOLVER="$RESOLVER valid=60s" \
35+
gomplate < /etc/nginx/nginx.conf.template)
36+
37+
echo "$rendered_nginx" | grep -F "resolver $RESOLVER valid=60s;" \
38+
|| fail "expected resolver directive in nginx.conf"
39+
40+
fallback_nginx=$(LISTEN_PORT=8080 \
41+
SERVER_NAME="example.com" \
42+
HEALTHCHECK_PATH="/health" \
43+
NO_ACCESS_LOGS=0 \
44+
PROXY_UWSGI=0 \
45+
STATIC_LOCATIONS= \
46+
UPSTREAM_SERVER="service.internal:8080" \
47+
UPSTREAM_RESOLVE=1 \
48+
NGINX_RESOLVER="" \
49+
gomplate < /etc/nginx/nginx.conf.template)
50+
51+
echo "$fallback_nginx" | grep -F "resolver 169.254.169.253 valid=60s;" \
52+
|| fail "expected default resolver when UPSTREAM_RESOLVE=1 and NGINX_RESOLVER is empty"
53+
fi
54+
1055
LISTEN_PORT="8080" \
1156
KEEPALIVE_TIMEOUT="65" \
1257
PROXY_REVERSE_URL="http://localhost:8081" \

0 commit comments

Comments
 (0)