Dynamic backend switching using nbsrv with Host header

I am using haproxy 2.4.7, and I would like to dynamically route to the correct backend, if the backend has available servers.
I struggle to find the correct syntax using nbsrv().

The error I am getting is:

# haproxy -c -f /etc/haproxy/haproxy.cfg
[NOTICE]   (31364) : haproxy version is 2.4.7-b5e51a5
[NOTICE]   (31364) : path to executable is /sbin/haproxy
[ALERT]    (31364) : parsing [/etc/haproxy/haproxy.cfg:18] : error detected while parsing switching rule : missing comma after fetch keyword 'nbsrv' in ACL expression 'nbsrv(%[req.hdr(host)])'.
[ALERT]    (31364) : Error(s) found in configuration file : /etc/haproxy/haproxy.cfg
[ALERT]    (31364) : Fatal errors found in configuration.

The error missing comma kind of doesn’t make sense to me.

How can this be done?
Here is my example config:

    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    log         /dev/log local0 debug
    user        haproxy
    group       haproxy
    default-path config

    timeout connect         10s
    timeout client          1m
    timeout server          1m

frontend EXT_WEB
    mode http
    use_backend %[req.hdr(host)]   if { hdr(host) -i -f acls/ishost_applications.acl } { nbsrv(%[req.hdr(host)]) gt 0 }

backend app01.test.net
    mode http
    server app01.test.net

backend app02.test.net
    mode http
    server app02.test.net

The acl file has the following contents:

# cat acls/ishost_applications.acl

Many thanks

All of the following variations failed with an error:

acl ishost_app  hdr(host) -i -f acls/ishost_applications.acl
http-request set-var(txn.hdr_host) hdr(host)

use_backend %[req.hdr(host)]  if ishost_app { nbsrv(hdr(host)) gt 0 }
use_backend %[req.hdr(host)]  if ishost_app { nbsrv(req.hdr(host)) gt 0 }
use_backend %[req.hdr(host)]  if ishost_app { nbsrv(%[req.hdr(host)]) gt 0 }
use_backend %[req.hdr(host)]  if ishost_app { nbsrv(var(req.hdr(host))) gt 0 }
use_backend %[req.hdr(host)]  if ishost_app { nbsrv(%[var(req.hdr(host))]) gt 0 }

use_backend %[req.hdr(host)]  if ishost_app { nbsrv(var(txn.hdr_host)) gt 0 }
use_backend %[req.hdr(host)]  if ishost_app { nbsrv(%[var(txn.hdr_host)]) gt 0 }
use_backend %[req.hdr(host)]  if ishost_app { nbsrv(%[txn.hdr_host]) gt 0 }
use_backend %[req.hdr(host)]  if ishost_app { nbsrv(txn.hdr_host) gt 0 }

After all, is nbsrv() even capable of dealing with fetches or variables, or does it only work with plain, static strings?

Any comments appreciated.

Did you discover any solution to this problem? I struggled about this just a few days ago, since the docs state:

Takes an input value of type string, interprets it as a backend name and
returns the number of usable servers in that backend. Can be used in places
where we want to look up a backend from a dynamic name, like a result of a
map lookup.

This was excatly what I was trying to do, but as in your examples, no way of passing in a variable/fetch etc. was parsed correctly.

Unfortunately, I have not yet discovered a solution.
I will try to ask the question in the Slack space.

When dealing with dynamic values, you want to use the nbsrv converter and not the sample fetch.
The nbsrv sample fetch arguments must be backend names known at config check time.


use_backend %[hdr(host)] if { hdr(host),nbsrv gt 0 }

1 Like

Many thanks jerome!
I tested it and it works.