VyOS Platform Blog

VyOS Project September 2026 Update

Written by Taras Pudiak | September 30, 2026, 11:30:00 PM Z

Hello, Community!

Over the last month, two well-known subsystems changed the way they work. OpenVPN acquired a kernel data path that carries traffic. The REST API acquired authentication methods beyond a shared key. Much of everything else came from outside the core team, including the OpenVPN work, which was contributed by an upstream OpenVPN developer.

OpenVPN gets a kernel data path

OpenVPN has carried its data channel in userspace for its whole life, which is why it has always been one of the slowest VPN options VyOS offers. Data Channel Offload (DCO) moves encryption and packet forwarding into a kernel module, leaving userspace to handle only the control channel.

The offload dco node has existed in the VyOS CLI for some time, but it did nothing. OpenVPN 2.6, which VyOS shipped, speaks only to an older kernel module, so enabling the offload did not change how your traffic was handled. VyOS now builds OpenVPN 2.7 and requires it, so the offload is real:

set interfaces openvpn vtun0 offload dco

If you already have such a configuration saved, you do not need to take any action before upgrading. The migration removes the offload request from any configuration that would otherwise fail to commit, and your tunnel keeps working, in userspace, as it did before.

Pay attention: the kernel module cannot serve everything the userspace data path could. It supports only layer 3 TUN devices (no TAP, so no bridge mode), has no decompression, has no static-key data path, and does not implement any ciphers except AES-GCM. A tap interface, a shared-secret tunnel, any compression setting, and CBC ciphers are all incompatible with the offload, and a configuration that combines them with offload dco is now rejected at commit time.

More details: https://vyos.dev/T8264, https://vyos.dev/T9330

Authenticating to the REST API

Until now, the VyOS REST API knew one way to identify a caller: an API key, sent in the request body or in an X-API-Key header. That is workable for a script you wrote yourself and awkward for everything else. It means a long-lived shared secret in every automation system that touches the router, with no expiry and no relationship to whatever identity system you already run.

Three additional methods landed this period.

A short-lived bearer token can be minted from an API key by posting it to a new /token endpoint and used in an Authorization: Bearer header afterward. 

The lifetime and the signing secret length are configurable, and default to one hour and 32 bytes:

set service https api rest authentication expiration <60-31536000>
set service https api rest authentication secret-length <16-65535>

An external identity provider can issue the token instead - Keycloak, Entra ID, Okta, Vault - and VyOS validates it directly against the provider's JWKS endpoint:

set service https api rest authentication oidc issuer <url>
set service https api rest authentication oidc jwks-url <url>
set service https api rest authentication oidc audience <txt>

Finally, the router can authenticate callers using a client certificate. nginx requests a certificate and verifies it against a CA you nominate, and a caller presenting a valid one needs no key or token at all:

set service https certificates ca-certificate <name>
set service https certificates verify-client <optional|required>

Client certificate authentication works under TLS 1.2 only. That limitation may be relaxed in the future.

The mTLS work also shipped a flaw of its own, which is fixed. Between 2026-08-26 and 2026-09-12, Rolling builds trusted the X-Client-Verify header unconditionally, so on a router where mTLS was not configured, a caller could supply that header themselves and skip API key authentication entirely. No LTS release ever carried the mTLS code.

All of this is groundwork for a REST-based Ansible collection, an alternative to the SSH and CLI-driven vyos.vyos collection.

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

IPsec

Interoperability failures are the most annoying class of VPN bug, because the two sides disagree about something neither of them will tell you about. This period fixed one of exactly that shape.

ESN (Extended Sequence Numbers) belongs in a CHILD_SA proposal and has no meaning in an IKE_SA proposal. VyOS was putting it in both. A peer that rejects the unexpected transform simply discards the IKE_SA_INIT, with no error of any kind, so the tunnel never establishes, and nothing on the VyOS side explains why. This was reproduced against a Cisco FTD, but nothing about it is specific to that platform. Worth knowing if you run Rolling: the defect did not require you to configure ESN, because it was the default disabled setting that triggered it.

Separately, the connection uniqueness policy is now configurable on the site-to-site peer level:

set vpn ipsec site-to-site peer <peer> unique <never|keep|replace>

On a peer that flaps, duplicate SAs otherwise accumulate on every DPD timeout. Previously, there was only a global option that changed the policy for every connection at once.

More details: https://vyos.dev/T9254, https://vyos.dev/T8952 

Routing, DHCP and interfaces

A default route learned over DHCP could go missing from FRR. The route is only known once a lease arrives, and the one-shot injection that carried it into static was in a race condition with the FRR reload of the commit that started the DHCP client. When the lease arrived mid-reload, the route went into the kernel, and FRR never learned about it, and nothing recovered afterward.  Every lease event now triggers reconciliation, for the default VRF and for every named VRF. The same fix covers routes configured below protocols static table <id> route <prefix> dhcp-interface, which were never re-rendered on a lease event at all.

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

Deleting an interface could take more with it than you asked for. Removing a Q-in-Q interface left a stranded sub-interface behind, and the more consequential half of a DHCP client running on a sub-interface of something else was killed without any indication. You would meet the issue during deleting a bond or bridge that carries vif, vif-s, or vif-c sub-interfaces, or deleting an entire vif-s.

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

Adding a second member to a bond released the first one, leaving the bond with the new member only. A companion fix stops the bond's MAC address from moving to whichever member was appended last. The address is adopted from the first member and stays there.

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

Two smaller interface changes. Deleting static ARP entries now works - delete protocols static arp committed without error and left the entries in the kernel, still marked PERMANENT. And receive checksum offload became configurable with set interfaces ethernet eth0 offload rx, the point being to turn it off, since some NICs are reported to raise false checksum failures that confuse monitoring.

More details: https://vyos.dev/T9268, https://vyos.dev/T9158 

Firewall and connection tracking

Firewall rules can now match on the result of a routing table lookup:

set firewall ipv4 prerouting raw rule 10 fib lookup destination-address
set firewall ipv4 prerouting raw rule 10 fib match route-type local

The use case that produced it is the general shape of the problem. Decisions in the raw table are evaluated at PREROUTING, before the kernel decides whether a packet is destined for the router or will be forwarded through it. So a notrack rule scoped by inbound interface - the usual way to keep a transit router's conntrack table small - also untracks traffic to the router's own control plane. The workaround was to enumerate every address configured on the router in a network group, which has to be maintained by hand. Matching route-type local says the same thing without the list.

Rules can also match a MAC address under a mask, on IPv4, IPv6, and bridge rule sets, so one rule can cover a vendor OUI or an arbitrary bit pattern:

set firewall ipv4 forward filter rule 10 destination mac-address 01:00:00:00:00:00
set firewall ipv4 forward filter rule 10 destination mac-address-mask 01:00:00:00:00:00

On the connection tracking side, VRF conntrack zoning worked for IPv4 and dropped packets for IPv6, which showed up as insert_failed and drop in conntrack -S. The kernel resolves the underlying collision by itself, but only once a NAT chain exists for the address family. IPv4 always had one, and so had been working by accident. The chain is now created deliberately, covering both families, and only while conntrack zoning is actually in use.

More details: https://vyos.dev/T9157, https://vyos.dev/T9242, https://vyos.dev/T6097 

Kernel crash dumps

When a router panics, the useful information is in memory, and a restart cleans it out, so you cannot recover details about the panic reasons. kdump solves this by booting a second, minimal kernel out of a reserved memory region immediately after the panic, with the crashed kernel's memory still intact, and writing it to disk before rebooting normally. Until now, VyOS had the kernel support for the mechanism, but nothing that used it.

kdump is now configurable:

set system option kdump
set system option kdump memory <auto | 128-1048576>
set system option kdump dump-path <path>

Creating the node is enough - memory reservation defaults to auto, and the dump path to /var/crash. Two operational commands come with it: show system kdump and show system kdump dumps. Both show tech-support report and generate tech-support archive pick up the kdump status and the most recent dump.

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

Containers

The container implementation moved from hand-written systemd units to systemd quadlets, which is how podman expects to be integrated with systemd these days. systemd's own generator now handles container and network lifecycle, and you can configure how long podman waits before killing a container that will not stop:

set container name <name> stop-timeout <1-60>

Containers can also be given the sys-rawio capability and can share the host's cgroup namespace with allow-host-cgroups, and podman is now built with systemd support, so container health checks are scheduled automatically.

Separately, and for a different audience, VyOS can be built as an OCI container image. The image is aimed squarely at network emulation, containerlab in particular. Treat it as a lab tool, and a best-effort one: it is not for production use. Running as a container also exposed a set of assumptions about being a real machine - show version reporting the host's hardware, GRUB updates on a system with no GRUB - which are now handled.

More details: https://vyos.dev/T9129, https://vyos.dev/T9269, https://vyos.dev/T9265 

Platform and build

VyOS has carried a fork of Debian's live-boot package since 2015 to mount its own filesystems at boot. That fork is retired. Debian's own live-boot is used now, with a drop-in component that provides the pieces VyOS needs on top of it.

The ISO shrank from 654 MB to 617 MB. Kernel and libc headers are gone - there is neither a compiler nor DKMS on a running VyOS system, so nothing could have used them - and Telegraf is now compiled with only the plugins that can be instantiated from the VyOS CLI.

The VyOS shell had been pinned on bash 4.1 for many years. It is now rebased on Debian's bash 5.2.37 and built through the normal package build system. One visible side effect of the newer readline: bracketed paste mode is enabled by default, so pasting multi-line text into the CLI behaves the way it does in a modern shell.

ARM64 work continued quietly in the background - architecture-aware firmware packaging, a runtime-resolved LCD driver path, and test coverage that understands which platform it is running on. None of this is the ARM64 release, but it is what the release needs underneath it.

The kernel moved to 6.18.50 for Rolling, and the out-of-tree Intel ixgbe and ice drivers were updated to 6.4.5 and 2.6.7.

More details: https://vyos.dev/T5475, https://vyos.dev/T9277, https://vyos.dev/T7575, https://vyos.dev/T9014, https://vyos.dev/T9298, https://vyos.dev/T9287, https://vyos.dev/T9272

Security maintenance

Most of the security work landing in Rolling this period was already announced with the VyOS 1.5.1 and 1.4.5 releases: the remote code execution vulnerability in the system update-check mechanism, and the twelve accel-ppp vulnerabilities. Rolling carries the same fixes, so update to a current build. The full descriptions are in that announcement: https://blog.vyos.io/vyos-1.5.1-lts-released-security-fixes-bgp-enhancements-and-automated-upgrades

Two items are new here. strongSwan 6.1.0 was released with a set of security fixes, and those have been backported onto the strongSwan 6.0.7 that VyOS builds. And a further accel-ppp update carries roughly thirty-five ports of upstream memory-safety and malformed-packet fixes on top of the twelve vulnerabilities above, spread across PPTP, PPPoE discovery, RADIUS, IPoE, and SSTP. If you terminate subscriber sessions, this is the more important of the two.

More details: https://vyos.dev/T8497, https://vyos.dev/T9286, https://vyos.dev/T9294, https://vyos.dev/T9321

Smaller conveniences

  • Certificates and CRLs issued by a CA using an Ed25519 or Ed448 key can now be verified. This is more urgent than it sounds: Let's Encrypt has begun signing its CA certificates with those algorithms, and VyOS could not import them at all, which blocked using an LE-issued certificate on the HAProxy load balancer.
  • Kea DHCP failover and conntrack-sync can use IPv6 for their peer links, which makes high availability workable in IPv6-only and IPv6-preferred networks.
  • FRR routing metrics can now be pushed out through Telegraf with set service monitoring telegraf frr-metrics, instead of requiring a Prometheus server to scrape the router.
  • The PowerDNS dont-query list is exposed as set service dns forwarding recursion-exclude-address, for anyone running authoritative servers inside private or reserved ranges.
  • The REST API gained a /ping endpoint, and its traceroute endpoint accepts a VRF.
  • The VyOS Python library now loads considerably faster, which shows up as a small but measurable CLI improvement on low-powered hardware.

More details: https://vyos.dev/T9225, https://vyos.dev/T9166, https://vyos.dev/T9260, https://vyos.dev/T8045, https://vyos.dev/T9224, https://vyos.dev/T9223, https://vyos.dev/T9261

Community activities

Around a third of this period's work came from outside the core team, including the OpenVPN 2.7 and DCO work described at the top of this post. A few of the others deserve to be told properly.

  • https://vyos.dev/T9254, https://github.com/vyos/vyos-1x/pull/5429

Some bugs are found by reading code, and some are found by losing a tunnel. The ESN fix above was chased for two days on a live HA pair carrying production traffic, by keeping one node on a known-good June build and walking the other forward through three later images. The reporter proved the two nodes generated different IKE proposals from an identical configuration, wrote the fix, and validated it by failing VRRP over to the patched node.

  • https://vyos.dev/T9157, https://github.com/vyos/vyos-1x/pull/5372

A feature request is more convincing when it comes with the outage that prompted it. The FIB match above arrived from an operator running four DFZ edge routers with full tables, who took down BGP for several minutes, discovering the problem it solves. The discussion that followed turned up a separate inconsistency in how nftables evaluates a combined address-and-interface FIB lookup, which neither participant had been looking for.

  • https://vyos.dev/T9162, https://github.com/vyos/vyos-1x/pull/5375

Displaying something is supposed to be a safe operation. show nat source rules raised a KeyError on any rule that had a protocol match but no inbound interface, because the code read the first expression in the rule as if it were an interface name. The command now searches for the interface match specifically, and falls back to any when there is not one.

  • https://vyos.dev/T9256

Buffer handling is invisible until it silently stops your failover from working. keepalived reports VRRP transitions to VyOS through messages via a FIFO pipe, read in fixed-size chunks with nothing held between reads. On a pair with enough instances in one sync group, a read lands mid-line, and the dispatcher sees two fragments instead of one message, so no transition script runs, and nothing reports that anything was missed. Incomplete trailing lines are now held over and prepended to the next read.

Several more community contributions are described in the sections above: IPv6 support for DHCP failover and conntrack-sync peer links, the PowerDNS exclusion list, MAC address masks in firewall rules, the ability to turn receive checksum offload off, Ed25519 and Ed448 CA verification, the REST API ping and traceroute additions, container cgroup namespace sharing, the sys-rawio capability, and podman health check support.

Thank you to everyone who contributed this period, the code, the reports, and the hours of production debugging behind them.