VyOS Networks Blog

Building an open source network OS for the people, together.

VyOS Project August 2026 Update

Taras Pudiak
Posted 3 Sep, 2026

Hello, Community!

In August, the VyOS development branch saw work in a few directions at once. Contributors outside the core team landed new firewall capabilities, a dynamic DNS overhaul, NTP options, and fixes for bugs that only turned up once someone ran a configuration nobody had tried before.

Interfaces that keep their names

Interface naming used to be decided from a single per-device udev event, which fires before all the hardware has finished enumerating. Whatever had managed to announce itself by that moment determined the outcome, so on multi-vendor PCIe systems, interfaces could be lost or misnamed. You would meet it whenever names were being allocated with nothing to match against - on first boot, or after a NIC replacement - and also on hardware where a card is simply slow to initialize, where a late-arriving NIC could lose the race on an otherwise ordinary reboot and come back reordered or with a second hardware-id entry against its name. On hardware that comes up promptly, a card VyOS has already seen keeps the name pinned to its MAC address, which is why most users will never have run into any of this.

Naming is now decided in one authoritative pass, run once the configuration is available during router startup. It waits for the configured hardware, applies every hardware-id binding you have set, and names whatever is left by PCIe distance from the root complex and MAC address - producing the same result on every boot. If a card your configuration expects is simply not there anymore - pulled, failed, or moved to another slot - boot now reports that interface as unresolved, instead of leaving you to work it out later from whatever stops working because of it.

NIC replacement is handled conservatively, and deliberately so. An interface whose hardware ID has been deleted inherits its previous configuration only when it is the sole pending node of its type that boots and exactly one candidate exists. Anything more ambiguous is left pending and reported rather than guessed: with only a MAC address and a PCIe position to go on, there is no way to tell which of several unconfigured NICs was genuinely the one you replaced, and a wrong binding writes itself back and becomes permanent on every later boot. A candidate that is not matched this way is not lost - it still receives an ordinary bootstrap name, it simply never inherits another node's settings.

More details: https://vyos.dev/T3871

Two smaller platform changes belong here as well. The kernel now includes the driver for MANA, Azure's own paravirtual network device, which is effectively a requirement for Azure images. And on Azure with Accelerated Networking, the helper that names virtual-function interfaces now runs from initramfs. Deployments with more than one accelerated NIC could previously end up with an interface that received no traffic and no address at all, or with two interfaces claiming the same MAC - the same story as above, with the association between hardware and name being settled too late.

More details: https://vyos.dev/T9138https://vyos.dev/T8329

Firewall

Remote groups - the firewall groups whose contents are downloaded from a URL - can now be given their own update interval:

set firewall group remote-group <name> interval <value>

The value accepts plain seconds or a suffix of s, m, h, d, or w, so 4h is valid, with a range from 60 seconds to four weeks. Previously, every remote group followed the global firewall global-options resolver-interval, which also drives domain-group and FQDN resolution - a single knob for two quite different jobs. A blocklist that its publisher updates daily and an FQDN that changes every few minutes no longer have to share a schedule. Groups with no interval set keep following the global value, so nothing changes until you ask for it.

More details: https://vyos.dev/T9076

Protocol values in rules are now normalized to protocol names before validation, which fixes a genuinely confusing failure. Writing a protocol by its IANA number worked on its own, but combining it with a port, TCP flags, GRE options, or synproxy was rejected - protocol 6 with a destination port produced "Protocol must be tcp, udp, or tcp_udp when specifying a port", because the checks compared against names, while you had given a number. Numbers and names now behave identically, and a number with no known name is kept as it is. Rule validation errors also became more informative, and now tell you which rule number failed - a small thing that stops being small when a rule set has two hundred rules in it.

More details: https://vyos.dev/T8247

PKI and certificate handling

A cluster of improvements landed around PKI, and the most consequential one fixes an ACME certificate quietly losing its chain.

When you use ACME - Let's Encrypt and similar services - the certificate you receive comes together with an intermediate CA certificate, and a client needs both of them to decide that your certificate is trustworthy. VyOS finds that intermediate and adds it to your configuration for you.

The problem was one of timing inside a single commit. HAProxy and the HTTPS server are set up in the same commit that fetches the certificate, but they were reading a copy of the configuration made just before the intermediate was added. So they never saw it, and served your certificate on its own, which is exactly the situation where a client that does not already know the intermediate refuses to connect. Renewals skipped the step that adds it altogether, so a certificate left in that state stayed that way. VyOS now re-reads the configuration after adding the intermediate, so the services configured afterward can see it.

The rest of the ACME work is about what happens when things go wrong, which is usually at renewal time rather than when you first set everything up:

  • Certbot often hands over more than one certificate in its chain - an intermediate plus the root that issued it. Only the first was kept, so the chain VyOS served was shorter than the one Certbot had already provided, and completing it meant configuring a root CA by hand. All of them are kept now.
  • That intermediate used to be written into your running configuration as an automatically created CA entry - something visible in your config, and deletable, that was really certbot's own internal state. It is now read from disk whenever a chain is needed, so it no longer appears in your configuration at all. A CA you configured yourself still takes precedence.
  • Because the chain is read from disk rather than stored, a change of intermediate by your CA - something Let's Encrypt does from time to time - is now picked up on its own at the next renewal. The copy kept in the configuration used to go on being served after the CA had moved to a new intermediate, and that is the sort of thing you find out about from a client that will not connect.
  • Between configuring an ACME certificate and its first successful issuance - or after a request has failed - the certificate file does not exist yet. Anything that had to build a chain from it crashed, including the show PKI. It no longer does.
  • Before requesting a replacement certificate, VyOS backs up the current one. An ordinary failure - a rejected request, a failed domain validation - restored that backup correctly. But if the attempt went wrong in a way the recovery path did not anticipate, where the request could not even be made, rather than being made and refused, the restore was skipped, and you were left with no certificate at all, rather than the one you had. Any failure now restores the backup.
  • When a request fails, you can now see why - rate limiting, a failed domain validation, whatever certbot actually reported. Previously, the CLI showed the command that had been run and its exit code, which tells you nothing about what to fix.

On the operational side, you can now inspect a stored CA or certificate in human-readable form with

show pki ca <name> text
show pki certificate <name> text

Both commands complete the names of the CAs and certificates you actually have, instead of requiring you to remember them. Importing a CA certificate from a remote URL is supported, and a file:// URL now resolves correctly.

Dynamic DNS

The ddclient integration received a substantial overhaul, contributed by a community member, together with an upgrade to ddclient 4.0.0. Porkbun is supported as a provider, luadns and desec can now be used for dual-stack configurations, and Gandi requires a personal access token, following the provider's own change. Protocol configuration was reworked, unsupported protocols are blocked at commit time rather than failing later, and two providers that no longer exist - googledomains and woima - were removed along with a set of legacy configuration properties that no longer did anything.

More details: https://vyos.dev/T6981

Traffic policy

The set-dscp option for shaper policies has existed in the CLI for some time, but nothing implemented it - no traffic control commands were generated, so packets were never remarked. It works now, using the correct field for each protocol, preserving ECN bits, recalculating the IPv4 header checksum afterward, and skipping non-IP traffic so that packets such as ARP are not corrupted.

Alongside it, two QoS bugs were fixed. A shaper class matching the Ether protocol with anything other than the default - arp, for example, crashed the commit outright. And applying an egress policy to an interface silently removed that interface's ingress redirect: a download shaper built by redirecting to an ifb device stopped shaping altogether, and traffic ran at full line rate, while the commit reported success. This one was a regression - it arrived in rolling on 2026-06-23, with the change that re-applies QoS configuration on dynamic interfaces. If you shape download traffic that way and see it quietly stop working, that is why.

More details: https://vyos.dev/T8996, https://vyos.dev/T9134, https://vyos.dev/T9080

Build and supply chain

The SBOM files shipped with rolling images now describe their contents accurately, which they previously did not. The tool that generates them works out which packages are installed by first identifying the operating system. VyOS reports itself as vyos and never mentions that it is built on Debian, so the tool did not know to look for Debian packages, and the list it produced was wrong. The build now tells it, and the inventory matches what is really in the image. Each document also names the image and version it was generated from, which starts to matter as soon as you keep more than one of them. The CycloneDX output is pinned to schema 1.6, so your tooling knows what it is parsing. The CycloneDX file is considerably smaller now, with per-file detail dropped, while the SPDX file keeps full file-level cataloging - so you can feed each to whatever suits it. And if SBOM generation fails, the image build now fails with it, instead of quietly producing an image without one.

The kernel packaging was also reworked to use real Debian packaging rather than fpm. This is invisible from the CLI, but it makes the kernel packages behave like every other package in the system.

More details: https://vyos.dev/T9203, https://vyos.dev/T9181

Smaller conveniences

The following command now offers to carry your bash history across, alongside the existing prompts for configuration and SSH host keys:

add system image 
Previously, it was silently discarded, and every upgrade left operators with an empty history.
  • The live image boot screen now shows version and flavor information, so you can tell which ISO you are actually booting instead of inferring it from the splash artwork.
  • The DHCP server's interface-mtu option was capped at 9000 - just below the 9216 that datacenter jumbo fabrics typically run - so a router with MTU 9216 on its LAN could not hand clients a matching value. The limit is now 16000, the same maximum VyOS allows on an interface.
  • Containers can be given the chown capability.
  • NTP client requests can be pinned to a source address or a source interface. On a router with several upstreams, the kernel picks the source address from whichever route wins, which on an exchange fabric with non-globally-routable addressing means the replies never find their way back - intermittently, and only when best-path selection happens to shift. This is the same idea as BGP's update-source, applied to the NTP client.

More details: https://vyos.dev/T9021, https://vyos.dev/T9140, https://vyos.dev/T9093, https://vyos.dev/T9184, https://vyos.dev/T9159

Community activities

This period was a strong one for community contributions - much of what is described above came from outside the core team. A few of them are worth telling properly:

Every firewall accumulates rules nobody is willing to delete, because nobody can prove they are unused. Rules can now be marked with:

set firewall ... rule <n> last-used

The detailed rule output gains a column showing when the rule last matched traffic. It is a small addition that answers a question every operator of a long-lived rule set eventually has to ask.

Silence is the worst way for a feature to fail. When flow-accounting moved from pmacct to the ipt_NETFLOW kernel module, system flow-accounting vrf quietly stopped having any effect: pmacct was a daemon and could be wrapped in ip vrf exec, but NetFlow is now emitted by the kernel, and nothing bound the module's export socket to the VRF. A collector reachable only inside a VRF - the usual out-of-band management design - received nothing at all, and the commit reported no error. Configurations produced by the flow-accounting migration script were affected, too, so an upgrade could stop your flow export without saying so. The export socket is now bound to the VRF device, with a per-server source-interface taking precedence where one is set. One caveat worth checking before you upgrade: a configuration that names a VRF but whose collector is actually reached through the default routing table works today only because the setting was ignored, and will stop working once it is honored.

One related change comes with it: a VRF can no longer be named where a source interface is expected. Neither Cisco nor Juniper sources a flow exporter from a VRF - both use a routed interface - and VyOS now works the same way, rejecting it with a clear message instead of the misleading "is not a member of VRF" error it used to produce. The VRF itself is still configured under the system flow-accounting VRF, as before.

Some bugs only appear on the second of something. Committing a second NHRP tunnel with redirect enabled failed outright with "Failed to apply NHRP tunnel firewall rules". The redirect chain is rendered per tunnel in a loop, but the nftables meter name inside it was hardcoded - so the second tunnel declared a meter that already existed, nftables rejected the whole atomic ruleset load, and the commit failed. The meter name is now derived from the loop index. Single-tunnel setups are unchanged, which is why nobody noticed until someone built the second tunnel.

A subtle one, and a good illustration of why timing bugs are hard to catch. While a commit held the lock, the netlink daemon slept instead of reading its socket, so kernel messages queued up rather than being consumed. When the commit finished, an event that was no longer true could be processed as if it had just arrived: an interface DOWN from a disable that had already been reversed. The reporter hit it on a VRRP pair whose transition scripts disable and re-enable the WAN interfaces about twenty seconds apart, and the stale event killed the DHCP client on an interface that was by then enabled and up - a silent WAN outage on the HA master, with nothing in the system able to notice or recover from it. The daemon now keeps draining the socket during a commit and discards what it reads, so nothing stale outlives the commit that masked it.

Thank you to everyone who contributed this period - the code, the reports, and the research behind them.

The post categories:

Comments