FastCGI worker pools
Persistent per-site worker pools for the visitor-facing hot path.
FastCGI worker pools
The page processor is dual-mode. Invoked as plain CGI it behaves exactly as it always has; spawned with a FastCGI listen socket on fd 0 (the spawn-fcgi convention) it services requests from a persistent accept loop instead. Modules compile once per worker, per-request state resets inside the loop, and both paths share the same request handler - a pooled site renders pages identically to a CGI one.
What runs persistent
One pool per site serves the anonymous, visitor-facing pages - the hot
path. The auth wrapper keeps its per-request CGI design, so
session-bearing traffic (login, the manager, WebDAV, plugin endpoints)
stays on the CGI path; the shipped vhost patterns carve those requests
out to the CGI wrapper. Because DOCROOT is fixed at spawn, one pool
serves exactly one site and isolation stays process-level.
Measured effect
On the reference host, as the mean of 50 cache-hit requests with one worker: 62.2 ms as plain CGI, 0.4 ms pooled. The CGI figure is almost entirely process start-up, which the accept loop amortises away.
Plain-CGI equivalence
Plain CGI remains the default, the dev-server path, and the
zero-dependency fallback - its output is byte-identical to the
pre-pool behaviour. FCGI.pm is lazy-required, so a CGI-only host needs
no FastCGI modules at all. The pool needs libfcgi-perl (plus
libfcgi-procmanager-perl for prefork), both Recommends of the
lazysite-common package. Wherever no pool is configured, plain CGI
keeps working untouched.
Opting a site in
On a packaged host, describe the pool in /etc/lazysite/pools/ and
enable the lazysite@ template unit for the domain:
# /etc/lazysite/pools/example.com.conf
DOCROOT=/home/siteuser/web/example.com/public_html
USER=siteuser
# optional: GROUP= (default www-data), WORKERS= (default 2),
# MAX_REQUESTS= (default 500), SOCKET=
systemctl enable --now lazysite@example.com
The unit starts the pool launcher as root; the launcher binds
/run/lazysite/example.com.sock (mode 0660, connectable by the web
server group), drops privileges to the site user - USER=root is
refused - and execs the processor with the socket on fd 0. Root never
remains in the request path and nothing writes into the site tree.
Then point the web server at the socket. Commented vhost-fcgi
examples ship in the lazysite-apache and lazysite-nginx packages;
for Apache mod_proxy_fcgi the core of it is:
SetHandler "proxy:unix:/run/lazysite/example.com.sock|fcgi://localhost/"
On HestiaCP, lazysite-hestia-domain add USER DOMAIN --fcgi writes the
pool config, enables lazysite@DOMAIN and selects the lazysite-fcgi
web template in one step.
Tuning
WORKERS- prefork size via FCGI::ProcManager; the pool launcher defaults to 2. WithWORKERS=0a single worker runs, exits afterMAX_REQUESTSrequests, and systemd'sRestart=respawns it.MAX_REQUESTS- a worker recycles after this many requests (default 500); memory hygiene for long-lived processes.
Edit the pool file, then systemctl restart lazysite@<domain>.