Swapping the Pi from Wi-Fi to a cable looks like a five-minute job, and it stays that way only if you treat the move as four separate settings instead of one. The endpoint name your clients dial can stay exactly the same while the address underneath it changes. What actually breaks is a listener pinned to the old address, a port forward still aimed at the wireless lease, or a return route that leaves through the interface you thought you retired. This walkthrough keeps a recovery path open at every step, so a failed cutover costs a reboot.

Record the four owners before you unplug anything

Four settings decide whether the cutover survives: the Pi address, the OpenVPN bind address, the router redirect target, and the path replies take. Write all four down before you touch the cable, because once the wireless lease disappears you cannot read the old values off the machine. The wired interface does not inherit the wireless profile, and on Bookworm the address lives in NetworkManager while Bullseye keeps it in dhcpcd.conf. A reservation that pinned the wireless MAC address says nothing about eth0. Treat the new address as a new assignment that needs its own proof.

Old wireless lease:   10.42.7.24/24 on wlan0
New wired address:    10.42.7.31/24 on eth0
Client endpoint:      vpn.lab.example:1194/udp

The listener is the second owner. If the service was started with a specific bind address, the socket dies with the old interface; if it binds all addresses, the socket survives and the router rule becomes the only thing left to fix.

The redirect target and the return path are the owners people skip. A forward that still points at 10.42.7.24 sends arriving handshakes to an address the Pi no longer holds, so clients time out with no server-side log line at all. The return path matters when both links are briefly up, because the Pi answers from whichever interface the routing table prefers and that is not always the one the packet arrived on. The route lookup below shows which source the kernel picks.

Read the actual socket before you trust the new address

Configuration files describe intent and the socket describes reality. Ask the running service what it is bound to instead of trusting the config you remember. The OpenVPN 2.6 manual defines --local as binding to one address and notes that omitting it binds every interface.

sudo systemctl cat openvpn-server@server | grep -n -- '--local'
sudo ss -lunp '( sport = :1194 )'

Read the local address column rather than the state column. A line showing 10.42.7.24:1194 means the socket is pinned to the address you are about to remove, while 0.0.0.0:1194 or *:1194 means the daemon will follow the interface change on its own. If the address is pinned, edit the profile and restart the unit rather than rewriting the live socket by hand, then confirm the service still reaches its certificate and key files.

Walk the move in an order that keeps you reachable

Order matters more than speed here. Bring the wired link up while the wireless one still works, confirm the new address and the return path, then retire the wireless profile. That sequence keeps a working session open until the replacement has been proven.

ip -br addr show eth0
ip route get 203.0.113.10
nmcli -g ipv4.method,ipv4.addresses con show "Wired connection 1"

The first command answers whether eth0 received the address you intended, and the second prints the source address the kernel would use for an outside destination, so run it before and after the change. If that source is still the wireless address, the wired link is not the preferred path yet and the outside test will fail for a reason that has nothing to do with OpenVPN. Fix the routing preference before you touch the forward.

With both links up, the Pi may prefer the wireless default route because it was installed first or because the wired link carries a higher metric. On Bookworm the supported knob is ipv4.route-metric on the connection, and on Bullseye the equivalent is a per-interface metric line in dhcpcd.conf. Demote the wireless link rather than disabling it, so the rollback path stays one command away.

Prove the tunnel from outside, not from the LAN

A VPN connection that works from the sofa proves less than it appears to. Traffic from inside the LAN to the public address passes through hairpin or reflection rules that routers implement inconsistently, so a passing LAN test can coexist with a broken outside path. Use a phone on cellular data, or any host outside the network, for the acceptance test. Keep the LAN attempt as a separate optional diagnostic.

Then read the log in the order the packets arrive. Nothing at all means the handshake never reached the Pi, so the redirect target or the host firewall is wrong. A handshake that starts and stalls points at keys, certificates or MTU. A session that completes and drops usually means the return path or a second default route, and the client log line Initialization Sequence Completed tells you the tunnel itself came up.

sudo journalctl -u openvpn-server@server -n 40 --no-pager
sudo tcpdump -ni any udp port 1194 -c 20

If nothing appears in the server log while the client retries, stop reading OpenVPN config and go back to the redirect target. The capture answers the arrival question in seconds, and inbound packets with no reply narrow the problem to the route.

Keep a one-step rollback and retire the wireless profile

Leave the wireless profile installed until the wired arrangement has passed a reboot. nmcli con show lists the profile names you will need, and a deleted profile has to be rebuilt from memory at the worst moment. Record the interface name, the address method and the metric next to the other four values. A rollback you can type from a note is worth more than one you have to reconstruct.

Retiring the wireless dependency is a property change rather than a deletion. Setting connection.autoconnect no keeps the credentials and the address history intact while stopping the Pi from reattaching to a network you moved away from.

nmcli con show
sudo nmcli con up "Wired connection 1"
sudo nmcli con mod "previously-used-wifi" connection.autoconnect no

Before you call the job done, confirm four things: the redirect target matches the wired address, the listener survived a restart, an outside client completes a handshake, and the Pi still answers on the wired address after a reboot. A reboot is the cheapest way to catch an assignment that existed only in the running kernel. If any of the four fails, bring the wireless profile back up with sudo nmcli con up and compare the recorded values against the live ones.

Ebitabo ebifaananyi