You could try this to see if your VyOS can generate a proper nftables output on its own.
Note! Only do this on a box you have physical access to (in case something goes south and you need to restore the file and reboot the box again).
The below has been tested on VyOS 1.4-rolling-202307250317 but the file in question doesnt seem to have changed since 11 jan 2021 so I guess we should be safe to test this 
-
Take backup of this file: /usr/share/vyos/templates/firewall/nftables-zone.j2 (better safe than sorry
)
-
Then modify it so you change this:
iifname { {{ zone[from_zone].interface | join(",") }} } counter return
into this:
iifname { {{ zone[from_zone].interface | join(",") }} } return
that is remove “counter” from those lines and then save the file.
The above should exist 3 times in total at around line 45, 51 and 70.
-
Try to reboot the box.
-
After reboot try to do the optimizer tests again:
sudo nft -s list ruleset > /config/v2ruleset.txt
sudo sed -i '1s/^/flush ruleset\n\n/' /config/v2ruleset.txt
sudo nft -o -f /config/v2ruleset.txt
sudo nft -s list ruleset > /config/v2ruleset2.txt
If everything went ok you should be able to load the “v2ruleset.txt” without errors.
And when editing “v2ruleset.txt” and scroll down to the VZONE definitions you should see that they look something like:
chain VZONE_wg8 {
iifname "wg8" counter return
iifname "eth1" counter jump NAME_lan-wg8
iifname "eth1" return
iifname "eth3" counter jump NAME_mullvadgb-wg8
iifname "eth3" return
iifname "eth2" counter jump NAME_mullvadus-wg8
iifname "eth2" return
iifname "eth0" counter jump NAME_wan-wg8
iifname "eth0" return
iifname "wg0" counter jump NAME_wg0-wg8
iifname "wg0" return
iifname "wg1" counter jump NAME_wg1-wg8
iifname "wg1" return
iifname "wg7" counter jump NAME_wg7-wg8
iifname "wg7" return
counter drop comment "zone_wg8 default-action drop"
}
That is only the first iifname have a “counter return” while the others just have “return”.
Im not sure if this is an error in VyOS or nftables - Im leaning towards nftables because it shouldnt output a config it doesnt like to load.
Most likely the error is in the optimizer itself and doing the above manipulation of how VyOS generates the zone-rules should be seen as a workaround until whatever is broken in the optimzier is fixed upstream.
Note that I didnt dig into if removing the counters would have any negative impact. What you miss is statistics regarding which packets ended up in the jump and never returned (accepted or dropped by the jump) vs. which one returned and was returned out of the VZONE chain.
Note according to docs of nftables doing a final return equals to accept so I think it should be further investigated if the current nftables config that VyOS produces is actually being correct or not.