Andrew Mercer
on this page

OpenVPN Client Systemd Service

Part of the OpenVPN Comprehensive Guide. Covers autostarting an OpenVPN client connection on boot and getting its routes to reliably come up — the original notes had a working service unit but flagged routes not coming up on reboot as an unsolved TODO.

Use the packaged template unit instead of a hand-rolled one

The original setup used two custom units (edge-cloud-vpn.service for the tunnel, edge-cloud-routes.service chained after it via ExecStart/ExecStop scripts) to add routes as a separate step from bringing up the tunnel. That split is very likely the actual cause of the boot-time race: two independently-scheduled services means two chances for systemd to start them in the wrong order relative to networking being fully up, especially across a reboot when interface bring-up timing varies.

Current OpenVPN packages ship a template unit, [email protected], that starts the client for any config dropped in /etc/openvpn/client/<name>.conf:

sudo cp amercer-pc1.ovpn /etc/openvpn/client/amercer-pc1.conf
sudo systemctl enable --now [email protected]

That alone typically fixes the "routes don't get added on reboot" problem that the original notes hit: routes pushed by the server, or added via route/route-up in the client config, come up as part of the same service's startup as the tunnel itself, rather than depending on a second service racing against network readiness. There's no separate route service to keep in sync.

If you still need custom routing logic

Some setups genuinely need something beyond what the server pushes — a route to a subnet only this particular client should reach, for instance. Rather than a second systemd unit, hook it into the OpenVPN client lifecycle directly with --route-up, which runs after the tunnel and its pushed routes are already up, guaranteeing ordering without relying on systemd unit ordering at all:

# in amercer-pc1.ovpn / /etc/openvpn/client/amercer-pc1.conf
script-security 2
route-up /etc/openvpn/client/amercer-pc1-route-up.sh
#!/bin/sh
# /etc/openvpn/client/amercer-pc1-route-up.sh
ip route add 192.168.20.0/24 dev "$dev"
sudo chmod +x /etc/openvpn/client/amercer-pc1-route-up.sh

This keeps route management inside OpenVPN's own event model instead of a second unit, so it can't run before the tunnel exists and can't be skipped by a systemd ordering hiccup on boot.

Make sure the network really is up first

If routes (or the tunnel itself) still fail to come up on reboot after switching to the template unit above, the remaining usual cause is the service starting before the underlying network interface has an address — network.target (used in the original custom units) only guarantees network services have started, not that any interface is actually configured yet. Depend on network-online.target instead, which does wait for that:

# /etc/systemd/system/[email protected]/override.conf
[Unit]
After=network-online.target
Wants=network-online.target
sudo systemctl daemon-reload
sudo systemctl restart [email protected]

(network-online.target needs a provider service actually enabled to mean anything — systemd-networkd-wait-online.service or NetworkManager-wait-online.service, whichever manages the interface. If neither is enabled, network-online.target is reached immediately and this doesn't help — check with systemctl status NetworkManager-wait-online.service / the networkd equivalent.)

Verifying on next boot

sudo reboot
# after it comes back up:
systemctl status [email protected]
ip route show

If the tunnel interface and expected routes are both present without manually restarting anything, the ordering fix held.