OpenVPN Keeps Pushing Old Routes? Check What It Loaded
Either the server never read your edit or the client never installed the push. Compare the running process, the pushed list and the return route.
You add a route push, restart the service, reconnect, and the client's table looks exactly as it did before. Two different things could be true: the server never loaded your edit, or the client never installed what it received. Those have nothing in common except the symptom, so the first move is to find out which one you have.
The comparison below runs from the process outward, because the process is the only authority on which file is in force.
The file you edited and the file the process reads
Start with the unit and the command line it produced. On a systemd host, systemctl cat openvpn@server prints the ExecStart line, and an instance unit typically derives its configuration path from the instance name, so a daemon started as openvpn@office reads a different file from one started as openvpn@server. Compare that with the argv of the running process. A configuration management tool adds another way to lose the change: it can write a correct file to a location the unit never mentions, which makes a green deploy look like a no-op.
Timestamps settle it quickly: if the file is newer than the process start time, the process cannot have read it. Router firmware adds a third case, because many models keep their settings in flash and rebuild the configuration during boot, which makes a file edit on disk the wrong layer entirely.
What the server pushes and what the client installs
A pushed route has to survive two filters. On the server it must sit in the loaded configuration rather than in a commented block or a client-specific file whose name does not match. On the client, pull must be enabled and no pull-filter may be discarding the option. Connect at verbosity 4 and read the pushed options the client reports: if the route appears in that list, the server did its job and the client is choosing not to install it.
Client-specific files are a frequent source of confusion. A file named after the certificate common name pushes options to that client alone, and a one-character mismatch stops the push silently, with no error on either side. The server logs the common name it saw at connection time, so compare that value with the filename rather than with what you believe the client is called.
The pushed prefix and the return route are different problems
A client that deliberately ignores redirect-gateway to keep its local internet path creates a second, unrelated gap. The server's probes reach the tunnel address and the answers never arrive, because a reply addressed to the server's tunnel IP is routed by the client's ordinary default gateway. The pushed subnet route is not at fault here; what is absent is a route that sends traffic for the server's tunnel network back down the tunnel device.
Adding that route on the client fixes answers to server-originated probes. It is not a substitute for the pushed subnet route, and the reverse is true as well, because the two describe opposite directions of travel.
Compare the tables on both ends
After any change, reconnect and read the client's table for the pushed prefix and for the tunnel device. Then test in both directions: client to a host on the pushed subnet, and that host back to the client. A route that exists but carries no traffic means one of three things: the far side is not forwarding, a firewall is dropping the flow, or the answer has no way back.
Keep three identifiers together: configuration path, unit or instance name, and the client identity you tested with.
Those three turn the next occurrence into a five-minute check rather than a repeat investigation, and they are what a colleague needs when the tunnel becomes someone else's problem.
A route problem that survives a restart is often not a routing problem at all; the fault sits upstream of the table you keep reading.