Skip to content

Bringing your haproxy.cfg to a Janus node

A Janus node runs a stock HAProxy (3.4, AWS-LC as its TLS library, the Prometheus exporter built in) under janusd: an existing configuration mostly works as is. What differs is the machine around it - no syslog, no users, no shell to copy files with. This is what to change, then apply it with janusctl haproxy apply-config FILE or the Controller’s HAProxy › Configuration.

HAProxy's configuration on a node's page: the editor with the running haproxy.cfg - here examples/haproxy/web.cfg - marked as matching the running configuration, and HAProxy's state with Reload, Restart and StopHAProxy's configuration on a node's page: the editor with the running haproxy.cfg - here examples/haproxy/web.cfg - marked as matching the running configuration, and HAProxy's state with Reload, Restart and Stop

Its Backends tab shows each server’s state, and sets it - ready, drain, maintenance - at runtime:

The Backends tab: the app backend's two servers, app1 and app2, up, each with Ready, Drain and MaintThe Backends tab: the app backend's two servers, app1 and app2, up, each with Ready, Drain and Maint

On a distributionOn a Janus nodeWhy
log /dev/log local0log stdout format raw local0No syslog daemon. janusd keeps HAProxy’s output: janusctl system logs haproxy, the Controller’s logs page
user haproxy / group haproxyuid 1000 / gid 1000No /etc/passwd to resolve names against
chroot /var/lib/haproxychroot /var/emptyThe empty directory janusd prepares
daemon(remove it)janusd supervises HAProxy in the foreground
stats socket /run/haproxy/admin.sock ...stats socket /run/janus/haproxy-admin.sock mode 660 level adminjanusd’s own runtime API goes through this socket - it must be there, at this path, with level admin
crt-base, ca-base(remove, use full paths)Your files live in /etc/haproxy/files/
ssl-dh-param-file(remove it)AWS-LC has no DHE ciphers - see TLS

HAProxy is built against AWS-LC rather than OpenSSL 3, whose locking slows HAProxy down as soon as several threads handshake - HAProxy’s own recommendation. Measured with HAProxy on 1 to 4 threads: 1.4 to 7 times more TLS handshakes per second than with OpenSSL 3.5, the gap growing with the threads. Certificates and crt-lists (runtime updates included), client-certificate verification with CRLs, SNI, ALPN, TLS 1.2/1.3, X25519MLKEM768 and the TLS sample fetches behave as with OpenSSL - except ssl_fc_curve, which names the NIST curves prime256v1/secp384r1 (OpenSSL: SECP256R1/SECP384R1), and the default TLS 1.2 preference, AES-128-GCM before AES-256-GCM. What AWS-LC doesn’t have, and the node refuses:

KeywordDo instead
ssl-dh-param-file, tune.ssl.default-dh-param (ignored, with a warning)Remove: no DHE
A ciphers/ssl-default-*-ciphers list made only of DHE-* ciphersAdd ECDHE ciphers - in a mixed list (Mozilla’s intermediate profile) the DHE ones are just skipped
X448, ffdhe*, brainpool* in curves/ssl-default-*-curvesRemove them from the list (the whole list is refused otherwise)
client-sigalgs, ssl-default-bind-client-sigalgs, ssl-default-server-client-sigalgsRemove (sigalgs works)
ssl-security-levelRemove; restrict with ssl-min-ver, ciphers and curves
ssl-mode-async, ssl-engine, ssl-provider, ssl-propqueryRemove: no engines or OpenSSL 3 providers

Fix such a configuration before updating a node that runs it on an OpenSSL release. Updated from the Controller (automatic revert, on by default) or with janusctl lifecycle upgrade -wait-for-health, a node whose HAProxy refuses its configuration goes back to its previous version on its own.

Error pages, maps, ACL lists, Lua, certificates you renew yourself: upload them as HAProxy’s files and reference /etc/haproxy/files/<name>. Let’s Encrypt certificates: the letsencrypt extension obtains them, in /etc/haproxy/acme/; its HTTP-01 rule uses ${JANUS_ACME_THUMBPRINT} rather than a thumbprint written in the configuration.

Backend server names (server app app.example.net:80) are resolved when HAProxy starts, through the node’s resolvers (its network configuration, or DHCP’s).

HAProxy may use up to 524288 open files - the limit systemd gives a service on a mainstream distribution. Without maxconn or ulimit-n in the global section, it sizes itself from that: about 262000 connections, and around 50 MB of memory for the tables, used or not. On a small node, fd-hard-limit 65536 (32000 connections, about 20 MB) or an explicit maxconn keeps it lighter. A ulimit-n or maxconn above that limit is refused: haproxy -c accepts it, but HAProxy wouldn’t start - the node reports HAProxy’s reason and keeps the configuration and the process it had.

Every configuration goes through haproxy -c first, then through a real start: a configuration HAProxy refuses at either step changes nothing. Reloads are seamless (-sf), and the configuration is kept on the node across reboots and updates.