As a general rule it’s a mistake to put a stateful firewall in front of a DNS server (at least for UDP). Sadly it’s one of those nuggets of operational knowledge that keeps being rediscovered the hard way.
OTOH, pf can handle very large state tables with ease. I also use a cluster of pf boxes to front-end a network with 20000+ users and it rides around 500000 to 800000 states every day.
on my big-boi pf NAT cluster, i keep the udp timeout relatively low to deal with state table congestion:
set limit { states 1000000, src-nodes 100000 } set state-defaults pflow, sloppy set state-policy floating set timeout src.track 240 set timeout udp.multiple 120