From 25c42e92268ea8239cbd34cf3802de8ce7289fd5 Mon Sep 17 00:00:00 2001
From: Konstantin Pavlov <thresh@videolan.org>
Date: Wed, 25 Nov 2020 21:41:08 +0300
Subject: [PATCH] Introduced an entrypoint script to autotune the number of
 worker processes

The script is a no-op bye default, you would need to enable its logic by
setting an NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE variable to any
value.

The script then tries to get the following values:

 - getconf _NPROCESSORS_ONLN
 - the amount of cpus from cpuset cgroup
 - the quotas from cpu/cpuacct cgroup

The lowest of all three is then applied to nginx.conf.
---
 Dockerfile-alpine.template                    |   1 +
 Dockerfile-debian.template                    |   1 +
 entrypoint/30-tune-worker-processes.sh        | 127 ++++++++++++++++++
 .../alpine-perl/30-tune-worker-processes.sh   | 127 ++++++++++++++++++
 mainline/alpine-perl/Dockerfile               |   1 +
 mainline/alpine/30-tune-worker-processes.sh   | 127 ++++++++++++++++++
 mainline/alpine/Dockerfile                    |   1 +
 .../debian-perl/30-tune-worker-processes.sh   | 127 ++++++++++++++++++
 mainline/debian-perl/Dockerfile               |   1 +
 mainline/debian/30-tune-worker-processes.sh   | 127 ++++++++++++++++++
 mainline/debian/Dockerfile                    |   1 +
 .../alpine-perl/30-tune-worker-processes.sh   | 127 ++++++++++++++++++
 stable/alpine-perl/Dockerfile                 |   1 +
 stable/alpine/30-tune-worker-processes.sh     | 127 ++++++++++++++++++
 stable/alpine/Dockerfile                      |   1 +
 .../debian-perl/30-tune-worker-processes.sh   | 127 ++++++++++++++++++
 stable/debian-perl/Dockerfile                 |   1 +
 stable/debian/30-tune-worker-processes.sh     | 127 ++++++++++++++++++
 stable/debian/Dockerfile                      |   1 +
 19 files changed, 1153 insertions(+)
 create mode 100755 entrypoint/30-tune-worker-processes.sh
 create mode 100755 mainline/alpine-perl/30-tune-worker-processes.sh
 create mode 100755 mainline/alpine/30-tune-worker-processes.sh
 create mode 100755 mainline/debian-perl/30-tune-worker-processes.sh
 create mode 100755 mainline/debian/30-tune-worker-processes.sh
 create mode 100755 stable/alpine-perl/30-tune-worker-processes.sh
 create mode 100755 stable/alpine/30-tune-worker-processes.sh
 create mode 100755 stable/debian-perl/30-tune-worker-processes.sh
 create mode 100755 stable/debian/30-tune-worker-processes.sh

diff --git a/Dockerfile-alpine.template b/Dockerfile-alpine.template
index eb623c2..1ce4411 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 8521d5a..91b7cf7 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 0000000..f2199ca
--- /dev/null
+++ b/entrypoint/30-tune-worker-processes.sh
@@ -0,0 +1,127 @@
+#!/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
+  ncpu=0
+  [ -f "$cpusetroot/cpuset.effective_cpus" ] || return
+  for token in $( tr ',' ' ' < "$cpusetroot/cpuset.effective_cpus" ); 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_cgroup_path() {
+  needle=$1
+  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"
+}
+
+ncpu_online=$( getconf _NPROCESSORS_ONLN )
+
+cpuset=$( get_cgroup_path "cpuset" )
+[ "$cpuset" ] && ncpu_cpuset=$( get_cpuset "$cpuset" )
+[ "$ncpu_cpuset" ] || ncpu_cpuset=$ncpu_online
+
+cpu=$( get_cgroup_path "cpu" )
+[ "$cpu" ] && ncpu_quota=$( get_quota "$cpu" )
+[ "$ncpu_quota" ] || ncpu_quota=$ncpu_online
+
+ncpu=$( printf "%s\n%s\n%s\n" "$ncpu_online" "$ncpu_quota" "$ncpu_cpuset" | 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 0000000..f2199ca
--- /dev/null
+++ b/mainline/alpine-perl/30-tune-worker-processes.sh
@@ -0,0 +1,127 @@
+#!/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
+  ncpu=0
+  [ -f "$cpusetroot/cpuset.effective_cpus" ] || return
+  for token in $( tr ',' ' ' < "$cpusetroot/cpuset.effective_cpus" ); 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_cgroup_path() {
+  needle=$1
+  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"
+}
+
+ncpu_online=$( getconf _NPROCESSORS_ONLN )
+
+cpuset=$( get_cgroup_path "cpuset" )
+[ "$cpuset" ] && ncpu_cpuset=$( get_cpuset "$cpuset" )
+[ "$ncpu_cpuset" ] || ncpu_cpuset=$ncpu_online
+
+cpu=$( get_cgroup_path "cpu" )
+[ "$cpu" ] && ncpu_quota=$( get_quota "$cpu" )
+[ "$ncpu_quota" ] || ncpu_quota=$ncpu_online
+
+ncpu=$( printf "%s\n%s\n%s\n" "$ncpu_online" "$ncpu_quota" "$ncpu_cpuset" | 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 513d7c6..1c0f606 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 0000000..f2199ca
--- /dev/null
+++ b/mainline/alpine/30-tune-worker-processes.sh
@@ -0,0 +1,127 @@
+#!/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
+  ncpu=0
+  [ -f "$cpusetroot/cpuset.effective_cpus" ] || return
+  for token in $( tr ',' ' ' < "$cpusetroot/cpuset.effective_cpus" ); 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_cgroup_path() {
+  needle=$1
+  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"
+}
+
+ncpu_online=$( getconf _NPROCESSORS_ONLN )
+
+cpuset=$( get_cgroup_path "cpuset" )
+[ "$cpuset" ] && ncpu_cpuset=$( get_cpuset "$cpuset" )
+[ "$ncpu_cpuset" ] || ncpu_cpuset=$ncpu_online
+
+cpu=$( get_cgroup_path "cpu" )
+[ "$cpu" ] && ncpu_quota=$( get_quota "$cpu" )
+[ "$ncpu_quota" ] || ncpu_quota=$ncpu_online
+
+ncpu=$( printf "%s\n%s\n%s\n" "$ncpu_online" "$ncpu_quota" "$ncpu_cpuset" | 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 5fc76ef..c6eb9a5 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 0000000..f2199ca
--- /dev/null
+++ b/mainline/debian-perl/30-tune-worker-processes.sh
@@ -0,0 +1,127 @@
+#!/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
+  ncpu=0
+  [ -f "$cpusetroot/cpuset.effective_cpus" ] || return
+  for token in $( tr ',' ' ' < "$cpusetroot/cpuset.effective_cpus" ); 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_cgroup_path() {
+  needle=$1
+  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"
+}
+
+ncpu_online=$( getconf _NPROCESSORS_ONLN )
+
+cpuset=$( get_cgroup_path "cpuset" )
+[ "$cpuset" ] && ncpu_cpuset=$( get_cpuset "$cpuset" )
+[ "$ncpu_cpuset" ] || ncpu_cpuset=$ncpu_online
+
+cpu=$( get_cgroup_path "cpu" )
+[ "$cpu" ] && ncpu_quota=$( get_quota "$cpu" )
+[ "$ncpu_quota" ] || ncpu_quota=$ncpu_online
+
+ncpu=$( printf "%s\n%s\n%s\n" "$ncpu_online" "$ncpu_quota" "$ncpu_cpuset" | 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 2080b84..3299932 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 0000000..f2199ca
--- /dev/null
+++ b/mainline/debian/30-tune-worker-processes.sh
@@ -0,0 +1,127 @@
+#!/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
+  ncpu=0
+  [ -f "$cpusetroot/cpuset.effective_cpus" ] || return
+  for token in $( tr ',' ' ' < "$cpusetroot/cpuset.effective_cpus" ); 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_cgroup_path() {
+  needle=$1
+  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"
+}
+
+ncpu_online=$( getconf _NPROCESSORS_ONLN )
+
+cpuset=$( get_cgroup_path "cpuset" )
+[ "$cpuset" ] && ncpu_cpuset=$( get_cpuset "$cpuset" )
+[ "$ncpu_cpuset" ] || ncpu_cpuset=$ncpu_online
+
+cpu=$( get_cgroup_path "cpu" )
+[ "$cpu" ] && ncpu_quota=$( get_quota "$cpu" )
+[ "$ncpu_quota" ] || ncpu_quota=$ncpu_online
+
+ncpu=$( printf "%s\n%s\n%s\n" "$ncpu_online" "$ncpu_quota" "$ncpu_cpuset" | 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 fd707c5..60bd285 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 0000000..f2199ca
--- /dev/null
+++ b/stable/alpine-perl/30-tune-worker-processes.sh
@@ -0,0 +1,127 @@
+#!/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
+  ncpu=0
+  [ -f "$cpusetroot/cpuset.effective_cpus" ] || return
+  for token in $( tr ',' ' ' < "$cpusetroot/cpuset.effective_cpus" ); 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_cgroup_path() {
+  needle=$1
+  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"
+}
+
+ncpu_online=$( getconf _NPROCESSORS_ONLN )
+
+cpuset=$( get_cgroup_path "cpuset" )
+[ "$cpuset" ] && ncpu_cpuset=$( get_cpuset "$cpuset" )
+[ "$ncpu_cpuset" ] || ncpu_cpuset=$ncpu_online
+
+cpu=$( get_cgroup_path "cpu" )
+[ "$cpu" ] && ncpu_quota=$( get_quota "$cpu" )
+[ "$ncpu_quota" ] || ncpu_quota=$ncpu_online
+
+ncpu=$( printf "%s\n%s\n%s\n" "$ncpu_online" "$ncpu_quota" "$ncpu_cpuset" | 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 d86c2cc..6df7dde 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 0000000..f2199ca
--- /dev/null
+++ b/stable/alpine/30-tune-worker-processes.sh
@@ -0,0 +1,127 @@
+#!/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
+  ncpu=0
+  [ -f "$cpusetroot/cpuset.effective_cpus" ] || return
+  for token in $( tr ',' ' ' < "$cpusetroot/cpuset.effective_cpus" ); 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_cgroup_path() {
+  needle=$1
+  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"
+}
+
+ncpu_online=$( getconf _NPROCESSORS_ONLN )
+
+cpuset=$( get_cgroup_path "cpuset" )
+[ "$cpuset" ] && ncpu_cpuset=$( get_cpuset "$cpuset" )
+[ "$ncpu_cpuset" ] || ncpu_cpuset=$ncpu_online
+
+cpu=$( get_cgroup_path "cpu" )
+[ "$cpu" ] && ncpu_quota=$( get_quota "$cpu" )
+[ "$ncpu_quota" ] || ncpu_quota=$ncpu_online
+
+ncpu=$( printf "%s\n%s\n%s\n" "$ncpu_online" "$ncpu_quota" "$ncpu_cpuset" | 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 bd34cfb..397380a 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 0000000..f2199ca
--- /dev/null
+++ b/stable/debian-perl/30-tune-worker-processes.sh
@@ -0,0 +1,127 @@
+#!/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
+  ncpu=0
+  [ -f "$cpusetroot/cpuset.effective_cpus" ] || return
+  for token in $( tr ',' ' ' < "$cpusetroot/cpuset.effective_cpus" ); 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_cgroup_path() {
+  needle=$1
+  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"
+}
+
+ncpu_online=$( getconf _NPROCESSORS_ONLN )
+
+cpuset=$( get_cgroup_path "cpuset" )
+[ "$cpuset" ] && ncpu_cpuset=$( get_cpuset "$cpuset" )
+[ "$ncpu_cpuset" ] || ncpu_cpuset=$ncpu_online
+
+cpu=$( get_cgroup_path "cpu" )
+[ "$cpu" ] && ncpu_quota=$( get_quota "$cpu" )
+[ "$ncpu_quota" ] || ncpu_quota=$ncpu_online
+
+ncpu=$( printf "%s\n%s\n%s\n" "$ncpu_online" "$ncpu_quota" "$ncpu_cpuset" | 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 9998d13..90a797d 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 0000000..f2199ca
--- /dev/null
+++ b/stable/debian/30-tune-worker-processes.sh
@@ -0,0 +1,127 @@
+#!/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
+  ncpu=0
+  [ -f "$cpusetroot/cpuset.effective_cpus" ] || return
+  for token in $( tr ',' ' ' < "$cpusetroot/cpuset.effective_cpus" ); 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_cgroup_path() {
+  needle=$1
+  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"
+}
+
+ncpu_online=$( getconf _NPROCESSORS_ONLN )
+
+cpuset=$( get_cgroup_path "cpuset" )
+[ "$cpuset" ] && ncpu_cpuset=$( get_cpuset "$cpuset" )
+[ "$ncpu_cpuset" ] || ncpu_cpuset=$ncpu_online
+
+cpu=$( get_cgroup_path "cpu" )
+[ "$cpu" ] && ncpu_quota=$( get_quota "$cpu" )
+[ "$ncpu_quota" ] || ncpu_quota=$ncpu_online
+
+ncpu=$( printf "%s\n%s\n%s\n" "$ncpu_online" "$ncpu_quota" "$ncpu_cpuset" | 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 a4b6b7f..a4d932b 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
-- 
GitLab