Files
computer-playbook/roles/svc-prx-openresty/templates/compose.yml.j2
kevinveenbirkenbachandClaude Opus 5.5 f52da1c875 fix(svc-prx-openresty): size worker_connections independently of pids_limit
The shared proxy's nginx.conf derived worker_connections from the
openresty container's pids_limit. pids_limit caps processes and threads,
worker_connections caps simultaneous sockets per worker (client and
upstream), so with pids_limit 256 and one worker the whole host's proxy
held about 256 sockets, roughly 128 proxied requests in flight across
every vhost. Raising pids_limit to buy connections would loosen the
fork-bomb guard.

svc-prx-openresty now declares services.openresty.worker_connections
(4096 per worker, the common nginx production range, enough for several
chunk-heavy apps such as OpenWebUI at ~150-170 requests per page load).
WEBSERVER_PROXY_WORKER_CONNECTIONS reads it through lookup('config'),
without a default, so a missing key fails loudly. pids_limit stays 256.

WEBSERVER_PROXY_WORKER_RLIMIT_NOFILE is derived as 2x worker_connections
(8192), one descriptor for each client and upstream socket of a proxied
request, and nginx.conf renders it as worker_rlimit_nofile. The openresty
container pins its nofile ulimit (soft and hard) to the same value:
workers call setrlimit as root without CAP_SYS_RESOURCE, so they can only
lower the hard limit, and the inherited hard limit depends on the host
daemon (Docker >= 25 defaults to 1024:524288, DiD nests whatever the
outer container has). An explicit ulimit makes the rlimit succeed on
every host instead of logging a setrlimit alert.

The unit test pins worker_connections 4096, worker_rlimit_nofile 8192 and
the container ulimit, and fails when worker_connections follows
pids_limit again.

Live proof (compose deploy of web-svc-html, both plays failed=0): the
deployed nginx.conf renders worker_rlimit_nofile 8192 and
worker_connections 4096; openresty's master, worker and cache manager
run with "Max open files 8192 8192"; the openresty log carries no
setrlimit or "worker_connections are not enough" line.

Closes #1300

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 03:00:03 +01:00

35 lines
1.7 KiB
Django/Jinja

{% include 'roles/sys-svc-compose/templates/base.yml.j2' %}
{% set service_update_order = 'stop-first' %}
{% set service_name = 'openresty' %}
{{ service_name }}:
{% include 'roles/sys-svc-container/templates/base.yml.j2' %}
{{ lookup('template', 'roles/sys-svc-container/templates/build.yml.j2') | indent(4) }}
args:
OPENRESTY_IMAGE: "{{ OPENRESTY_IMAGE }}"
OPENRESTY_VERSION: "{{ OPENRESTY_VERSION }}"
PROMETHEUS_ENABLED: "{{ OPENRESTY_PROMETHEUS_ENABLED }}"
LUA_PROMETHEUS_URL_MAIN: "{{ OPENRESTY_LUA_PROMETHEUS_URL_MAIN }}"
LUA_PROMETHEUS_URL_COUNTER: "{{ OPENRESTY_LUA_PROMETHEUS_URL_COUNTER }}"
LUA_PROMETHEUS_URL_KEYS: "{{ OPENRESTY_LUA_PROMETHEUS_URL_KEYS }}"
{{ lookup('compose_only', 'container_name', OPENRESTY_CONTAINER) }}
{{ lookup('container_image', application_id, 'openresty') }}
{% if DEPLOYMENT_MODE == 'swarm' %}
{{ lookup('container_ports',
{'service': 'openresty', 'protocol': 'http', 'mode': 'host'},
{'service': 'openresty', 'protocol': 'https', 'mode': 'host'}) | indent(4) }}
{{ lookup('container_extra_hosts', extra_hosts=['host.docker.internal:host-gateway']) | indent(4) }}
{{ lookup('container_networks') | indent(4) }}
{% else %}
network_mode: "host"
{% endif %}
{{ lookup('container_volumes', service_name) | indent(4) }}
ulimits:
nofile:
soft: {{ WEBSERVER_PROXY_WORKER_RLIMIT_NOFILE }}
hard: {{ WEBSERVER_PROXY_WORKER_RLIMIT_NOFILE }}
command: ["openresty", "-g", "daemon off;"]
{{ lookup('container_healthcheck', service_name) | indent(4) }}
{{ lookup('compose_volumes') }}
{{ lookup('compose_networks') }}