Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ RUN apk add --no-cache \
COPY src /
ENV KEEPALIVE_TIMEOUT=65
ENV PROXY_UWSGI=0
ENV NGINX_RESOLVER=
ENV UPSTREAM_RESOLVE=0
ENV LISTEN_PORT=80
ENV STATUS_LISTEN_PORT=8091
ENV HEALTHCHECK_PATH="/lb-status/"
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Pair nginx-proxy with your favorite upstream server (wsgi, uwsgi, asgi, et al.)
| `STATUS_LISTEN_PORT` | nginx status port | No | 8091 | |
| `UPSTREAM_SERVER` | Upstream server | Yes | | myapp:8080 fail_timeout=0, unix://mnt/server.sock |
| `PROXY_REVERSE_URL` | Upstream server URL (Deprecated, please use UPSTREAM_SERVER) | No | | http://myapp:8080 |
| `NGINX_RESOLVER` | Value for nginx `resolver` directive | No | auto* | `169.254.169.253 valid=60s` |
| `UPSTREAM_RESOLVE` | Enable dynamic DNS resolution for the upstream (`resolve` parameter) | No | 0 | 1 |
| `SERVER_NAME` | Allowed server names (hostnames) | Yes | | |
| `SILENT` | Silence entrypoint output | No | | |
| `STATIC_LOCATIONS` | Static asset mappings | No | | |
Expand All @@ -30,6 +32,20 @@ Pair nginx-proxy with your favorite upstream server (wsgi, uwsgi, asgi, et al.)
| `LOG_ONLY_5XX` | only log 5XX HTTP status access events | No | 0 | 1 |
| `WORKER_CONNECTIONS` | Set the number of allowed worker connections | No | 1024 | 2048 |

* Defaults to 169.254.169.253 valid=60s when `UPSTREAM_RESOLVE=1` and no custom resolver is provided.

### Handling DNS Changes

If your upstream service rotates IP addresses (for example when fronted by a load
balancer), configure nginx to re-resolve the upstream host name by setting
`NGINX_RESOLVER` to the DNS servers you want nginx to use and enabling
`UPSTREAM_RESOLVE=1`. The value of `NGINX_RESOLVER` is passed directly to the
[`resolver` directive][nginx-resolver], so you can include modifiers such as
`valid=60s` or `ipv6=off`. When using a unix socket (`UPSTREAM_SERVER=unix://…`)
the resolve option is ignored.
If you leave `NGINX_RESOLVER` unset, nginx-proxy defaults to AWS's metadata
resolver (`169.254.169.253 valid=60s`) whenever `UPSTREAM_RESOLVE=1`.

### Hosting Static Assets

Static files can be hosted from your proxied application by sharing a volume
Expand Down Expand Up @@ -93,3 +109,4 @@ Notable differences from the official [nginx container][]
[gomplate]: https://docs.gomplate.ca/
[uwsgi]: https://uwsgi-docs.readthedocs.io/en/latest/
[nginx status]: https://nginx.org/en/docs/http/ngx_http_stub_status_module.html
[nginx-resolver]: https://nginx.org/en/docs/http/ngx_http_core_module.html#resolver
6 changes: 5 additions & 1 deletion src/etc/nginx/conf.d/default.conf.template
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ server {
{{ end }}

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

server {
Expand Down
7 changes: 7 additions & 0 deletions src/etc/nginx/nginx.conf.template
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ http {
default 0;
}

{{- $explicitResolver := ne .Env.NGINX_RESOLVER "" -}}
{{- $shouldResolve := eq .Env.UPSTREAM_RESOLVE "1" -}}
{{ if or $explicitResolver $shouldResolve }}
# Configure DNS servers for runtime upstream re-resolution.
resolver {{ if $explicitResolver }}{{ .Env.NGINX_RESOLVER }}{{ else }}169.254.169.253 valid=60s{{ end }};
{{ end }}

{{ if (eq .Env.LOG_ONLY_5XX "1") }}
access_log /dev/stdout json_analytics if=$is5xx;
{{ else }}
Expand Down
45 changes: 45 additions & 0 deletions test/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,51 @@ function fail {
exit 1
}

RESOLVER=$(awk '/^nameserver/ {print $2; exit}' /etc/resolv.conf)
if [ -n "$RESOLVER" ]; then
rendered_default=$(LISTEN_PORT=8080 \
SERVER_NAME="example.com" \
HEALTHCHECK_PATH="/health" \
NO_ACCESS_LOGS=0 \
PROXY_UWSGI=0 \
STATIC_LOCATIONS= \
UPSTREAM_SERVER="service.internal:8080" \
UPSTREAM_RESOLVE=1 \
NGINX_RESOLVER="$RESOLVER valid=60s" \
gomplate < /etc/nginx/conf.d/default.conf.template)

echo "$rendered_default" | grep -F "server service.internal:8080 resolve;" \
|| fail "expected resolve parameter when UPSTREAM_RESOLVE=1"

rendered_nginx=$(LISTEN_PORT=8080 \
SERVER_NAME="example.com" \
HEALTHCHECK_PATH="/health" \
NO_ACCESS_LOGS=0 \
PROXY_UWSGI=0 \
STATIC_LOCATIONS= \
UPSTREAM_SERVER="service.internal:8080" \
UPSTREAM_RESOLVE=1 \
NGINX_RESOLVER="$RESOLVER valid=60s" \
gomplate < /etc/nginx/nginx.conf.template)

echo "$rendered_nginx" | grep -F "resolver $RESOLVER valid=60s;" \
|| fail "expected resolver directive in nginx.conf"

fallback_nginx=$(LISTEN_PORT=8080 \
SERVER_NAME="example.com" \
HEALTHCHECK_PATH="/health" \
NO_ACCESS_LOGS=0 \
PROXY_UWSGI=0 \
STATIC_LOCATIONS= \
UPSTREAM_SERVER="service.internal:8080" \
UPSTREAM_RESOLVE=1 \
NGINX_RESOLVER="" \
gomplate < /etc/nginx/nginx.conf.template)

echo "$fallback_nginx" | grep -F "resolver 169.254.169.253 valid=60s;" \
|| fail "expected default resolver when UPSTREAM_RESOLVE=1 and NGINX_RESOLVER is empty"
fi

LISTEN_PORT="8080" \
KEEPALIVE_TIMEOUT="65" \
PROXY_REVERSE_URL="http://localhost:8081" \
Expand Down