You check the tunnel, run systemctl status openvpn, and get active (exited) with no Main PID and an empty process list. The natural reading is that the service started and died, so you open the unit, find /bin/true, and conclude the package is broken. On the Debian packaging that state is the wrapper working as designed, and the tunnel lives in one of the instance units it starts. The aggregate unit will never tell you which instance built it.

What active (exited) actually means on this unit

The packaged unit is a oneshot: its ExecStart is /bin/true, and RemainAfterExit=yes is what keeps a finished unit reported as active (exited). Take that directive away and the same successful /bin/true oneshot ends in inactive (dead), which is exactly the state people expect and misread in the other direction. The command is a placeholder, and the unit is reporting on work that other units perform. Nothing in that arrangement is broken, and replacing /bin/true changes the packaging contract rather than the tunnel.

The wrapper delegates. It starts the per-instance units that the package configuration or its systemd generator supplies, which is why an empty process list proves nothing about the tunnel. When no instance is selected, the wrapper still succeeds and still starts nothing. The state answers whether the wrapper ran, not whether a tunnel exists.

Read the three lines of the status output rather than the word in parentheses. Loaded names the unit file systemd read, Active carries the timestamp of the last successful run, and the absent Main PID fits a oneshot that has finished. Those three lines are also how you tell this wrapper apart from a service that crashed a second after starting.

List the units before you read the status again

Ask systemd what exists before asking what is running. A template can be installed while no instance is instantiated, and only an instantiated unit carries a tunnel. Start with the loaded set:

systemctl list-units 'openvpn*'

That list is what systemd has loaded at this moment, which is the closest thing to a tunnel inventory. Installed unit files answer a different question, and that list still shows a template when nothing is instantiated:

systemctl list-unit-files 'openvpn*'

Read the two lists together, because a template in the second and an instance in the first are different answers. A template that exists and an instance that runs are not the same fact, and only the second one means a tunnel. Package layouts differ across distributions and OpenVPN versions as well. An older openvpn@ template, a split client and server pair, and a distribution wrapper can all be legitimate on their own packaging.

Match your machine rather than the tutorial you found. The instance name in somebody else's output may be office while yours is home or work. What transfers is the method, not the string.

Follow the instance name to the file it reads

On the upstream client template the unit's working directory is /etc/openvpn/client and its command takes %i.conf from there, so openvpn-client@office reads /etc/openvpn/client/office.conf. Confirm that mapping on the installed unit instead of trusting the example, because the template you have may be older or may be a distribution rewrite:

systemctl cat [email protected]

Read the drop-ins that appear at the bottom of that output, since a file under .d/ can change ExecStart, the working directory, or the config path. A profile you downloaded as office.ovpn is not the file the instance opens. That gap is the usual reason an error names a path you never edited.

Relative paths inside the profile are the next trap. Moving the main file while leaving ca, cert and key references relative trades a missing-file error for a different missing-file error. The second one points at a file you were sure existed, which is why the profile directory matters more than the file name.

Start it, then read that instance's journal

Start the instance you just inspected, because the aggregate unit's log will not contain the tunnel's errors. Then read the journal for this boot and look for a configuration error, a file-access error and a connection error in that order, since each one fails at a different stage of startup.

sudo systemctl start [email protected]
journalctl -u [email protected] -b

Starting and enabling are separate decisions: start runs the unit now, enable arranges the next boot, and enable --now does both in one step. daemon-reload rereads unit definitions and does not restart a running process or apply an edited profile. After you change the profile, restart the instance, because that is what rereads the file.

Don't put a second manager on the same tunnel

NetworkManager and the OpenVPN 3 session managers own their own connection state, and a systemd instance beside one of them puts two owners on the same tunnel. The symptom is a tunnel that comes up and is replaced seconds later, or one that refuses to stay stopped. Choose the manager your distribution documents for the desktop, and use systemd deliberately on a server.

Check for an existing owner before starting an instance. When a desktop applet shows the connection, the instance you start competes with it rather than completing it. Disable the manager you are not using instead of running both and hoping the order holds. That conflict also explains journals where a tunnel is established and then torn down by a process you never started.

Answer the original question with the instance name, the file it read, and the journal line that failed. OpenVPN's systemd integration notes describe the template layout and the file mapping the instance name resolves through.

Ebitabo ebifaananyi