The command returns, the service shows failed, and nothing resembling an explanation reaches the terminal. That silence is the symptom people describe as "OpenVPN will not start", and it almost never means the program died without saying anything. It means the message went to a channel you were not reading, or that the thing you started was not the thing you configured. Both cases are diagnosable in under a minute once you stop treating the profile as the suspect.

Four places absorb the output: the unit's journal, the plugin that owns the connection, the other end of the tunnel, and a different binary than the one you think is running. Each has its own check.

Silence means you were not watching the right channel

A foreground invocation prints to your terminal. A unit prints to the journal, and a failed unit can exit before it writes anything at all if the failure is in how it was started rather than in what it was told. The distinction matters: no journal entry means the process never really ran, while one entry followed by an exit means it ran and rejected something.

systemctl status openvpn-client@office
journalctl -u openvpn-client@office -b --no-pager

Add systemctl show -p Result -p ExecMainStatus openvpn-client@office to separate a clean exit from a signal. A status of 1 with a Result=exit-code is a program decision; Result=signal or a timeout points at the environment instead, and no amount of profile editing will change those. Record both values before restarting anything, because a restart clears the transient state that produced them.

The instance name is part of the path

The packaged template unit does not take a file path. It takes an instance name, and that name is appended to a directory to find the profile: office resolves to /etc/openvpn/client/office.conf on the Debian and Ubuntu packaging. A file called office-v2.conf started as @office is never read, and the unit exits quietly because from its point of view there was nothing to do. The lookup directory differs between client and server mode, so the same mistake points somewhere else entirely when you switch templates.

That mismatch is the most common source of the silent failure, and it is invisible in the profile because the profile is fine. List what actually exists and compare the two names character by character, including any extension the packaging expects.

ls -l /etc/openvpn/client/
systemctl list-units 'openvpn*' --all

If the unit is enabled but no instance is listed, the enablement itself may reference a name that was later renamed. Re-enabling the correct instance is usually enough, and the old unit file left behind is worth deleting so it does not confuse the next person. A leftover unit file is a small thing that costs somebody an hour later.

When the plugin owns the tunnel, the terminal sees nothing

A connection started from the desktop applet is owned by NetworkManager, and its OpenVPN output goes into that service's journal rather than into any shell you have open. Running the client by hand and seeing nothing is expected in that arrangement, because the hand-run process is not the one carrying the connection.

Search the journal for the service that owns it, then reproduce the parse with verbosity you can read. The two commands below look at different owners of the same failure.

journalctl -b -u NetworkManager --no-pager | grep -i -A3 openvpn
openvpn --config /etc/openvpn/client/office.conf --verb 4 --log /tmp/ovpn.log

The standalone run is for reading messages, not for replacing the managed connection, and the two differ afterwards: DNS integration, routes and lifetime belong to the plugin. Fixing a parse error in the standalone run does not automatically fix name resolution once the applet takes over, so keep the two observations separate.

Two binaries, one PATH

A manual build installed under /usr/local wins for a shell invocation, because /usr/local/sbin precedes /usr/sbin on the default Ubuntu PATH. A unit does not use your PATH; it runs the absolute path written into ExecStart. So the version that works in your terminal and the version that fails as a service can be genuinely different programs.

Compare the two before rebuilding anything. The third command is the one that decides the argument.

command -v openvpn
whereis openvpn
systemctl cat openvpn-client@office | grep ExecStart

One more environment difference hides in the same place. A packaged unit relies on a runtime directory under /run that systemd creates for it; a hand-written replacement unit that omits that setting can fail before it opens a single file, and the failure looks like a generic refusal with no OpenVPN message attached.

Failures the client cannot report

Some outcomes are decided at the far end. A gateway that answers with an HTTP status and closes the connection has told you about its own state, and the useful next step is its log or the account behind the credential rather than your local files. Nothing local is broken in that scenario, and editing the profile to compensate makes the next diagnosis harder. Read that log before you touch the client configuration.

Another silent case is a client that is working correctly. A client-mode tunnel never listens on a port, so a check that expects an open listener will report nothing and look like a failure. Confirm which mode you started before drawing a conclusion from an empty port list.

Work the layers in order: does the unit exist and is the instance name right, did anything reach the journal or the --log file, which binary and profile actually ran, and is the failure local or remote. Each answer removes a layer. Starting from the profile means rewriting a file that may never have been opened.

འབྲེལ་བའི་རྩོམ་ཡིག