diff --git a/Dockerfile-alpine.template b/Dockerfile-alpine.template
new file mode 100644
index 0000000000000000000000000000000000000000..56e62f36c5cc5672b97953870a0a56872a211f3e
--- /dev/null
+++ b/Dockerfile-alpine.template
@@ -0,0 +1,114 @@
+FROM alpine:%%ALPINE_VERSION%%
+
+LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
+
+ENV NGINX_VERSION %%NGINX_VERSION%%
+ENV NJS_VERSION   %%NJS_VERSION%%
+ENV PKG_RELEASE   %%PKG_RELEASE%%
+
+RUN set -x \
+# create nginx user/group first, to be consistent throughout docker variants
+    && addgroup -g 101 -S nginx \
+    && adduser -S -D -H -u 101 -h /var/cache/nginx -s /sbin/nologin -G nginx -g nginx nginx \
+    && apkArch="$(cat /etc/apk/arch)" \
+    && nginxPackages="%%PACKAGES%%
+    " \
+    && case "$apkArch" in \
+        x86_64) \
+# arches officially built by upstream
+            set -x \
+            && KEY_SHA512="e7fa8303923d9b95db37a77ad46c68fd4755ff935d0a534d26eba83de193c76166c68bfe7f65471bf8881004ef4aa6df3e34689c305662750c0172fca5d8552a *stdin" \
+            && apk add --no-cache --virtual .cert-deps \
+                openssl \
+            && wget -O /tmp/nginx_signing.rsa.pub https://nginx.org/keys/nginx_signing.rsa.pub \
+            && if [ "$(openssl rsa -pubin -in /tmp/nginx_signing.rsa.pub -text -noout | openssl sha512 -r)" = "$KEY_SHA512" ]; then \
+                echo "key verification succeeded!"; \
+                mv /tmp/nginx_signing.rsa.pub /etc/apk/keys/; \
+            else \
+                echo "key verification failed!"; \
+                exit 1; \
+            fi \
+            && apk del .cert-deps \
+            && apk add -X "%%PACKAGEREPO%%v$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" --no-cache $nginxPackages \
+            ;; \
+        *) \
+# we're on an architecture upstream doesn't officially build for
+# let's build binaries from the published packaging sources
+            set -x \
+            && tempDir="$(mktemp -d)" \
+            && chown nobody:nobody $tempDir \
+            && apk add --no-cache --virtual .build-deps \
+                gcc \
+                libc-dev \
+                make \
+                openssl-dev \
+                pcre-dev \
+                zlib-dev \
+                linux-headers \
+                libxslt-dev \
+                gd-dev \
+                geoip-dev \
+                perl-dev \
+                libedit-dev \
+                mercurial \
+                bash \
+                alpine-sdk \
+                findutils \
+            && su nobody -s /bin/sh -c " \
+                export HOME=${tempDir} \
+                && cd ${tempDir} \
+                && hg clone https://hg.nginx.org/pkg-oss \
+                && cd pkg-oss \
+                && hg up %%REVISION%% \
+                && cd alpine \
+                && make all \
+                && apk index -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk \
+                && abuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \
+                " \
+            && cp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/ \
+            && apk del .build-deps \
+            && apk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages \
+            ;; \
+    esac \
+# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)
+    && if [ -n "$tempDir" ]; then rm -rf "$tempDir"; fi \
+    && if [ -n "/etc/apk/keys/abuild-key.rsa.pub" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi \
+    && if [ -n "/etc/apk/keys/nginx_signing.rsa.pub" ]; then rm -f /etc/apk/keys/nginx_signing.rsa.pub; fi \
+# Bring in gettext so we can get `envsubst`, then throw
+# the rest away. To do this, we need to install `gettext`
+# then move `envsubst` out of the way so `gettext` can
+# be deleted completely, then move `envsubst` back.
+    && apk add --no-cache --virtual .gettext gettext \
+    && mv /usr/bin/envsubst /tmp/ \
+    \
+    && runDeps="$( \
+        scanelf --needed --nobanner /tmp/envsubst \
+            | awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \
+            | sort -u \
+            | xargs -r apk info --installed \
+            | sort -u \
+    )" \
+    && apk add --no-cache $runDeps \
+    && apk del .gettext \
+    && mv /tmp/envsubst /usr/local/bin/ \
+# Bring in tzdata so users could set the timezones through the environment
+# variables
+    && apk add --no-cache tzdata \
+# Bring in curl and ca-certificates to make registering on DNS SD easier
+    && apk add --no-cache curl ca-certificates \
+# forward request and error logs to docker log collector
+    && ln -sf /dev/stdout /var/log/nginx/access.log \
+    && ln -sf /dev/stderr /var/log/nginx/error.log \
+# create a docker-entrypoint.d directory
+    && mkdir /docker-entrypoint.d
+
+COPY docker-entrypoint.sh /
+COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
+COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
+ENTRYPOINT ["/docker-entrypoint.sh"]
+
+EXPOSE 80
+
+STOPSIGNAL SIGTERM
+
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/Dockerfile-debian.template b/Dockerfile-debian.template
new file mode 100644
index 0000000000000000000000000000000000000000..ca89e8f991e00286055b1d5ac60fe4cb833464e8
--- /dev/null
+++ b/Dockerfile-debian.template
@@ -0,0 +1,105 @@
+FROM debian:%%DEBIAN_VERSION%%-slim
+
+LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
+
+ENV NGINX_VERSION   %%NGINX_VERSION%%
+ENV NJS_VERSION     %%NJS_VERSION%%
+ENV PKG_RELEASE     %%PKG_RELEASE%%
+
+RUN set -x \
+# create nginx user/group first, to be consistent throughout docker variants
+    && addgroup --system --gid 101 nginx \
+    && adduser --system --disabled-login --ingroup nginx --no-create-home --home /nonexistent --gecos "nginx user" --shell /bin/false --uid 101 nginx \
+    && apt-get update \
+    && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 ca-certificates \
+    && \
+    NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \
+    found=''; \
+    for server in \
+        ha.pool.sks-keyservers.net \
+        hkp://keyserver.ubuntu.com:80 \
+        hkp://p80.pool.sks-keyservers.net:80 \
+        pgp.mit.edu \
+    ; do \
+        echo "Fetching GPG key $NGINX_GPGKEY from $server"; \
+        apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \
+    done; \
+    test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
+    apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \
+    && dpkgArch="$(dpkg --print-architecture)" \
+    && nginxPackages="%%PACKAGES%%
+    " \
+    && case "$dpkgArch" in \
+        amd64|i386) \
+# arches officialy built by upstream
+            echo "deb %%PACKAGEREPO%% %%DEBIAN_VERSION%% nginx" >> /etc/apt/sources.list.d/nginx.list \
+            && apt-get update \
+            ;; \
+        *) \
+# we're on an architecture upstream doesn't officially build for
+# let's build binaries from the published source packages
+            echo "deb-src %%PACKAGEREPO%% %%DEBIAN_VERSION%% nginx" >> /etc/apt/sources.list.d/nginx.list \
+            \
+# new directory for storing sources and .deb files
+            && tempDir="$(mktemp -d)" \
+            && chmod 777 "$tempDir" \
+# (777 to ensure APT's "_apt" user can access it too)
+            \
+# save list of currently-installed packages so build dependencies can be cleanly removed later
+            && savedAptMark="$(apt-mark showmanual)" \
+            \
+# build .deb files from upstream's source packages (which are verified by apt-get)
+            && apt-get update \
+            && apt-get build-dep -y $nginxPackages \
+            && ( \
+                cd "$tempDir" \
+                && DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \
+                    apt-get source --compile $nginxPackages \
+            ) \
+# we don't remove APT lists here because they get re-downloaded and removed later
+            \
+# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
+# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies)
+            && apt-mark showmanual | xargs apt-mark auto > /dev/null \
+            && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \
+            \
+# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be)
+            && ls -lAFh "$tempDir" \
+            && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \
+            && grep '^Package: ' "$tempDir/Packages" \
+            && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
+# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")
+#   Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
+#   ...
+#   E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages  Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
+            && apt-get -o Acquire::GzipIndexes=false update \
+            ;; \
+    esac \
+    \
+    && apt-get install --no-install-recommends --no-install-suggests -y \
+                        $nginxPackages \
+                        gettext-base \
+                        curl \
+    && apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \
+    \
+# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)
+    && if [ -n "$tempDir" ]; then \
+        apt-get purge -y --auto-remove \
+        && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \
+    fi \
+# forward request and error logs to docker log collector
+    && ln -sf /dev/stdout /var/log/nginx/access.log \
+    && ln -sf /dev/stderr /var/log/nginx/error.log \
+# create a docker-entrypoint.d directory
+    && mkdir /docker-entrypoint.d
+
+COPY docker-entrypoint.sh /
+COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
+COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
+ENTRYPOINT ["/docker-entrypoint.sh"]
+
+EXPOSE 80
+
+STOPSIGNAL SIGTERM
+
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/entrypoint/10-listen-on-ipv6-by-default.sh b/entrypoint/10-listen-on-ipv6-by-default.sh
new file mode 100755
index 0000000000000000000000000000000000000000..832918a2767b9504cd63003ff87cdd73f8abeeda
--- /dev/null
+++ b/entrypoint/10-listen-on-ipv6-by-default.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+# vim:sw=4:ts=4:et
+
+set -e
+
+ME=$(basename $0)
+DEFAULT_CONF_FILE="etc/nginx/conf.d/default.conf"
+
+# check if we have ipv6 available
+if [ ! -f "/proc/net/if_inet6" ]; then
+    echo >&3 "$ME: error: ipv6 not available"
+    exit 0
+fi
+
+if [ ! -f "/$DEFAULT_CONF_FILE" ]; then
+    echo >&3 "$ME: error: /$DEFAULT_CONF_FILE is not a file or does not exist"
+    exit 0
+fi
+
+# check if the file can be modified, e.g. not on a r/o filesystem
+touch /$DEFAULT_CONF_FILE 2>/dev/null || { echo >&3 "$ME: error: can not modify /$DEFAULT_CONF_FILE (read-only file system?)"; exit 0; }
+
+# check if the file is already modified, e.g. on a container restart
+grep -q "listen  \[::]\:80;" /$DEFAULT_CONF_FILE && { echo >&3 "$ME: info: IPv6 listen already enabled"; exit 0; }
+
+if [ -f "/etc/os-release" ]; then
+    . /etc/os-release
+else
+    echo >&3 "$ME: error: can not guess the operating system"
+    exit 0
+fi
+
+echo >&3 "$ME: Getting the checksum of /$DEFAULT_CONF_FILE"
+
+case "$ID" in
+    "debian")
+        CHECKSUM=$(dpkg-query --show --showformat='${Conffiles}\n' nginx | grep $DEFAULT_CONF_FILE | cut -d' ' -f 3)
+        echo "$CHECKSUM  /$DEFAULT_CONF_FILE" | md5sum -c - >/dev/null 2>&1 || {
+            echo >&3 "$ME: info: /$DEFAULT_CONF_FILE differs from the packaged version"
+            exit 0
+        }
+        ;;
+    "alpine")
+        CHECKSUM=$(apk manifest nginx 2>/dev/null| grep $DEFAULT_CONF_FILE | cut -d' ' -f 1 | cut -d ':' -f 2)
+        echo "$CHECKSUM  /$DEFAULT_CONF_FILE" | sha1sum -c - >/dev/null 2>&1 || {
+            echo >&3 "$ME: info: /$DEFAULT_CONF_FILE differs from the packaged version"
+            exit 0
+        }
+        ;;
+    *)
+        echo >&3 "$ME: error: Unsupported distribution"
+        exit 0
+        ;;
+esac
+
+# enable ipv6 on default.conf listen sockets
+sed -i -E 's,listen       80;,listen       80;\n    listen  [::]:80;,' /$DEFAULT_CONF_FILE
+
+echo >&3 "$ME: Enabled listen on IPv6 in /$DEFAULT_CONF_FILE"
+
+exit 0
diff --git a/entrypoint/20-envsubst-on-templates.sh b/entrypoint/20-envsubst-on-templates.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4f330295b93267c5b3847b3d3620f89c35cdf010
--- /dev/null
+++ b/entrypoint/20-envsubst-on-templates.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+set -e
+
+ME=$(basename $0)
+
+auto_envsubst() {
+  local template_dir="${NGINX_ENVSUBST_TEMPLATE_DIR:-/etc/nginx/templates}"
+  local suffix="${NGINX_ENVSUBST_TEMPLATE_SUFFIX:-.template}"
+  local output_dir="${NGINX_ENVSUBST_OUTPUT_DIR:-/etc/nginx/conf.d}"
+
+  local template defined_envs relative_path output_path subdir
+  defined_envs=$(printf '${%s} ' $(env | cut -d= -f1))
+  [ -d "$template_dir" ] || return 0
+  if [ ! -w "$output_dir" ]; then
+    echo >&3 "$ME: ERROR: $template_dir exists, but $output_dir is not writable"
+    return 0
+  fi
+  find "$template_dir" -follow -type f -name "*$suffix" -print | while read -r template; do
+    relative_path="${template#$template_dir/}"
+    output_path="$output_dir/${relative_path%$suffix}"
+    subdir=$(dirname "$relative_path")
+    # create a subdirectory where the template file exists
+    mkdir -p "$output_dir/$subdir"
+    echo >&3 "$ME: Running envsubst on $template to $output_path"
+    envsubst "$defined_envs" < "$template" > "$output_path"
+  done
+}
+
+auto_envsubst
+
+exit 0
diff --git a/entrypoint/docker-entrypoint.sh b/entrypoint/docker-entrypoint.sh
new file mode 100755
index 0000000000000000000000000000000000000000..88732541bc9c5f80c5121195a97989d67b49ec7a
--- /dev/null
+++ b/entrypoint/docker-entrypoint.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+# vim:sw=4:ts=4:et
+
+set -e
+
+if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then
+    exec 3>&1
+else
+    exec 3>/dev/null
+fi
+
+if [ "$1" = "nginx" -o "$1" = "nginx-debug" ]; then
+    if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then
+        echo >&3 "$0: /docker-entrypoint.d/ is not empty, will attempt to perform configuration"
+
+        echo >&3 "$0: Looking for shell scripts in /docker-entrypoint.d/"
+        find "/docker-entrypoint.d/" -follow -type f -print | sort -n | while read -r f; do
+            case "$f" in
+                *.sh)
+                    if [ -x "$f" ]; then
+                        echo >&3 "$0: Launching $f";
+                        "$f"
+                    else
+                        # warn on shell scripts without exec bit
+                        echo >&3 "$0: Ignoring $f, not executable";
+                    fi
+                    ;;
+                *) echo >&3 "$0: Ignoring $f";;
+            esac
+        done
+
+        echo >&3 "$0: Configuration complete; ready for start up"
+    else
+        echo >&3 "$0: No files found in /docker-entrypoint.d/, skipping configuration"
+    fi
+fi
+
+exec "$@"
diff --git a/update.sh b/update.sh
new file mode 100755
index 0000000000000000000000000000000000000000..e0606bd0e3192ca54dd3b99b6d86fbfd5e7a4bc5
--- /dev/null
+++ b/update.sh
@@ -0,0 +1,148 @@
+#!/usr/bin/env bash
+set -Eeuo pipefail
+shopt -s nullglob
+
+cd "$(dirname "$(readlink -f "$BASH_SOURCE")")"
+
+declare branches=(
+    "stable"
+    "mainline"
+)
+
+declare -A nginx=(
+    [mainline]='1.19.4'
+    [stable]='1.18.0'
+)
+
+defaultnjs='0.4.4'
+declare -A njs=(
+    #[stable]='0.4.3'
+)
+
+defaultpkg='1'
+declare -A pkg=(
+    [stable]=2
+)
+
+defaultdebian='buster'
+declare -A debian=(
+    #[stable]='stretch'
+)
+
+defaultalpine='3.12'
+declare -A alpine=(
+    [stable]='3.11'
+)
+
+# When we bump njs version in a stable release we don't move the tag in the
+# mercurial repo.  This setting allows us to specify a revision to check out
+# when building alpine packages on architectures not supported by nginx.org
+defaultrev='${NGINX_VERSION}-${PKG_RELEASE}'
+declare -A rev=(
+    #[stable]='-r 500'
+)
+
+get_packages() {
+    local distro="$1"; shift;
+    local branch="$1"; shift;
+    local perl=
+    local r=
+    local sep=
+
+    case "$distro:$branch" in
+        alpine*:*)
+            r="r"
+            sep="."
+            ;;
+        debian*:stable)
+            sep="."
+            ;;
+        debian*:*)
+            sep="+"
+         ;;
+    esac
+
+    case "$distro" in
+        *-perl)
+            perl="nginx-module-perl"
+            ;;
+    esac
+
+    echo -n ' \\\n'
+    for p in nginx nginx-module-xslt nginx-module-geoip nginx-module-image-filter $perl; do
+        echo -n '        '"$p"'=${NGINX_VERSION}-'"$r"'${PKG_RELEASE} \\\n'
+    done
+    for p in nginx-module-njs; do
+        echo -n '        '"$p"'=${NGINX_VERSION}'"$sep"'${NJS_VERSION}-'"$r"'${PKG_RELEASE} \\'
+    done
+}
+
+get_packagerepo() {
+    local distro="${1%-perl}"; shift;
+    local branch="$1"; shift;
+
+    [ "$branch" = "mainline" ] && branch="$branch/" || branch=""
+
+    echo "https://nginx.org/packages/${branch}${distro}/"
+}
+
+get_packagever() {
+    local distro="${1%-perl}"; shift;
+    local branch="$1"; shift;
+    local suffix=
+
+    [ "${distro}" = "debian" ] && suffix="~${debianver}"
+
+    echo ${pkg[$branch]:-$defaultpkg}${suffix}
+}
+
+generated_warning() {
+    cat << __EOF__
+#
+# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh"
+#
+# PLEASE DO NOT EDIT IT DIRECTLY.
+#
+__EOF__
+}
+
+for branch in "${branches[@]}"; do
+    for variant in \
+        alpine{,-perl} \
+        debian{,-perl} \
+    ; do
+        echo "$branch: $variant"
+        dir="$branch/$variant"
+        variant="$(basename "$variant")"
+
+        [ -d "$dir" ] || continue
+
+        template="Dockerfile-${variant%-perl}.template"
+        { generated_warning; cat "$template"; } > "$dir/Dockerfile"
+
+        debianver="${debian[$branch]:-$defaultdebian}"
+        alpinever="${alpine[$branch]:-$defaultalpine}"
+        nginxver="${nginx[$branch]}"
+        njsver="${njs[${branch}]:-$defaultnjs}"
+        pkgver="${pkg[${branch}]:-$defaultpkg}"
+        revver="${rev[${branch}]:-$defaultrev}"
+
+        packagerepo=$(get_packagerepo "$variant" "$branch")
+        packages=$(get_packages "$variant" "$branch")
+        packagever=$(get_packagever "$variant" "$branch")
+
+        sed -i \
+            -e 's,%%ALPINE_VERSION%%,'"$alpinever"',' \
+            -e 's,%%DEBIAN_VERSION%%,'"$debianver"',' \
+            -e 's,%%NGINX_VERSION%%,'"$nginxver"',' \
+            -e 's,%%NJS_VERSION%%,'"$njsver"',' \
+            -e 's,%%PKG_RELEASE%%,'"$packagever"',' \
+            -e 's,%%PACKAGES%%,'"$packages"',' \
+            -e 's,%%PACKAGEREPO%%,'"$packagerepo"',' \
+            -e 's,%%REVISION%%,'"$revver"',' \
+            "$dir/Dockerfile"
+
+        cp -a entrypoint/*.sh "$dir/"
+
+    done
+done