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>
group_vars/all 📦
This directory contains the Ansible group_vars applied to every host in the inventory. It holds the shared baseline that every role and every host-specific var can build on top of.
Scope 🎯
Variables in this directory MUST apply across all hosts and all roles. Typical examples are the software identity, deployment toggles, domain defaults, the central network and port registries, OIDC and LDAP endpoints, design tokens, and resource defaults.
Role-specific configuration MUST NOT live here, even when only one role reads it today. Such values belong in the corresponding role:
roles/<role>/meta/services.ymlfor anything an operator may override, because the inventory'sapplications.<role>block is deep-merged on top of it andlookup('config', application_id, '<path>')reads the result.roles/<role>/vars/main.ymlfor values derived from that configuration, and for those the role fixes. Role vars outrank inventory variables, so a value placed here cannot be overridden per host.
File naming and load order 🔢
Files MUST use the pattern NN_<topic>.yml, where NN is a zero-padded two-digit numeric prefix. Ansible loads files in lexicographic order, so the prefix determines the load order and lets later files reference values defined earlier.
Prefixes MUST be unique and sequential without gaps. When you add, remove, or reorder a file, you MUST update every hard-coded reference in code, tests, and documentation to keep the sequence consistent. You SHOULD grep for group_vars/all/<old-name>.yml before renaming.