keep-alive interval 0 is how the CLI turns the OpenVPN keepalive off. Server mode rendered it as keepalive 0 0 (T9334); site-to-site mode renders the two directives that helper stands for, and neither is guarded:
data/templates/openvpn/server.conf.j2
ping {{ keep_alive.interval }}
ping-restart {{ keep_alive.failure_count }}So interval 0 with the default failure-count produces ping 0 / ping-restart 60. OpenVPN accepts both, since --ping and --ping-restart are parsed with a lower bound of 0 unlike --keepalive, which is why nothing fails visibly:
vyos@vyos:~$ grep -n "^ping" /run/openvpn/vtun20.conf 22:ping 0 23:ping-restart 60 vyos@vyos:~$ systemctl is-active openvpn@vtun20.service active
A zero send timeout leaves the ping timer unarmed while the restart timer stays armed, so the configuration that asked for no keepalive gets "send no keepalives, and tear the tunnel down after 60 seconds of silence". Two site-to-site tunnels pointed at each other, both with keep-alive interval 0, left idle:
Sep 18 10:31:52 vyos openvpn-vtun20[19257]: Timers: ping-restart 60 Sep 18 10:33:02 vyos openvpn-vtun20[19257]: Inactivity timeout (--ping-restart), restarting Sep 18 10:33:02 vyos openvpn-vtun20[19257]: SIGUSR1[soft,ping-restart] received, process restarting Sep 18 10:33:02 vyos openvpn-vtun20[19257]: Restart pause, 2 second(s) Sep 18 10:33:05 vyos openvpn-vtun20[19257]: Initialization Sequence Completed Sep 18 10:33:05 vyos openvpn-vtun20[19257]: Timers: ping-restart 60 Sep 18 10:34:14 vyos openvpn-vtun20[19257]: Inactivity timeout (--ping-restart), restarting
Timers: ping-restart 60 is the whole timer set - no ping timer is armed at all. Both peers restart in lockstep about once a minute for as long as the tunnel carries no traffic. The same cycle shows on vtun21.
This is what the option parsing produces:
init.c, OpenVPN 2.7
if (c->options.ping_send_timeout)
event_timeout_init(&c->c2.ping_send_interval, c->options.ping_send_timeout, 0);
if (c->options.ping_rec_timeout)
event_timeout_init(&c->c2.ping_rec_interval, c->options.ping_rec_timeout, now);verify() does not catch it either: the keep-alive validation added in T8264 sits under if openvpn['mode'] == 'server':, so site-to-site is never checked.
Steps to reproduce
Two tunnels pointed at each other on one router are enough:
set interfaces openvpn vtun20 mode site-to-site set interfaces openvpn vtun20 local-host 127.0.0.1 set interfaces openvpn vtun20 local-port 1194 set interfaces openvpn vtun20 remote-host 127.0.0.1 set interfaces openvpn vtun20 remote-port 1195 set interfaces openvpn vtun20 local-address 10.9.0.1 set interfaces openvpn vtun20 remote-address 10.9.0.2 set interfaces openvpn vtun20 shared-secret-key <name> set interfaces openvpn vtun20 encryption cipher aes256 set interfaces openvpn vtun20 keep-alive interval 0 set interfaces openvpn vtun21 mode site-to-site set interfaces openvpn vtun21 local-host 127.0.0.1 set interfaces openvpn vtun21 local-port 1195 set interfaces openvpn vtun21 remote-host 127.0.0.1 set interfaces openvpn vtun21 remote-port 1194 set interfaces openvpn vtun21 local-address 10.9.0.2 set interfaces openvpn vtun21 remote-address 10.9.0.1 set interfaces openvpn vtun21 shared-secret-key <name> set interfaces openvpn vtun21 encryption cipher aes256 set interfaces openvpn vtun21 keep-alive interval 0 commit
cat /run/openvpn/vtun20.conf shows ping 0 and ping-restart 60. Leave both tunnels idle and watch journalctl -fu openvpn@vtun20: the tunnel comes up, then restarts roughly every 70 seconds without any traffic at all.
Expected behaviour
interval 0 leaves both directives out of the generated file, the same way T9334 makes server mode leave keepalive out.
Version
VyOS rolling 2026.09.17-0028. The template has rendered it this way for years, so every release with site-to-site support is affected.