Count the OpenVPN processes before you touch a profile. A Pi that admits roaming users and also dials an upstream service runs two daemons, and each one reads a configuration file chosen by the instance name you typed. The packaged templates then hand each daemon its own working directory, its own runtime status file, and its own tunnel device. Get that split wrong and you will spend an evening changing a correct setting inside a file the running process never opens.

Each Instance Name Picks Its Own File

In the unit files shipped with OpenVPN 2.6, the start arguments carry an instance placeholder that is filled from the text after the @ sign. The server template runs from the server configuration directory and resolves its argument against %i.conf there, while the client template does the same from the client directory. So [email protected] reads admits.conf in the server directory and [email protected] reads uplink.conf in the client directory, and neither name has any meaning in the other directory.

Confirm the mapping on the machine instead of assuming the package kept upstream defaults. A distribution drop-in is allowed to replace the working directory or the config argument outright.

systemctl cat [email protected]
systemctl cat [email protected]

Read the WorkingDirectory line and the config argument in that output, because the enabled unit is the only thing that decides which file loads. A drop-in that appends --config to ExecStart wins over the template even when the template path looks right. Ask for the fragment and drop-in paths when you want to know whether a local override is in play. If both roles resolve to one file, you have a single process trying to be a server and a client, and the later option in that file silently wins.

Local Transport and Forwarded Payload Hit Different Hooks

The server's encrypted connection to each roaming user is generated on the Pi itself, so its outer replies leave through the local output hook. The client's connection to its upstream peer is also locally generated output, even though it reaches a remote network. Forwarded payload is a third category, and the kernel matches it only after it has already chosen an outbound device for the packet.

FlowHook that sees itWhat to inspect
Roaming user handshakes to the serverLocal input and local outputListener socket, bind address, rule admitting the UDP port
User payload leaving through the upstream tunnelForwardIPv4 forwarding switch, selected route, permission rule, any translation
Pi's own connection to the upstream peerLocal outputUnderlay route to the provider endpoint, not the tunnel route

A rule that matches an outbound interface inside a forward chain can permit or drop a packet that routing has already assigned to that interface, but it never chooses the interface. When one host terminates a tunnel and originates another, the policy that sends user payload into the upstream tunnel and the policy that protects the Pi's own encrypted replies are separate decisions. They happen to land on the same physical NIC, which is why they get confused.

That distinction explains a common half-failure. Users connect, the handshake completes, and their traffic still leaves the wrong way, because the handshake was local and the payload was forward and only one of the two was ever tested.

An Imported Route Can Swallow Replies to Your Users

The client profile's security setting decides what the upstream may push into the Pi's routing table, and a broad redirect is the option that turns a working dual-role host into a broken one. If the pushed route covers the public addresses of roaming users, replies the server generates toward them can follow the new route into the upstream tunnel instead of leaving by the underlay. People often confirm that the tunnel to the provider still works and stop there, because that traffic is the tunnel's own transport and it keeps flowing while everything else misbehaves.

Source or mark based policy is how the two streams stay apart. OpenVPN's Linux mark option stamps a daemon's own encrypted packets with a firewall mark, which a routing rule can then send to a dedicated table. Source addresses work just as well when the server's outer replies always leave from one underlay address. Either approach needs valid routes in the table it selects and an exception for the provider's endpoint, or the tunnel itself stops coming up.

Ask the kernel for the path a reply would actually take before writing any rule. The addresses below are placeholders for a Pi underlay address and one roaming user's public address.

ip rule show
ip route get 198.51.100.7 from 192.0.2.10

Verify the Split, Then Stop One Role at a Time

Test three flows separately, because a working handshake proves nothing about the other two. Accept a new inbound connection, send one permitted application through the upstream exit, and confirm that both processes still reach their own peers.

Then stop the client role and watch what the server role keeps. This is the cheapest way to find a route or mark rule that was meant for one instance and applied to both.

systemctl stop [email protected]
systemctl status [email protected] --no-pager
ss -lunp | grep openvpn
journalctl -u [email protected] -n 20 --no-pager

What you want to see is the listener still bound, the tunnel device still present, and the log free of new errors while the client role is down. If stopping the client also cuts the server's replies to existing users, the two roles share something that should have been scoped to one of them. Record which instance, which config file, and which flow owner each change touched, because the next person who debugs this host will otherwise repeat the same discovery from zero.

ସମ୍ପର୍କିତ ପ୍ରବନ୍ଧ