When the launcher refuses to start OpenVPN because a PID file already exists, the natural reading is that a tunnel is up somewhere. That reading is wrong often enough to be worth checking before anything is deleted. A file in a runtime directory is bookkeeping, and bookkeeping survives crashes, reboots and interrupted shutdowns that left no process behind. Two questions decide the case: is there a live process, and does that process belong to this instance.

Start with the service, not the file

The refusal usually comes from a launcher rather than from OpenVPN itself. It reads a path, finds something there, and stops before parsing a configuration. The first question is therefore about the launcher's contract, not about the tunnel.

Read the unit or script that printed the message and note three things: the path it checks, the service type it declares, and whether it is responsible for writing the file at all. A modern unit may use simple or notify supervision and never touch a PID file, while an older init script may create one itself. Copying a check from one model into the other produces exactly this kind of refusal. Find out which model is in use before interpreting anything.

Only then does the question become whether an instance is actually running.

Check whether that identifier belongs to your instance

A number in a file proves that a number was written. It does not prove that a process with that identifier exists now, and it certainly does not prove the process is yours, because identifiers are reused after a reboot.

Compare the process start time against the file's timestamp, the executable against the unit's command, and the service membership against the unit name. A mismatch in any of the three means you are looking at bookkeeping rather than at a tunnel.

On a host running containers or several namespaces, an identifier may be meaningful in one view and absent in another. Failing to see a process is also not evidence that it does not exist, if your account cannot inspect it. Check from the context the launcher uses before drawing conclusions.

systemctl status [email protected]
ps -o pid,lstart,args -p "$(cat /run/openvpn/vpn.pid)"

What the launcher promised to do with the file

PIDFile= tells the service manager where to read the main process identifier. The manager does not write that file, though it does remove a surviving one after shutdown, which is exactly why an interrupted exit can leave a file behind.

That division of responsibility explains two symptoms people report as bugs. A file that never appears means the daemon or its script failed to write it, not that supervision is misconfigured. A file that survives a stop means the shutdown path never completed.

It also explains why current guidance suggests avoiding the convention when supervision can track the main process directly. That guidance is not permission to change a packaged unit's type, because the daemon's foreground, forking or notification behaviour has to match what the unit declares. Change the type without changing the daemon and you get a unit that reports failure while the tunnel runs, or the reverse. Match the pair, or leave both alone. The systemd service reference describes the ownership split.

Clean shutdown versus interrupted exit

A clean stop asks the daemon to exit, waits for it, and lets the manager tidy up its own bookkeeping. An interrupted exit, a killed process or a reboot mid-shutdown skips parts of that sequence, and the file is what remains. Finding one after a crash tells you the shutdown did not finish, not that something is listening.

Compare the file's timestamp with the last boot and with the unit's last stop.

Then check the live evidence directly. A socket bound to the VPN port and a tunnel interface with an address are proof that something is running, in a way that a text file in a runtime directory never is. If both are absent, nothing is running, whatever the file claims.

Stop deleting the file as a habit

Deleting it is a legitimate recovery step once you have established that no instance owns it and that the launcher documents this behaviour.

As a permanent pre-start habit, it defeats the check you are working around. If stale files return after every clean stop, the writer and the shutdown lifecycle disagree about who owns the path, and that disagreement is the bug worth fixing.

Keep an independent way into the host, start one instance through its normal manager, and read the first result. The next person gets a unit that either works or explains itself.

Mikanda mikatakata