diff --git a/Dockerfile-alpine.template b/Dockerfile-alpine.template
index eb623c2e8cbd95ca7aa39cc974f3c45a5492d48b..1ce4411ab4112b4100d355c8911cc4239fc0f659 100644
--- a/Dockerfile-alpine.template
+++ b/Dockerfile-alpine.template
@@ -105,6 +105,7 @@ RUN set -x \
 COPY docker-entrypoint.sh /
 COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
 COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
+COPY 30-tune-worker-processes.sh /docker-entrypoint.d
 ENTRYPOINT ["/docker-entrypoint.sh"]
 
 EXPOSE 80
diff --git a/Dockerfile-debian.template b/Dockerfile-debian.template
index 8521d5a9ea1d4fb2bdfb960568c6771e6952077e..91b7cf78c8e58d0b78a3afdb978884f893a65283 100644
--- a/Dockerfile-debian.template
+++ b/Dockerfile-debian.template
@@ -96,6 +96,7 @@ RUN set -x \
 COPY docker-entrypoint.sh /
 COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
 COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
+COPY 30-tune-worker-processes.sh /docker-entrypoint.d
 ENTRYPOINT ["/docker-entrypoint.sh"]
 
 EXPOSE 80
diff --git a/entrypoint/30-tune-worker-processes.sh b/entrypoint/30-tune-worker-processes.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4adb0432db5efc257de1d5d72a9b0a81f993f663
--- /dev/null
+++ b/entrypoint/30-tune-worker-processes.sh
@@ -0,0 +1,194 @@
+#!/bin/sh
+# vim:sw=2:ts=2:sts=2:et
+
+set -eu
+
+LC_ALL=C
+ME=$( basename "$0" )
+PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+
+[ "${NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE:-}" ] || exit 0
+
+touch /etc/nginx/nginx.conf 2>/dev/null || { echo >&2 "$ME: error: can not modify /etc/nginx/nginx.conf (read-only file system?)"; exit 0; }
+
+ceildiv() {
+  num=$1
+  div=$2
+  echo $(( (num + div - 1) / div ))
+}
+
+get_cpuset() {
+  cpusetroot=$1
+  cpusetfile=$2
+  ncpu=0
+  [ -f "$cpusetroot/$cpusetfile" ] || return
+  for token in $( tr ',' ' ' < "$cpusetroot/$cpusetfile" ); do
+    case "$token" in
+      *-*)
+        count=$( seq $(echo "$token" | tr '-' ' ') | wc -l )
+        ncpu=$(( ncpu+count ))
+        ;;
+      *)
+        ncpu=$(( ncpu+1 ))
+        ;;
+    esac
+  done
+  echo "$ncpu"
+}
+
+get_quota() {
+  cpuroot=$1
+  ncpu=0
+  [ -f "$cpuroot/cpu.cfs_quota_us" ] || return
+  [ -f "$cpuroot/cpu.cfs_period_us" ] || return
+  cfs_quota=$( cat "$cpuroot/cpu.cfs_quota_us" )
+  cfs_period=$( cat "$cpuroot/cpu.cfs_period_us" )
+  [ "$cfs_quota" = "-1" ] && return
+  [ "$cfs_period" = "0" ] && return
+  ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
+  [ "$ncpu" -gt 0 ] || return
+  echo "$ncpu"
+}
+
+get_quota_v2() {
+  cpuroot=$1
+  ncpu=0
+  [ -f "$cpuroot/cpu.max" ] || return
+  cfs_quota=$( cut -d' ' -f 1 < "$cpuroot/cpu.max" )
+  cfs_period=$( cut -d' ' -f 2 < "$cpuroot/cpu.max" )
+  [ "$cfs_quota" = "max" ] && return
+  [ "$cfs_period" = "0" ] && return
+  ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
+  [ "$ncpu" -gt 0 ] || return
+  echo "$ncpu"
+}
+
+get_cgroup_v1_path() {
+  needle=$1
+  found=
+  foundroot=
+  mountpoint=
+
+  [ -r "/proc/self/mountinfo" ] || return
+  [ -r "/proc/self/cgroup" ] || return
+
+  while IFS= read -r line; do
+    case "$needle" in
+      "cpuset")
+        case "$line" in
+          *cpuset*)
+            found=$( echo "$line" | cut -d ' ' -f 4,5 )
+            ;;
+        esac
+        ;;
+      "cpu")
+        case "$line" in
+          *cpuset*)
+            ;;
+          *cpu,cpuacct*|*cpuacct,cpu|*cpuacct*|*cpu*)
+            found=$( echo "$line" | cut -d ' ' -f 4,5 )
+            ;;
+        esac
+    esac
+  done << __EOF__
+$( grep -F -- '- cgroup ' /proc/self/mountinfo )
+__EOF__
+
+  while IFS= read -r line; do
+    controller=$( echo "$line" | cut -d: -f 2 )
+    case "$needle" in
+      "cpuset")
+        case "$controller" in
+          cpuset)
+            mountpoint=$( echo "$line" | cut -d: -f 3 )
+            ;;
+        esac
+        ;;
+      "cpu")
+        case "$controller" in
+          cpu,cpuacct|cpuacct,cpu|cpuacct|cpu)
+            mountpoint=$( echo "$line" | cut -d: -f 3 )
+            ;;
+        esac
+        ;;
+    esac
+done << __EOF__
+$( grep -F -- 'cpu' /proc/self/cgroup )
+__EOF__
+
+  case "${found%% *}" in
+    "/")
+      foundroot="${found##* }$mountpoint"
+      ;;
+    "$mountpoint")
+      foundroot="${found##* }"
+      ;;
+  esac
+  echo "$foundroot"
+}
+
+get_cgroup_v2_path() {
+  found=
+  foundroot=
+  mountpoint=
+
+  [ -r "/proc/self/mountinfo" ] || return
+  [ -r "/proc/self/cgroup" ] || return
+
+  while IFS= read -r line; do
+    found=$( echo "$line" | cut -d ' ' -f 4,5 )
+  done << __EOF__
+$( grep -F -- '- cgroup2 ' /proc/self/mountinfo )
+__EOF__
+
+  while IFS= read -r line; do
+    mountpoint=$( echo "$line" | cut -d: -f 3 )
+done << __EOF__
+$( grep -F -- '0::' /proc/self/cgroup )
+__EOF__
+
+  case "${found%% *}" in
+    "")
+      return
+      ;;
+    "/")
+      foundroot="${found##* }$mountpoint"
+      ;;
+    "$mountpoint")
+      foundroot="${found##* }"
+      ;;
+  esac
+  echo "$foundroot"
+}
+
+ncpu_online=$( getconf _NPROCESSORS_ONLN )
+ncpu_cpuset=
+ncpu_quota=
+ncpu_cpuset_v2=
+ncpu_quota_v2=
+
+cpuset=$( get_cgroup_v1_path "cpuset" )
+[ "$cpuset" ] && ncpu_cpuset=$( get_cpuset "$cpuset" "cpuset.effective_cpus" )
+[ "$ncpu_cpuset" ] || ncpu_cpuset=$ncpu_online
+
+cpu=$( get_cgroup_v1_path "cpu" )
+[ "$cpu" ] && ncpu_quota=$( get_quota "$cpu" )
+[ "$ncpu_quota" ] || ncpu_quota=$ncpu_online
+
+cgroup_v2=$( get_cgroup_v2_path )
+[ "$cgroup_v2" ] && ncpu_cpuset_v2=$( get_cpuset "$cgroup_v2" "cpuset.cpus.effective" )
+[ "$ncpu_cpuset_v2" ] || ncpu_cpuset_v2=$ncpu_online
+
+[ "$cgroup_v2" ] && ncpu_quota_v2=$( get_quota_v2 "$cgroup_v2" )
+[ "$ncpu_quota_v2" ] || ncpu_quota_v2=$ncpu_online
+
+ncpu=$( printf "%s\n%s\n%s\n%s\n%s\n" \
+               "$ncpu_online" \
+               "$ncpu_cpuset" \
+               "$ncpu_quota" \
+               "$ncpu_cpuset_v2" \
+               "$ncpu_quota_v2" \
+               | sort -n \
+               | head -n 1 )
+
+sed -i.bak -r 's/^(worker_processes)(.*)$/# Commented out by '"$ME"' on '"$(date)"'\n#\1\2\n\1 '"$ncpu"';/' /etc/nginx/nginx.conf
diff --git a/mainline/alpine-perl/30-tune-worker-processes.sh b/mainline/alpine-perl/30-tune-worker-processes.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4adb0432db5efc257de1d5d72a9b0a81f993f663
--- /dev/null
+++ b/mainline/alpine-perl/30-tune-worker-processes.sh
@@ -0,0 +1,194 @@
+#!/bin/sh
+# vim:sw=2:ts=2:sts=2:et
+
+set -eu
+
+LC_ALL=C
+ME=$( basename "$0" )
+PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+
+[ "${NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE:-}" ] || exit 0
+
+touch /etc/nginx/nginx.conf 2>/dev/null || { echo >&2 "$ME: error: can not modify /etc/nginx/nginx.conf (read-only file system?)"; exit 0; }
+
+ceildiv() {
+  num=$1
+  div=$2
+  echo $(( (num + div - 1) / div ))
+}
+
+get_cpuset() {
+  cpusetroot=$1
+  cpusetfile=$2
+  ncpu=0
+  [ -f "$cpusetroot/$cpusetfile" ] || return
+  for token in $( tr ',' ' ' < "$cpusetroot/$cpusetfile" ); do
+    case "$token" in
+      *-*)
+        count=$( seq $(echo "$token" | tr '-' ' ') | wc -l )
+        ncpu=$(( ncpu+count ))
+        ;;
+      *)
+        ncpu=$(( ncpu+1 ))
+        ;;
+    esac
+  done
+  echo "$ncpu"
+}
+
+get_quota() {
+  cpuroot=$1
+  ncpu=0
+  [ -f "$cpuroot/cpu.cfs_quota_us" ] || return
+  [ -f "$cpuroot/cpu.cfs_period_us" ] || return
+  cfs_quota=$( cat "$cpuroot/cpu.cfs_quota_us" )
+  cfs_period=$( cat "$cpuroot/cpu.cfs_period_us" )
+  [ "$cfs_quota" = "-1" ] && return
+  [ "$cfs_period" = "0" ] && return
+  ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
+  [ "$ncpu" -gt 0 ] || return
+  echo "$ncpu"
+}
+
+get_quota_v2() {
+  cpuroot=$1
+  ncpu=0
+  [ -f "$cpuroot/cpu.max" ] || return
+  cfs_quota=$( cut -d' ' -f 1 < "$cpuroot/cpu.max" )
+  cfs_period=$( cut -d' ' -f 2 < "$cpuroot/cpu.max" )
+  [ "$cfs_quota" = "max" ] && return
+  [ "$cfs_period" = "0" ] && return
+  ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
+  [ "$ncpu" -gt 0 ] || return
+  echo "$ncpu"
+}
+
+get_cgroup_v1_path() {
+  needle=$1
+  found=
+  foundroot=
+  mountpoint=
+
+  [ -r "/proc/self/mountinfo" ] || return
+  [ -r "/proc/self/cgroup" ] || return
+
+  while IFS= read -r line; do
+    case "$needle" in
+      "cpuset")
+        case "$line" in
+          *cpuset*)
+            found=$( echo "$line" | cut -d ' ' -f 4,5 )
+            ;;
+        esac
+        ;;
+      "cpu")
+        case "$line" in
+          *cpuset*)
+            ;;
+          *cpu,cpuacct*|*cpuacct,cpu|*cpuacct*|*cpu*)
+            found=$( echo "$line" | cut -d ' ' -f 4,5 )
+            ;;
+        esac
+    esac
+  done << __EOF__
+$( grep -F -- '- cgroup ' /proc/self/mountinfo )
+__EOF__
+
+  while IFS= read -r line; do
+    controller=$( echo "$line" | cut -d: -f 2 )
+    case "$needle" in
+      "cpuset")
+        case "$controller" in
+          cpuset)
+            mountpoint=$( echo "$line" | cut -d: -f 3 )
+            ;;
+        esac
+        ;;
+      "cpu")
+        case "$controller" in
+          cpu,cpuacct|cpuacct,cpu|cpuacct|cpu)
+            mountpoint=$( echo "$line" | cut -d: -f 3 )
+            ;;
+        esac
+        ;;
+    esac
+done << __EOF__
+$( grep -F -- 'cpu' /proc/self/cgroup )
+__EOF__
+
+  case "${found%% *}" in
+    "/")
+      foundroot="${found##* }$mountpoint"
+      ;;
+    "$mountpoint")
+      foundroot="${found##* }"
+      ;;
+  esac
+  echo "$foundroot"
+}
+
+get_cgroup_v2_path() {
+  found=
+  foundroot=
+  mountpoint=
+
+  [ -r "/proc/self/mountinfo" ] || return
+  [ -r "/proc/self/cgroup" ] || return
+
+  while IFS= read -r line; do
+    found=$( echo "$line" | cut -d ' ' -f 4,5 )
+  done << __EOF__
+$( grep -F -- '- cgroup2 ' /proc/self/mountinfo )
+__EOF__
+
+  while IFS= read -r line; do
+    mountpoint=$( echo "$line" | cut -d: -f 3 )
+done << __EOF__
+$( grep -F -- '0::' /proc/self/cgroup )
+__EOF__
+
+  case "${found%% *}" in
+    "")
+      return
+      ;;
+    "/")
+      foundroot="${found##* }$mountpoint"
+      ;;
+    "$mountpoint")
+      foundroot="${found##* }"
+      ;;
+  esac
+  echo "$foundroot"
+}
+
+ncpu_online=$( getconf _NPROCESSORS_ONLN )
+ncpu_cpuset=
+ncpu_quota=
+ncpu_cpuset_v2=
+ncpu_quota_v2=
+
+cpuset=$( get_cgroup_v1_path "cpuset" )
+[ "$cpuset" ] && ncpu_cpuset=$( get_cpuset "$cpuset" "cpuset.effective_cpus" )
+[ "$ncpu_cpuset" ] || ncpu_cpuset=$ncpu_online
+
+cpu=$( get_cgroup_v1_path "cpu" )
+[ "$cpu" ] && ncpu_quota=$( get_quota "$cpu" )
+[ "$ncpu_quota" ] || ncpu_quota=$ncpu_online
+
+cgroup_v2=$( get_cgroup_v2_path )
+[ "$cgroup_v2" ] && ncpu_cpuset_v2=$( get_cpuset "$cgroup_v2" "cpuset.cpus.effective" )
+[ "$ncpu_cpuset_v2" ] || ncpu_cpuset_v2=$ncpu_online
+
+[ "$cgroup_v2" ] && ncpu_quota_v2=$( get_quota_v2 "$cgroup_v2" )
+[ "$ncpu_quota_v2" ] || ncpu_quota_v2=$ncpu_online
+
+ncpu=$( printf "%s\n%s\n%s\n%s\n%s\n" \
+               "$ncpu_online" \
+               "$ncpu_cpuset" \
+               "$ncpu_quota" \
+               "$ncpu_cpuset_v2" \
+               "$ncpu_quota_v2" \
+               | sort -n \
+               | head -n 1 )
+
+sed -i.bak -r 's/^(worker_processes)(.*)$/# Commented out by '"$ME"' on '"$(date)"'\n#\1\2\n\1 '"$ncpu"';/' /etc/nginx/nginx.conf
diff --git a/mainline/alpine-perl/Dockerfile b/mainline/alpine-perl/Dockerfile
index ae3b6ba2cae07152bdf6da6d047be8919ff7dd3a..d3d9a146a1660717766ad8d3c4b6d2e331eb2795 100644
--- a/mainline/alpine-perl/Dockerfile
+++ b/mainline/alpine-perl/Dockerfile
@@ -116,6 +116,7 @@ RUN set -x \
 COPY docker-entrypoint.sh /
 COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
 COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
+COPY 30-tune-worker-processes.sh /docker-entrypoint.d
 ENTRYPOINT ["/docker-entrypoint.sh"]
 
 EXPOSE 80
diff --git a/mainline/alpine/30-tune-worker-processes.sh b/mainline/alpine/30-tune-worker-processes.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4adb0432db5efc257de1d5d72a9b0a81f993f663
--- /dev/null
+++ b/mainline/alpine/30-tune-worker-processes.sh
@@ -0,0 +1,194 @@
+#!/bin/sh
+# vim:sw=2:ts=2:sts=2:et
+
+set -eu
+
+LC_ALL=C
+ME=$( basename "$0" )
+PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+
+[ "${NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE:-}" ] || exit 0
+
+touch /etc/nginx/nginx.conf 2>/dev/null || { echo >&2 "$ME: error: can not modify /etc/nginx/nginx.conf (read-only file system?)"; exit 0; }
+
+ceildiv() {
+  num=$1
+  div=$2
+  echo $(( (num + div - 1) / div ))
+}
+
+get_cpuset() {
+  cpusetroot=$1
+  cpusetfile=$2
+  ncpu=0
+  [ -f "$cpusetroot/$cpusetfile" ] || return
+  for token in $( tr ',' ' ' < "$cpusetroot/$cpusetfile" ); do
+    case "$token" in
+      *-*)
+        count=$( seq $(echo "$token" | tr '-' ' ') | wc -l )
+        ncpu=$(( ncpu+count ))
+        ;;
+      *)
+        ncpu=$(( ncpu+1 ))
+        ;;
+    esac
+  done
+  echo "$ncpu"
+}
+
+get_quota() {
+  cpuroot=$1
+  ncpu=0
+  [ -f "$cpuroot/cpu.cfs_quota_us" ] || return
+  [ -f "$cpuroot/cpu.cfs_period_us" ] || return
+  cfs_quota=$( cat "$cpuroot/cpu.cfs_quota_us" )
+  cfs_period=$( cat "$cpuroot/cpu.cfs_period_us" )
+  [ "$cfs_quota" = "-1" ] && return
+  [ "$cfs_period" = "0" ] && return
+  ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
+  [ "$ncpu" -gt 0 ] || return
+  echo "$ncpu"
+}
+
+get_quota_v2() {
+  cpuroot=$1
+  ncpu=0
+  [ -f "$cpuroot/cpu.max" ] || return
+  cfs_quota=$( cut -d' ' -f 1 < "$cpuroot/cpu.max" )
+  cfs_period=$( cut -d' ' -f 2 < "$cpuroot/cpu.max" )
+  [ "$cfs_quota" = "max" ] && return
+  [ "$cfs_period" = "0" ] && return
+  ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
+  [ "$ncpu" -gt 0 ] || return
+  echo "$ncpu"
+}
+
+get_cgroup_v1_path() {
+  needle=$1
+  found=
+  foundroot=
+  mountpoint=
+
+  [ -r "/proc/self/mountinfo" ] || return
+  [ -r "/proc/self/cgroup" ] || return
+
+  while IFS= read -r line; do
+    case "$needle" in
+      "cpuset")
+        case "$line" in
+          *cpuset*)
+            found=$( echo "$line" | cut -d ' ' -f 4,5 )
+            ;;
+        esac
+        ;;
+      "cpu")
+        case "$line" in
+          *cpuset*)
+            ;;
+          *cpu,cpuacct*|*cpuacct,cpu|*cpuacct*|*cpu*)
+            found=$( echo "$line" | cut -d ' ' -f 4,5 )
+            ;;
+        esac
+    esac
+  done << __EOF__
+$( grep -F -- '- cgroup ' /proc/self/mountinfo )
+__EOF__
+
+  while IFS= read -r line; do
+    controller=$( echo "$line" | cut -d: -f 2 )
+    case "$needle" in
+      "cpuset")
+        case "$controller" in
+          cpuset)
+            mountpoint=$( echo "$line" | cut -d: -f 3 )
+            ;;
+        esac
+        ;;
+      "cpu")
+        case "$controller" in
+          cpu,cpuacct|cpuacct,cpu|cpuacct|cpu)
+            mountpoint=$( echo "$line" | cut -d: -f 3 )
+            ;;
+        esac
+        ;;
+    esac
+done << __EOF__
+$( grep -F -- 'cpu' /proc/self/cgroup )
+__EOF__
+
+  case "${found%% *}" in
+    "/")
+      foundroot="${found##* }$mountpoint"
+      ;;
+    "$mountpoint")
+      foundroot="${found##* }"
+      ;;
+  esac
+  echo "$foundroot"
+}
+
+get_cgroup_v2_path() {
+  found=
+  foundroot=
+  mountpoint=
+
+  [ -r "/proc/self/mountinfo" ] || return
+  [ -r "/proc/self/cgroup" ] || return
+
+  while IFS= read -r line; do
+    found=$( echo "$line" | cut -d ' ' -f 4,5 )
+  done << __EOF__
+$( grep -F -- '- cgroup2 ' /proc/self/mountinfo )
+__EOF__
+
+  while IFS= read -r line; do
+    mountpoint=$( echo "$line" | cut -d: -f 3 )
+done << __EOF__
+$( grep -F -- '0::' /proc/self/cgroup )
+__EOF__
+
+  case "${found%% *}" in
+    "")
+      return
+      ;;
+    "/")
+      foundroot="${found##* }$mountpoint"
+      ;;
+    "$mountpoint")
+      foundroot="${found##* }"
+      ;;
+  esac
+  echo "$foundroot"
+}
+
+ncpu_online=$( getconf _NPROCESSORS_ONLN )
+ncpu_cpuset=
+ncpu_quota=
+ncpu_cpuset_v2=
+ncpu_quota_v2=
+
+cpuset=$( get_cgroup_v1_path "cpuset" )
+[ "$cpuset" ] && ncpu_cpuset=$( get_cpuset "$cpuset" "cpuset.effective_cpus" )
+[ "$ncpu_cpuset" ] || ncpu_cpuset=$ncpu_online
+
+cpu=$( get_cgroup_v1_path "cpu" )
+[ "$cpu" ] && ncpu_quota=$( get_quota "$cpu" )
+[ "$ncpu_quota" ] || ncpu_quota=$ncpu_online
+
+cgroup_v2=$( get_cgroup_v2_path )
+[ "$cgroup_v2" ] && ncpu_cpuset_v2=$( get_cpuset "$cgroup_v2" "cpuset.cpus.effective" )
+[ "$ncpu_cpuset_v2" ] || ncpu_cpuset_v2=$ncpu_online
+
+[ "$cgroup_v2" ] && ncpu_quota_v2=$( get_quota_v2 "$cgroup_v2" )
+[ "$ncpu_quota_v2" ] || ncpu_quota_v2=$ncpu_online
+
+ncpu=$( printf "%s\n%s\n%s\n%s\n%s\n" \
+               "$ncpu_online" \
+               "$ncpu_cpuset" \
+               "$ncpu_quota" \
+               "$ncpu_cpuset_v2" \
+               "$ncpu_quota_v2" \
+               | sort -n \
+               | head -n 1 )
+
+sed -i.bak -r 's/^(worker_processes)(.*)$/# Commented out by '"$ME"' on '"$(date)"'\n#\1\2\n\1 '"$ncpu"';/' /etc/nginx/nginx.conf
diff --git a/mainline/alpine/Dockerfile b/mainline/alpine/Dockerfile
index 11eeab8da02898de691dcb3126a9e18040875e60..ea8829e1e76c5959db43762e9e85f344eaacca03 100644
--- a/mainline/alpine/Dockerfile
+++ b/mainline/alpine/Dockerfile
@@ -115,6 +115,7 @@ RUN set -x \
 COPY docker-entrypoint.sh /
 COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
 COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
+COPY 30-tune-worker-processes.sh /docker-entrypoint.d
 ENTRYPOINT ["/docker-entrypoint.sh"]
 
 EXPOSE 80
diff --git a/mainline/debian-perl/30-tune-worker-processes.sh b/mainline/debian-perl/30-tune-worker-processes.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4adb0432db5efc257de1d5d72a9b0a81f993f663
--- /dev/null
+++ b/mainline/debian-perl/30-tune-worker-processes.sh
@@ -0,0 +1,194 @@
+#!/bin/sh
+# vim:sw=2:ts=2:sts=2:et
+
+set -eu
+
+LC_ALL=C
+ME=$( basename "$0" )
+PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+
+[ "${NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE:-}" ] || exit 0
+
+touch /etc/nginx/nginx.conf 2>/dev/null || { echo >&2 "$ME: error: can not modify /etc/nginx/nginx.conf (read-only file system?)"; exit 0; }
+
+ceildiv() {
+  num=$1
+  div=$2
+  echo $(( (num + div - 1) / div ))
+}
+
+get_cpuset() {
+  cpusetroot=$1
+  cpusetfile=$2
+  ncpu=0
+  [ -f "$cpusetroot/$cpusetfile" ] || return
+  for token in $( tr ',' ' ' < "$cpusetroot/$cpusetfile" ); do
+    case "$token" in
+      *-*)
+        count=$( seq $(echo "$token" | tr '-' ' ') | wc -l )
+        ncpu=$(( ncpu+count ))
+        ;;
+      *)
+        ncpu=$(( ncpu+1 ))
+        ;;
+    esac
+  done
+  echo "$ncpu"
+}
+
+get_quota() {
+  cpuroot=$1
+  ncpu=0
+  [ -f "$cpuroot/cpu.cfs_quota_us" ] || return
+  [ -f "$cpuroot/cpu.cfs_period_us" ] || return
+  cfs_quota=$( cat "$cpuroot/cpu.cfs_quota_us" )
+  cfs_period=$( cat "$cpuroot/cpu.cfs_period_us" )
+  [ "$cfs_quota" = "-1" ] && return
+  [ "$cfs_period" = "0" ] && return
+  ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
+  [ "$ncpu" -gt 0 ] || return
+  echo "$ncpu"
+}
+
+get_quota_v2() {
+  cpuroot=$1
+  ncpu=0
+  [ -f "$cpuroot/cpu.max" ] || return
+  cfs_quota=$( cut -d' ' -f 1 < "$cpuroot/cpu.max" )
+  cfs_period=$( cut -d' ' -f 2 < "$cpuroot/cpu.max" )
+  [ "$cfs_quota" = "max" ] && return
+  [ "$cfs_period" = "0" ] && return
+  ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
+  [ "$ncpu" -gt 0 ] || return
+  echo "$ncpu"
+}
+
+get_cgroup_v1_path() {
+  needle=$1
+  found=
+  foundroot=
+  mountpoint=
+
+  [ -r "/proc/self/mountinfo" ] || return
+  [ -r "/proc/self/cgroup" ] || return
+
+  while IFS= read -r line; do
+    case "$needle" in
+      "cpuset")
+        case "$line" in
+          *cpuset*)
+            found=$( echo "$line" | cut -d ' ' -f 4,5 )
+            ;;
+        esac
+        ;;
+      "cpu")
+        case "$line" in
+          *cpuset*)
+            ;;
+          *cpu,cpuacct*|*cpuacct,cpu|*cpuacct*|*cpu*)
+            found=$( echo "$line" | cut -d ' ' -f 4,5 )
+            ;;
+        esac
+    esac
+  done << __EOF__
+$( grep -F -- '- cgroup ' /proc/self/mountinfo )
+__EOF__
+
+  while IFS= read -r line; do
+    controller=$( echo "$line" | cut -d: -f 2 )
+    case "$needle" in
+      "cpuset")
+        case "$controller" in
+          cpuset)
+            mountpoint=$( echo "$line" | cut -d: -f 3 )
+            ;;
+        esac
+        ;;
+      "cpu")
+        case "$controller" in
+          cpu,cpuacct|cpuacct,cpu|cpuacct|cpu)
+            mountpoint=$( echo "$line" | cut -d: -f 3 )
+            ;;
+        esac
+        ;;
+    esac
+done << __EOF__
+$( grep -F -- 'cpu' /proc/self/cgroup )
+__EOF__
+
+  case "${found%% *}" in
+    "/")
+      foundroot="${found##* }$mountpoint"
+      ;;
+    "$mountpoint")
+      foundroot="${found##* }"
+      ;;
+  esac
+  echo "$foundroot"
+}
+
+get_cgroup_v2_path() {
+  found=
+  foundroot=
+  mountpoint=
+
+  [ -r "/proc/self/mountinfo" ] || return
+  [ -r "/proc/self/cgroup" ] || return
+
+  while IFS= read -r line; do
+    found=$( echo "$line" | cut -d ' ' -f 4,5 )
+  done << __EOF__
+$( grep -F -- '- cgroup2 ' /proc/self/mountinfo )
+__EOF__
+
+  while IFS= read -r line; do
+    mountpoint=$( echo "$line" | cut -d: -f 3 )
+done << __EOF__
+$( grep -F -- '0::' /proc/self/cgroup )
+__EOF__
+
+  case "${found%% *}" in
+    "")
+      return
+      ;;
+    "/")
+      foundroot="${found##* }$mountpoint"
+      ;;
+    "$mountpoint")
+      foundroot="${found##* }"
+      ;;
+  esac
+  echo "$foundroot"
+}
+
+ncpu_online=$( getconf _NPROCESSORS_ONLN )
+ncpu_cpuset=
+ncpu_quota=
+ncpu_cpuset_v2=
+ncpu_quota_v2=
+
+cpuset=$( get_cgroup_v1_path "cpuset" )
+[ "$cpuset" ] && ncpu_cpuset=$( get_cpuset "$cpuset" "cpuset.effective_cpus" )
+[ "$ncpu_cpuset" ] || ncpu_cpuset=$ncpu_online
+
+cpu=$( get_cgroup_v1_path "cpu" )
+[ "$cpu" ] && ncpu_quota=$( get_quota "$cpu" )
+[ "$ncpu_quota" ] || ncpu_quota=$ncpu_online
+
+cgroup_v2=$( get_cgroup_v2_path )
+[ "$cgroup_v2" ] && ncpu_cpuset_v2=$( get_cpuset "$cgroup_v2" "cpuset.cpus.effective" )
+[ "$ncpu_cpuset_v2" ] || ncpu_cpuset_v2=$ncpu_online
+
+[ "$cgroup_v2" ] && ncpu_quota_v2=$( get_quota_v2 "$cgroup_v2" )
+[ "$ncpu_quota_v2" ] || ncpu_quota_v2=$ncpu_online
+
+ncpu=$( printf "%s\n%s\n%s\n%s\n%s\n" \
+               "$ncpu_online" \
+               "$ncpu_cpuset" \
+               "$ncpu_quota" \
+               "$ncpu_cpuset_v2" \
+               "$ncpu_quota_v2" \
+               | sort -n \
+               | head -n 1 )
+
+sed -i.bak -r 's/^(worker_processes)(.*)$/# Commented out by '"$ME"' on '"$(date)"'\n#\1\2\n\1 '"$ncpu"';/' /etc/nginx/nginx.conf
diff --git a/mainline/debian-perl/Dockerfile b/mainline/debian-perl/Dockerfile
index 1f818c940867faa6458cce6104666dde353a2b41..aa215d79a1a7869a62c763601068a2256e481538 100644
--- a/mainline/debian-perl/Dockerfile
+++ b/mainline/debian-perl/Dockerfile
@@ -107,6 +107,7 @@ RUN set -x \
 COPY docker-entrypoint.sh /
 COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
 COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
+COPY 30-tune-worker-processes.sh /docker-entrypoint.d
 ENTRYPOINT ["/docker-entrypoint.sh"]
 
 EXPOSE 80
diff --git a/mainline/debian/30-tune-worker-processes.sh b/mainline/debian/30-tune-worker-processes.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4adb0432db5efc257de1d5d72a9b0a81f993f663
--- /dev/null
+++ b/mainline/debian/30-tune-worker-processes.sh
@@ -0,0 +1,194 @@
+#!/bin/sh
+# vim:sw=2:ts=2:sts=2:et
+
+set -eu
+
+LC_ALL=C
+ME=$( basename "$0" )
+PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+
+[ "${NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE:-}" ] || exit 0
+
+touch /etc/nginx/nginx.conf 2>/dev/null || { echo >&2 "$ME: error: can not modify /etc/nginx/nginx.conf (read-only file system?)"; exit 0; }
+
+ceildiv() {
+  num=$1
+  div=$2
+  echo $(( (num + div - 1) / div ))
+}
+
+get_cpuset() {
+  cpusetroot=$1
+  cpusetfile=$2
+  ncpu=0
+  [ -f "$cpusetroot/$cpusetfile" ] || return
+  for token in $( tr ',' ' ' < "$cpusetroot/$cpusetfile" ); do
+    case "$token" in
+      *-*)
+        count=$( seq $(echo "$token" | tr '-' ' ') | wc -l )
+        ncpu=$(( ncpu+count ))
+        ;;
+      *)
+        ncpu=$(( ncpu+1 ))
+        ;;
+    esac
+  done
+  echo "$ncpu"
+}
+
+get_quota() {
+  cpuroot=$1
+  ncpu=0
+  [ -f "$cpuroot/cpu.cfs_quota_us" ] || return
+  [ -f "$cpuroot/cpu.cfs_period_us" ] || return
+  cfs_quota=$( cat "$cpuroot/cpu.cfs_quota_us" )
+  cfs_period=$( cat "$cpuroot/cpu.cfs_period_us" )
+  [ "$cfs_quota" = "-1" ] && return
+  [ "$cfs_period" = "0" ] && return
+  ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
+  [ "$ncpu" -gt 0 ] || return
+  echo "$ncpu"
+}
+
+get_quota_v2() {
+  cpuroot=$1
+  ncpu=0
+  [ -f "$cpuroot/cpu.max" ] || return
+  cfs_quota=$( cut -d' ' -f 1 < "$cpuroot/cpu.max" )
+  cfs_period=$( cut -d' ' -f 2 < "$cpuroot/cpu.max" )
+  [ "$cfs_quota" = "max" ] && return
+  [ "$cfs_period" = "0" ] && return
+  ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
+  [ "$ncpu" -gt 0 ] || return
+  echo "$ncpu"
+}
+
+get_cgroup_v1_path() {
+  needle=$1
+  found=
+  foundroot=
+  mountpoint=
+
+  [ -r "/proc/self/mountinfo" ] || return
+  [ -r "/proc/self/cgroup" ] || return
+
+  while IFS= read -r line; do
+    case "$needle" in
+      "cpuset")
+        case "$line" in
+          *cpuset*)
+            found=$( echo "$line" | cut -d ' ' -f 4,5 )
+            ;;
+        esac
+        ;;
+      "cpu")
+        case "$line" in
+          *cpuset*)
+            ;;
+          *cpu,cpuacct*|*cpuacct,cpu|*cpuacct*|*cpu*)
+            found=$( echo "$line" | cut -d ' ' -f 4,5 )
+            ;;
+        esac
+    esac
+  done << __EOF__
+$( grep -F -- '- cgroup ' /proc/self/mountinfo )
+__EOF__
+
+  while IFS= read -r line; do
+    controller=$( echo "$line" | cut -d: -f 2 )
+    case "$needle" in
+      "cpuset")
+        case "$controller" in
+          cpuset)
+            mountpoint=$( echo "$line" | cut -d: -f 3 )
+            ;;
+        esac
+        ;;
+      "cpu")
+        case "$controller" in
+          cpu,cpuacct|cpuacct,cpu|cpuacct|cpu)
+            mountpoint=$( echo "$line" | cut -d: -f 3 )
+            ;;
+        esac
+        ;;
+    esac
+done << __EOF__
+$( grep -F -- 'cpu' /proc/self/cgroup )
+__EOF__
+
+  case "${found%% *}" in
+    "/")
+      foundroot="${found##* }$mountpoint"
+      ;;
+    "$mountpoint")
+      foundroot="${found##* }"
+      ;;
+  esac
+  echo "$foundroot"
+}
+
+get_cgroup_v2_path() {
+  found=
+  foundroot=
+  mountpoint=
+
+  [ -r "/proc/self/mountinfo" ] || return
+  [ -r "/proc/self/cgroup" ] || return
+
+  while IFS= read -r line; do
+    found=$( echo "$line" | cut -d ' ' -f 4,5 )
+  done << __EOF__
+$( grep -F -- '- cgroup2 ' /proc/self/mountinfo )
+__EOF__
+
+  while IFS= read -r line; do
+    mountpoint=$( echo "$line" | cut -d: -f 3 )
+done << __EOF__
+$( grep -F -- '0::' /proc/self/cgroup )
+__EOF__
+
+  case "${found%% *}" in
+    "")
+      return
+      ;;
+    "/")
+      foundroot="${found##* }$mountpoint"
+      ;;
+    "$mountpoint")
+      foundroot="${found##* }"
+      ;;
+  esac
+  echo "$foundroot"
+}
+
+ncpu_online=$( getconf _NPROCESSORS_ONLN )
+ncpu_cpuset=
+ncpu_quota=
+ncpu_cpuset_v2=
+ncpu_quota_v2=
+
+cpuset=$( get_cgroup_v1_path "cpuset" )
+[ "$cpuset" ] && ncpu_cpuset=$( get_cpuset "$cpuset" "cpuset.effective_cpus" )
+[ "$ncpu_cpuset" ] || ncpu_cpuset=$ncpu_online
+
+cpu=$( get_cgroup_v1_path "cpu" )
+[ "$cpu" ] && ncpu_quota=$( get_quota "$cpu" )
+[ "$ncpu_quota" ] || ncpu_quota=$ncpu_online
+
+cgroup_v2=$( get_cgroup_v2_path )
+[ "$cgroup_v2" ] && ncpu_cpuset_v2=$( get_cpuset "$cgroup_v2" "cpuset.cpus.effective" )
+[ "$ncpu_cpuset_v2" ] || ncpu_cpuset_v2=$ncpu_online
+
+[ "$cgroup_v2" ] && ncpu_quota_v2=$( get_quota_v2 "$cgroup_v2" )
+[ "$ncpu_quota_v2" ] || ncpu_quota_v2=$ncpu_online
+
+ncpu=$( printf "%s\n%s\n%s\n%s\n%s\n" \
+               "$ncpu_online" \
+               "$ncpu_cpuset" \
+               "$ncpu_quota" \
+               "$ncpu_cpuset_v2" \
+               "$ncpu_quota_v2" \
+               | sort -n \
+               | head -n 1 )
+
+sed -i.bak -r 's/^(worker_processes)(.*)$/# Commented out by '"$ME"' on '"$(date)"'\n#\1\2\n\1 '"$ncpu"';/' /etc/nginx/nginx.conf
diff --git a/mainline/debian/Dockerfile b/mainline/debian/Dockerfile
index 43d697015d1e69950780acc406a91f646b6aa473..e9fdc169fc09ec7d61ba9af59a2e07099a9cec79 100644
--- a/mainline/debian/Dockerfile
+++ b/mainline/debian/Dockerfile
@@ -106,6 +106,7 @@ RUN set -x \
 COPY docker-entrypoint.sh /
 COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
 COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
+COPY 30-tune-worker-processes.sh /docker-entrypoint.d
 ENTRYPOINT ["/docker-entrypoint.sh"]
 
 EXPOSE 80
diff --git a/stable/alpine-perl/30-tune-worker-processes.sh b/stable/alpine-perl/30-tune-worker-processes.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4adb0432db5efc257de1d5d72a9b0a81f993f663
--- /dev/null
+++ b/stable/alpine-perl/30-tune-worker-processes.sh
@@ -0,0 +1,194 @@
+#!/bin/sh
+# vim:sw=2:ts=2:sts=2:et
+
+set -eu
+
+LC_ALL=C
+ME=$( basename "$0" )
+PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+
+[ "${NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE:-}" ] || exit 0
+
+touch /etc/nginx/nginx.conf 2>/dev/null || { echo >&2 "$ME: error: can not modify /etc/nginx/nginx.conf (read-only file system?)"; exit 0; }
+
+ceildiv() {
+  num=$1
+  div=$2
+  echo $(( (num + div - 1) / div ))
+}
+
+get_cpuset() {
+  cpusetroot=$1
+  cpusetfile=$2
+  ncpu=0
+  [ -f "$cpusetroot/$cpusetfile" ] || return
+  for token in $( tr ',' ' ' < "$cpusetroot/$cpusetfile" ); do
+    case "$token" in
+      *-*)
+        count=$( seq $(echo "$token" | tr '-' ' ') | wc -l )
+        ncpu=$(( ncpu+count ))
+        ;;
+      *)
+        ncpu=$(( ncpu+1 ))
+        ;;
+    esac
+  done
+  echo "$ncpu"
+}
+
+get_quota() {
+  cpuroot=$1
+  ncpu=0
+  [ -f "$cpuroot/cpu.cfs_quota_us" ] || return
+  [ -f "$cpuroot/cpu.cfs_period_us" ] || return
+  cfs_quota=$( cat "$cpuroot/cpu.cfs_quota_us" )
+  cfs_period=$( cat "$cpuroot/cpu.cfs_period_us" )
+  [ "$cfs_quota" = "-1" ] && return
+  [ "$cfs_period" = "0" ] && return
+  ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
+  [ "$ncpu" -gt 0 ] || return
+  echo "$ncpu"
+}
+
+get_quota_v2() {
+  cpuroot=$1
+  ncpu=0
+  [ -f "$cpuroot/cpu.max" ] || return
+  cfs_quota=$( cut -d' ' -f 1 < "$cpuroot/cpu.max" )
+  cfs_period=$( cut -d' ' -f 2 < "$cpuroot/cpu.max" )
+  [ "$cfs_quota" = "max" ] && return
+  [ "$cfs_period" = "0" ] && return
+  ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
+  [ "$ncpu" -gt 0 ] || return
+  echo "$ncpu"
+}
+
+get_cgroup_v1_path() {
+  needle=$1
+  found=
+  foundroot=
+  mountpoint=
+
+  [ -r "/proc/self/mountinfo" ] || return
+  [ -r "/proc/self/cgroup" ] || return
+
+  while IFS= read -r line; do
+    case "$needle" in
+      "cpuset")
+        case "$line" in
+          *cpuset*)
+            found=$( echo "$line" | cut -d ' ' -f 4,5 )
+            ;;
+        esac
+        ;;
+      "cpu")
+        case "$line" in
+          *cpuset*)
+            ;;
+          *cpu,cpuacct*|*cpuacct,cpu|*cpuacct*|*cpu*)
+            found=$( echo "$line" | cut -d ' ' -f 4,5 )
+            ;;
+        esac
+    esac
+  done << __EOF__
+$( grep -F -- '- cgroup ' /proc/self/mountinfo )
+__EOF__
+
+  while IFS= read -r line; do
+    controller=$( echo "$line" | cut -d: -f 2 )
+    case "$needle" in
+      "cpuset")
+        case "$controller" in
+          cpuset)
+            mountpoint=$( echo "$line" | cut -d: -f 3 )
+            ;;
+        esac
+        ;;
+      "cpu")
+        case "$controller" in
+          cpu,cpuacct|cpuacct,cpu|cpuacct|cpu)
+            mountpoint=$( echo "$line" | cut -d: -f 3 )
+            ;;
+        esac
+        ;;
+    esac
+done << __EOF__
+$( grep -F -- 'cpu' /proc/self/cgroup )
+__EOF__
+
+  case "${found%% *}" in
+    "/")
+      foundroot="${found##* }$mountpoint"
+      ;;
+    "$mountpoint")
+      foundroot="${found##* }"
+      ;;
+  esac
+  echo "$foundroot"
+}
+
+get_cgroup_v2_path() {
+  found=
+  foundroot=
+  mountpoint=
+
+  [ -r "/proc/self/mountinfo" ] || return
+  [ -r "/proc/self/cgroup" ] || return
+
+  while IFS= read -r line; do
+    found=$( echo "$line" | cut -d ' ' -f 4,5 )
+  done << __EOF__
+$( grep -F -- '- cgroup2 ' /proc/self/mountinfo )
+__EOF__
+
+  while IFS= read -r line; do
+    mountpoint=$( echo "$line" | cut -d: -f 3 )
+done << __EOF__
+$( grep -F -- '0::' /proc/self/cgroup )
+__EOF__
+
+  case "${found%% *}" in
+    "")
+      return
+      ;;
+    "/")
+      foundroot="${found##* }$mountpoint"
+      ;;
+    "$mountpoint")
+      foundroot="${found##* }"
+      ;;
+  esac
+  echo "$foundroot"
+}
+
+ncpu_online=$( getconf _NPROCESSORS_ONLN )
+ncpu_cpuset=
+ncpu_quota=
+ncpu_cpuset_v2=
+ncpu_quota_v2=
+
+cpuset=$( get_cgroup_v1_path "cpuset" )
+[ "$cpuset" ] && ncpu_cpuset=$( get_cpuset "$cpuset" "cpuset.effective_cpus" )
+[ "$ncpu_cpuset" ] || ncpu_cpuset=$ncpu_online
+
+cpu=$( get_cgroup_v1_path "cpu" )
+[ "$cpu" ] && ncpu_quota=$( get_quota "$cpu" )
+[ "$ncpu_quota" ] || ncpu_quota=$ncpu_online
+
+cgroup_v2=$( get_cgroup_v2_path )
+[ "$cgroup_v2" ] && ncpu_cpuset_v2=$( get_cpuset "$cgroup_v2" "cpuset.cpus.effective" )
+[ "$ncpu_cpuset_v2" ] || ncpu_cpuset_v2=$ncpu_online
+
+[ "$cgroup_v2" ] && ncpu_quota_v2=$( get_quota_v2 "$cgroup_v2" )
+[ "$ncpu_quota_v2" ] || ncpu_quota_v2=$ncpu_online
+
+ncpu=$( printf "%s\n%s\n%s\n%s\n%s\n" \
+               "$ncpu_online" \
+               "$ncpu_cpuset" \
+               "$ncpu_quota" \
+               "$ncpu_cpuset_v2" \
+               "$ncpu_quota_v2" \
+               | sort -n \
+               | head -n 1 )
+
+sed -i.bak -r 's/^(worker_processes)(.*)$/# Commented out by '"$ME"' on '"$(date)"'\n#\1\2\n\1 '"$ncpu"';/' /etc/nginx/nginx.conf
diff --git a/stable/alpine-perl/Dockerfile b/stable/alpine-perl/Dockerfile
index d86c2ccd165d1f74ad17e2f1ad98ff232f338e1a..6df7dde2c251af7437cdecad938c9f8f658e9805 100644
--- a/stable/alpine-perl/Dockerfile
+++ b/stable/alpine-perl/Dockerfile
@@ -116,6 +116,7 @@ RUN set -x \
 COPY docker-entrypoint.sh /
 COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
 COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
+COPY 30-tune-worker-processes.sh /docker-entrypoint.d
 ENTRYPOINT ["/docker-entrypoint.sh"]
 
 EXPOSE 80
diff --git a/stable/alpine/30-tune-worker-processes.sh b/stable/alpine/30-tune-worker-processes.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4adb0432db5efc257de1d5d72a9b0a81f993f663
--- /dev/null
+++ b/stable/alpine/30-tune-worker-processes.sh
@@ -0,0 +1,194 @@
+#!/bin/sh
+# vim:sw=2:ts=2:sts=2:et
+
+set -eu
+
+LC_ALL=C
+ME=$( basename "$0" )
+PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+
+[ "${NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE:-}" ] || exit 0
+
+touch /etc/nginx/nginx.conf 2>/dev/null || { echo >&2 "$ME: error: can not modify /etc/nginx/nginx.conf (read-only file system?)"; exit 0; }
+
+ceildiv() {
+  num=$1
+  div=$2
+  echo $(( (num + div - 1) / div ))
+}
+
+get_cpuset() {
+  cpusetroot=$1
+  cpusetfile=$2
+  ncpu=0
+  [ -f "$cpusetroot/$cpusetfile" ] || return
+  for token in $( tr ',' ' ' < "$cpusetroot/$cpusetfile" ); do
+    case "$token" in
+      *-*)
+        count=$( seq $(echo "$token" | tr '-' ' ') | wc -l )
+        ncpu=$(( ncpu+count ))
+        ;;
+      *)
+        ncpu=$(( ncpu+1 ))
+        ;;
+    esac
+  done
+  echo "$ncpu"
+}
+
+get_quota() {
+  cpuroot=$1
+  ncpu=0
+  [ -f "$cpuroot/cpu.cfs_quota_us" ] || return
+  [ -f "$cpuroot/cpu.cfs_period_us" ] || return
+  cfs_quota=$( cat "$cpuroot/cpu.cfs_quota_us" )
+  cfs_period=$( cat "$cpuroot/cpu.cfs_period_us" )
+  [ "$cfs_quota" = "-1" ] && return
+  [ "$cfs_period" = "0" ] && return
+  ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
+  [ "$ncpu" -gt 0 ] || return
+  echo "$ncpu"
+}
+
+get_quota_v2() {
+  cpuroot=$1
+  ncpu=0
+  [ -f "$cpuroot/cpu.max" ] || return
+  cfs_quota=$( cut -d' ' -f 1 < "$cpuroot/cpu.max" )
+  cfs_period=$( cut -d' ' -f 2 < "$cpuroot/cpu.max" )
+  [ "$cfs_quota" = "max" ] && return
+  [ "$cfs_period" = "0" ] && return
+  ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
+  [ "$ncpu" -gt 0 ] || return
+  echo "$ncpu"
+}
+
+get_cgroup_v1_path() {
+  needle=$1
+  found=
+  foundroot=
+  mountpoint=
+
+  [ -r "/proc/self/mountinfo" ] || return
+  [ -r "/proc/self/cgroup" ] || return
+
+  while IFS= read -r line; do
+    case "$needle" in
+      "cpuset")
+        case "$line" in
+          *cpuset*)
+            found=$( echo "$line" | cut -d ' ' -f 4,5 )
+            ;;
+        esac
+        ;;
+      "cpu")
+        case "$line" in
+          *cpuset*)
+            ;;
+          *cpu,cpuacct*|*cpuacct,cpu|*cpuacct*|*cpu*)
+            found=$( echo "$line" | cut -d ' ' -f 4,5 )
+            ;;
+        esac
+    esac
+  done << __EOF__
+$( grep -F -- '- cgroup ' /proc/self/mountinfo )
+__EOF__
+
+  while IFS= read -r line; do
+    controller=$( echo "$line" | cut -d: -f 2 )
+    case "$needle" in
+      "cpuset")
+        case "$controller" in
+          cpuset)
+            mountpoint=$( echo "$line" | cut -d: -f 3 )
+            ;;
+        esac
+        ;;
+      "cpu")
+        case "$controller" in
+          cpu,cpuacct|cpuacct,cpu|cpuacct|cpu)
+            mountpoint=$( echo "$line" | cut -d: -f 3 )
+            ;;
+        esac
+        ;;
+    esac
+done << __EOF__
+$( grep -F -- 'cpu' /proc/self/cgroup )
+__EOF__
+
+  case "${found%% *}" in
+    "/")
+      foundroot="${found##* }$mountpoint"
+      ;;
+    "$mountpoint")
+      foundroot="${found##* }"
+      ;;
+  esac
+  echo "$foundroot"
+}
+
+get_cgroup_v2_path() {
+  found=
+  foundroot=
+  mountpoint=
+
+  [ -r "/proc/self/mountinfo" ] || return
+  [ -r "/proc/self/cgroup" ] || return
+
+  while IFS= read -r line; do
+    found=$( echo "$line" | cut -d ' ' -f 4,5 )
+  done << __EOF__
+$( grep -F -- '- cgroup2 ' /proc/self/mountinfo )
+__EOF__
+
+  while IFS= read -r line; do
+    mountpoint=$( echo "$line" | cut -d: -f 3 )
+done << __EOF__
+$( grep -F -- '0::' /proc/self/cgroup )
+__EOF__
+
+  case "${found%% *}" in
+    "")
+      return
+      ;;
+    "/")
+      foundroot="${found##* }$mountpoint"
+      ;;
+    "$mountpoint")
+      foundroot="${found##* }"
+      ;;
+  esac
+  echo "$foundroot"
+}
+
+ncpu_online=$( getconf _NPROCESSORS_ONLN )
+ncpu_cpuset=
+ncpu_quota=
+ncpu_cpuset_v2=
+ncpu_quota_v2=
+
+cpuset=$( get_cgroup_v1_path "cpuset" )
+[ "$cpuset" ] && ncpu_cpuset=$( get_cpuset "$cpuset" "cpuset.effective_cpus" )
+[ "$ncpu_cpuset" ] || ncpu_cpuset=$ncpu_online
+
+cpu=$( get_cgroup_v1_path "cpu" )
+[ "$cpu" ] && ncpu_quota=$( get_quota "$cpu" )
+[ "$ncpu_quota" ] || ncpu_quota=$ncpu_online
+
+cgroup_v2=$( get_cgroup_v2_path )
+[ "$cgroup_v2" ] && ncpu_cpuset_v2=$( get_cpuset "$cgroup_v2" "cpuset.cpus.effective" )
+[ "$ncpu_cpuset_v2" ] || ncpu_cpuset_v2=$ncpu_online
+
+[ "$cgroup_v2" ] && ncpu_quota_v2=$( get_quota_v2 "$cgroup_v2" )
+[ "$ncpu_quota_v2" ] || ncpu_quota_v2=$ncpu_online
+
+ncpu=$( printf "%s\n%s\n%s\n%s\n%s\n" \
+               "$ncpu_online" \
+               "$ncpu_cpuset" \
+               "$ncpu_quota" \
+               "$ncpu_cpuset_v2" \
+               "$ncpu_quota_v2" \
+               | sort -n \
+               | head -n 1 )
+
+sed -i.bak -r 's/^(worker_processes)(.*)$/# Commented out by '"$ME"' on '"$(date)"'\n#\1\2\n\1 '"$ncpu"';/' /etc/nginx/nginx.conf
diff --git a/stable/alpine/Dockerfile b/stable/alpine/Dockerfile
index bd34cfb1fc0db96fd4dfb34d904a017782d54ddf..397380ad6694d495332dafbe1218c685e02d4325 100644
--- a/stable/alpine/Dockerfile
+++ b/stable/alpine/Dockerfile
@@ -115,6 +115,7 @@ RUN set -x \
 COPY docker-entrypoint.sh /
 COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
 COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
+COPY 30-tune-worker-processes.sh /docker-entrypoint.d
 ENTRYPOINT ["/docker-entrypoint.sh"]
 
 EXPOSE 80
diff --git a/stable/debian-perl/30-tune-worker-processes.sh b/stable/debian-perl/30-tune-worker-processes.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4adb0432db5efc257de1d5d72a9b0a81f993f663
--- /dev/null
+++ b/stable/debian-perl/30-tune-worker-processes.sh
@@ -0,0 +1,194 @@
+#!/bin/sh
+# vim:sw=2:ts=2:sts=2:et
+
+set -eu
+
+LC_ALL=C
+ME=$( basename "$0" )
+PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+
+[ "${NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE:-}" ] || exit 0
+
+touch /etc/nginx/nginx.conf 2>/dev/null || { echo >&2 "$ME: error: can not modify /etc/nginx/nginx.conf (read-only file system?)"; exit 0; }
+
+ceildiv() {
+  num=$1
+  div=$2
+  echo $(( (num + div - 1) / div ))
+}
+
+get_cpuset() {
+  cpusetroot=$1
+  cpusetfile=$2
+  ncpu=0
+  [ -f "$cpusetroot/$cpusetfile" ] || return
+  for token in $( tr ',' ' ' < "$cpusetroot/$cpusetfile" ); do
+    case "$token" in
+      *-*)
+        count=$( seq $(echo "$token" | tr '-' ' ') | wc -l )
+        ncpu=$(( ncpu+count ))
+        ;;
+      *)
+        ncpu=$(( ncpu+1 ))
+        ;;
+    esac
+  done
+  echo "$ncpu"
+}
+
+get_quota() {
+  cpuroot=$1
+  ncpu=0
+  [ -f "$cpuroot/cpu.cfs_quota_us" ] || return
+  [ -f "$cpuroot/cpu.cfs_period_us" ] || return
+  cfs_quota=$( cat "$cpuroot/cpu.cfs_quota_us" )
+  cfs_period=$( cat "$cpuroot/cpu.cfs_period_us" )
+  [ "$cfs_quota" = "-1" ] && return
+  [ "$cfs_period" = "0" ] && return
+  ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
+  [ "$ncpu" -gt 0 ] || return
+  echo "$ncpu"
+}
+
+get_quota_v2() {
+  cpuroot=$1
+  ncpu=0
+  [ -f "$cpuroot/cpu.max" ] || return
+  cfs_quota=$( cut -d' ' -f 1 < "$cpuroot/cpu.max" )
+  cfs_period=$( cut -d' ' -f 2 < "$cpuroot/cpu.max" )
+  [ "$cfs_quota" = "max" ] && return
+  [ "$cfs_period" = "0" ] && return
+  ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
+  [ "$ncpu" -gt 0 ] || return
+  echo "$ncpu"
+}
+
+get_cgroup_v1_path() {
+  needle=$1
+  found=
+  foundroot=
+  mountpoint=
+
+  [ -r "/proc/self/mountinfo" ] || return
+  [ -r "/proc/self/cgroup" ] || return
+
+  while IFS= read -r line; do
+    case "$needle" in
+      "cpuset")
+        case "$line" in
+          *cpuset*)
+            found=$( echo "$line" | cut -d ' ' -f 4,5 )
+            ;;
+        esac
+        ;;
+      "cpu")
+        case "$line" in
+          *cpuset*)
+            ;;
+          *cpu,cpuacct*|*cpuacct,cpu|*cpuacct*|*cpu*)
+            found=$( echo "$line" | cut -d ' ' -f 4,5 )
+            ;;
+        esac
+    esac
+  done << __EOF__
+$( grep -F -- '- cgroup ' /proc/self/mountinfo )
+__EOF__
+
+  while IFS= read -r line; do
+    controller=$( echo "$line" | cut -d: -f 2 )
+    case "$needle" in
+      "cpuset")
+        case "$controller" in
+          cpuset)
+            mountpoint=$( echo "$line" | cut -d: -f 3 )
+            ;;
+        esac
+        ;;
+      "cpu")
+        case "$controller" in
+          cpu,cpuacct|cpuacct,cpu|cpuacct|cpu)
+            mountpoint=$( echo "$line" | cut -d: -f 3 )
+            ;;
+        esac
+        ;;
+    esac
+done << __EOF__
+$( grep -F -- 'cpu' /proc/self/cgroup )
+__EOF__
+
+  case "${found%% *}" in
+    "/")
+      foundroot="${found##* }$mountpoint"
+      ;;
+    "$mountpoint")
+      foundroot="${found##* }"
+      ;;
+  esac
+  echo "$foundroot"
+}
+
+get_cgroup_v2_path() {
+  found=
+  foundroot=
+  mountpoint=
+
+  [ -r "/proc/self/mountinfo" ] || return
+  [ -r "/proc/self/cgroup" ] || return
+
+  while IFS= read -r line; do
+    found=$( echo "$line" | cut -d ' ' -f 4,5 )
+  done << __EOF__
+$( grep -F -- '- cgroup2 ' /proc/self/mountinfo )
+__EOF__
+
+  while IFS= read -r line; do
+    mountpoint=$( echo "$line" | cut -d: -f 3 )
+done << __EOF__
+$( grep -F -- '0::' /proc/self/cgroup )
+__EOF__
+
+  case "${found%% *}" in
+    "")
+      return
+      ;;
+    "/")
+      foundroot="${found##* }$mountpoint"
+      ;;
+    "$mountpoint")
+      foundroot="${found##* }"
+      ;;
+  esac
+  echo "$foundroot"
+}
+
+ncpu_online=$( getconf _NPROCESSORS_ONLN )
+ncpu_cpuset=
+ncpu_quota=
+ncpu_cpuset_v2=
+ncpu_quota_v2=
+
+cpuset=$( get_cgroup_v1_path "cpuset" )
+[ "$cpuset" ] && ncpu_cpuset=$( get_cpuset "$cpuset" "cpuset.effective_cpus" )
+[ "$ncpu_cpuset" ] || ncpu_cpuset=$ncpu_online
+
+cpu=$( get_cgroup_v1_path "cpu" )
+[ "$cpu" ] && ncpu_quota=$( get_quota "$cpu" )
+[ "$ncpu_quota" ] || ncpu_quota=$ncpu_online
+
+cgroup_v2=$( get_cgroup_v2_path )
+[ "$cgroup_v2" ] && ncpu_cpuset_v2=$( get_cpuset "$cgroup_v2" "cpuset.cpus.effective" )
+[ "$ncpu_cpuset_v2" ] || ncpu_cpuset_v2=$ncpu_online
+
+[ "$cgroup_v2" ] && ncpu_quota_v2=$( get_quota_v2 "$cgroup_v2" )
+[ "$ncpu_quota_v2" ] || ncpu_quota_v2=$ncpu_online
+
+ncpu=$( printf "%s\n%s\n%s\n%s\n%s\n" \
+               "$ncpu_online" \
+               "$ncpu_cpuset" \
+               "$ncpu_quota" \
+               "$ncpu_cpuset_v2" \
+               "$ncpu_quota_v2" \
+               | sort -n \
+               | head -n 1 )
+
+sed -i.bak -r 's/^(worker_processes)(.*)$/# Commented out by '"$ME"' on '"$(date)"'\n#\1\2\n\1 '"$ncpu"';/' /etc/nginx/nginx.conf
diff --git a/stable/debian-perl/Dockerfile b/stable/debian-perl/Dockerfile
index 9998d1321e6286eb697f558d86d0ba8e28ba58c4..90a797dc126ef036b80004b7bbe14470c33407cc 100644
--- a/stable/debian-perl/Dockerfile
+++ b/stable/debian-perl/Dockerfile
@@ -107,6 +107,7 @@ RUN set -x \
 COPY docker-entrypoint.sh /
 COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
 COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
+COPY 30-tune-worker-processes.sh /docker-entrypoint.d
 ENTRYPOINT ["/docker-entrypoint.sh"]
 
 EXPOSE 80
diff --git a/stable/debian/30-tune-worker-processes.sh b/stable/debian/30-tune-worker-processes.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4adb0432db5efc257de1d5d72a9b0a81f993f663
--- /dev/null
+++ b/stable/debian/30-tune-worker-processes.sh
@@ -0,0 +1,194 @@
+#!/bin/sh
+# vim:sw=2:ts=2:sts=2:et
+
+set -eu
+
+LC_ALL=C
+ME=$( basename "$0" )
+PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+
+[ "${NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE:-}" ] || exit 0
+
+touch /etc/nginx/nginx.conf 2>/dev/null || { echo >&2 "$ME: error: can not modify /etc/nginx/nginx.conf (read-only file system?)"; exit 0; }
+
+ceildiv() {
+  num=$1
+  div=$2
+  echo $(( (num + div - 1) / div ))
+}
+
+get_cpuset() {
+  cpusetroot=$1
+  cpusetfile=$2
+  ncpu=0
+  [ -f "$cpusetroot/$cpusetfile" ] || return
+  for token in $( tr ',' ' ' < "$cpusetroot/$cpusetfile" ); do
+    case "$token" in
+      *-*)
+        count=$( seq $(echo "$token" | tr '-' ' ') | wc -l )
+        ncpu=$(( ncpu+count ))
+        ;;
+      *)
+        ncpu=$(( ncpu+1 ))
+        ;;
+    esac
+  done
+  echo "$ncpu"
+}
+
+get_quota() {
+  cpuroot=$1
+  ncpu=0
+  [ -f "$cpuroot/cpu.cfs_quota_us" ] || return
+  [ -f "$cpuroot/cpu.cfs_period_us" ] || return
+  cfs_quota=$( cat "$cpuroot/cpu.cfs_quota_us" )
+  cfs_period=$( cat "$cpuroot/cpu.cfs_period_us" )
+  [ "$cfs_quota" = "-1" ] && return
+  [ "$cfs_period" = "0" ] && return
+  ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
+  [ "$ncpu" -gt 0 ] || return
+  echo "$ncpu"
+}
+
+get_quota_v2() {
+  cpuroot=$1
+  ncpu=0
+  [ -f "$cpuroot/cpu.max" ] || return
+  cfs_quota=$( cut -d' ' -f 1 < "$cpuroot/cpu.max" )
+  cfs_period=$( cut -d' ' -f 2 < "$cpuroot/cpu.max" )
+  [ "$cfs_quota" = "max" ] && return
+  [ "$cfs_period" = "0" ] && return
+  ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
+  [ "$ncpu" -gt 0 ] || return
+  echo "$ncpu"
+}
+
+get_cgroup_v1_path() {
+  needle=$1
+  found=
+  foundroot=
+  mountpoint=
+
+  [ -r "/proc/self/mountinfo" ] || return
+  [ -r "/proc/self/cgroup" ] || return
+
+  while IFS= read -r line; do
+    case "$needle" in
+      "cpuset")
+        case "$line" in
+          *cpuset*)
+            found=$( echo "$line" | cut -d ' ' -f 4,5 )
+            ;;
+        esac
+        ;;
+      "cpu")
+        case "$line" in
+          *cpuset*)
+            ;;
+          *cpu,cpuacct*|*cpuacct,cpu|*cpuacct*|*cpu*)
+            found=$( echo "$line" | cut -d ' ' -f 4,5 )
+            ;;
+        esac
+    esac
+  done << __EOF__
+$( grep -F -- '- cgroup ' /proc/self/mountinfo )
+__EOF__
+
+  while IFS= read -r line; do
+    controller=$( echo "$line" | cut -d: -f 2 )
+    case "$needle" in
+      "cpuset")
+        case "$controller" in
+          cpuset)
+            mountpoint=$( echo "$line" | cut -d: -f 3 )
+            ;;
+        esac
+        ;;
+      "cpu")
+        case "$controller" in
+          cpu,cpuacct|cpuacct,cpu|cpuacct|cpu)
+            mountpoint=$( echo "$line" | cut -d: -f 3 )
+            ;;
+        esac
+        ;;
+    esac
+done << __EOF__
+$( grep -F -- 'cpu' /proc/self/cgroup )
+__EOF__
+
+  case "${found%% *}" in
+    "/")
+      foundroot="${found##* }$mountpoint"
+      ;;
+    "$mountpoint")
+      foundroot="${found##* }"
+      ;;
+  esac
+  echo "$foundroot"
+}
+
+get_cgroup_v2_path() {
+  found=
+  foundroot=
+  mountpoint=
+
+  [ -r "/proc/self/mountinfo" ] || return
+  [ -r "/proc/self/cgroup" ] || return
+
+  while IFS= read -r line; do
+    found=$( echo "$line" | cut -d ' ' -f 4,5 )
+  done << __EOF__
+$( grep -F -- '- cgroup2 ' /proc/self/mountinfo )
+__EOF__
+
+  while IFS= read -r line; do
+    mountpoint=$( echo "$line" | cut -d: -f 3 )
+done << __EOF__
+$( grep -F -- '0::' /proc/self/cgroup )
+__EOF__
+
+  case "${found%% *}" in
+    "")
+      return
+      ;;
+    "/")
+      foundroot="${found##* }$mountpoint"
+      ;;
+    "$mountpoint")
+      foundroot="${found##* }"
+      ;;
+  esac
+  echo "$foundroot"
+}
+
+ncpu_online=$( getconf _NPROCESSORS_ONLN )
+ncpu_cpuset=
+ncpu_quota=
+ncpu_cpuset_v2=
+ncpu_quota_v2=
+
+cpuset=$( get_cgroup_v1_path "cpuset" )
+[ "$cpuset" ] && ncpu_cpuset=$( get_cpuset "$cpuset" "cpuset.effective_cpus" )
+[ "$ncpu_cpuset" ] || ncpu_cpuset=$ncpu_online
+
+cpu=$( get_cgroup_v1_path "cpu" )
+[ "$cpu" ] && ncpu_quota=$( get_quota "$cpu" )
+[ "$ncpu_quota" ] || ncpu_quota=$ncpu_online
+
+cgroup_v2=$( get_cgroup_v2_path )
+[ "$cgroup_v2" ] && ncpu_cpuset_v2=$( get_cpuset "$cgroup_v2" "cpuset.cpus.effective" )
+[ "$ncpu_cpuset_v2" ] || ncpu_cpuset_v2=$ncpu_online
+
+[ "$cgroup_v2" ] && ncpu_quota_v2=$( get_quota_v2 "$cgroup_v2" )
+[ "$ncpu_quota_v2" ] || ncpu_quota_v2=$ncpu_online
+
+ncpu=$( printf "%s\n%s\n%s\n%s\n%s\n" \
+               "$ncpu_online" \
+               "$ncpu_cpuset" \
+               "$ncpu_quota" \
+               "$ncpu_cpuset_v2" \
+               "$ncpu_quota_v2" \
+               | sort -n \
+               | head -n 1 )
+
+sed -i.bak -r 's/^(worker_processes)(.*)$/# Commented out by '"$ME"' on '"$(date)"'\n#\1\2\n\1 '"$ncpu"';/' /etc/nginx/nginx.conf
diff --git a/stable/debian/Dockerfile b/stable/debian/Dockerfile
index a4b6b7f0731666c2a53ba71a23fff33b3616ba41..a4d932bf73ebae7615a24e79b6311efa8c7912ab 100644
--- a/stable/debian/Dockerfile
+++ b/stable/debian/Dockerfile
@@ -106,6 +106,7 @@ RUN set -x \
 COPY docker-entrypoint.sh /
 COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
 COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
+COPY 30-tune-worker-processes.sh /docker-entrypoint.d
 ENTRYPOINT ["/docker-entrypoint.sh"]
 
 EXPOSE 80