Chroot can't quit

I’m attempting to harden a forwarding proxy that currently runs on a standard Trixie deployment with haproxy version 3.0.11-1+deb13u2.
When I try to chroot it, the control processes timeout.
In an attempt to isolate the problem I’ve mocked a simple test system but can’t seem to get the worker process to quit with systemd or socat.
Gemini concluded the client wasn’t listening and has advised me to change the unit file settings:

[Service]
# Pivot to simple to stop waiting for unresponsive notify signals
Type=simple

# Force immediate termination since workers are deaf inside the chroot
KillSignal=SIGKILL
TimeoutStopSec=2s

# Standard Hardening & Capabilities
RuntimeDirectory=haproxy
RuntimeDirectoryMode=0750
AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_CHROOT CAP_SETGID CAP_SETUID
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_CHROOT CAP_SETGID CAP_SETUID

# Map the journal socket for visibility
BindReadOnlyPaths=/run/systemd/journal/dev-log:/var/lib/haproxy/dev/log

But a few socat commands to the worker proved it was healthy and responsive but didn’t terminate when sent the quit command.
This is the mock haproxy.cfg:


global
        log /dev/log    local0
        chroot /var/lib/haproxy
        stats socket /run/haproxy/admin.sock mode 660 level admin
        stats timeout 30s
        user haproxy
        group haproxy
        master-worker

        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private

        ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
        ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
        ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

defaults
        log     global
        mode tcp
        timeout connect 5s
        timeout client 50s
        timeout server 50s
        option tcplog
        option  dontlognull
        errorfile 400 /etc/haproxy/errors/400.http
        errorfile 403 /etc/haproxy/errors/403.http
        errorfile 408 /etc/haproxy/errors/408.http
        errorfile 500 /etc/haproxy/errors/500.http
        errorfile 502 /etc/haproxy/errors/502.http
        errorfile 503 /etc/haproxy/errors/503.http
        errorfile 504 /etc/haproxy/errors/504.http

frontend tproxy_test
    bind *:8080 transparent
    default_backend local_dummy

backend local_dummy
    server loopback 127.0.0.1:9999

This is an ongoing test and I’m just reaching out to see if anyone here has a better solution than Gemini

When you install haproxy on Debian Trixie, haproxy is already chrooted into /var/lib/haproxy and everything will just work.

root@debian-4gb-fsn1-1:~# grep chroot /etc/haproxy/haproxy.cfg
         chroot /var/lib/haproxy
root@debian-4gb-fsn1-1:~#

Nothing else is needed.