A script that starts OpenVPN and never connects is usually not a network problem: the client is sitting at a prompt with no terminal to draw it on. OpenVPN has exactly two supported ways to answer that prompt without a human, a credential file behind auth-user-pass and the management socket, and they are not interchangeable. The file costs one directive and answers one prompt; the socket answers every prompt and hands anything that can open a loopback port an unauthenticated control channel. Which one belongs in your deployment depends on which prompt is actually blocking, and on which secret you can afford to leave at rest.

Name the prompt before you choose a mechanism

Four prompts look identical on a terminal. The account pair comes from the server's PAM or auth-user-pass-verify setup and is answered by auth-user-pass. A private key passphrase belongs to the key file and is read through --askpass or typed by hand. A PKCS#12 bundle asks for a password of its own. Proxy authentication uses the auth file that --http-proxy takes, not the account credentials.

When OpenVPN runs as a child of systemd, PHP or Node there is no terminal, so any of these blocks silently. The process sits there, the log stops after the cipher line, and the unit eventually times out; that silence is why people add a retry loop or a sleep instead of looking at which prompt is pending. Only the management socket can answer all four. The credential file answers the first one and nothing else.

auth-user-pass: one file, one prompt

The file holds the username on line one and the password on line two, with nothing else: no quotes, no key names, no comment. Set mode 600 and make the owner the account that runs openvpn, which for a service is often a dedicated user rather than your login. A root-owned file under /root is the difference between a command that works in your shell and a unit that fails at boot.

Two details turn a correct file into a prompt. An editor that saves CRLF leaves a carriage return glued to the password, so the server rejects a password that looks correct in the file; check with cat -A or od -c rather than by eye. And an argument-less auth-user-pass inside the profile means "ask the user" by design, which a second directive on the command line may not override, because the profile is parsed where it sits.

Option order is the third trap, and it is version-specific. Some OpenVPN 2.4 builds ignore --auth-user-pass when it appears before --config, so the same two options in the other order connect. Put the directive inside the profile, or after --config on the command line, and grep the profile for every occurrence before changing anything else.

auth-nocache belongs to this mechanism but supplies nothing. It limits how long the client keeps the credential in memory, which helps after a crash and not at all when the file is missing. A reconnect after a server-side session timeout re-reads the file, so a credential that worked at boot can still expire in the middle of the day.

The management socket: every prompt, more surface

Start the client with a listener and state explicitly that prompts will be reported on it, using the directives documented in the OpenVPN 2.6 manual page:

management 127.0.0.1 3030
management-query-passwords

From bash, hold the connection on a spare file descriptor and answer by prompt name instead of by position, because a reconnect can ask in a different order:

exec 3<>/dev/tcp/127.0.0.1/3030
while read -r -u 3 line; do
  case "$line" in
    *"Need 'Auth' username/password"*)
      echo 'username "Auth" alice' >&3
      echo 'password "Auth" secret' >&3 ;;
    *"Need 'Private Key' password"*)
      echo 'password "Private Key" secret' >&3 ;;
  esac
done

The boundary is the transport. The management protocol is plaintext over TCP on loopback, so the listener must not be published on a LAN or tunnel address to let a remote controller reach it; a remote controller means an SSH forward, not a wider bind. The community daemon also accepts one management client at a time, so a GUI or a monitor cannot share the socket with your script. The second connection is refused, not queued, and the failure looks like a script that went deaf.

That socket is also a control channel. Without a management password, anything that can connect to 127.0.0.1:3030 can read status, kill the tunnel or answer a prompt itself. The management password and the credential file are the same class of secret, and both deserve the same care.

Which one fits the deployment

Choose the credential file when the only prompt is the account pair, the client runs as a service with a stable owner, and you can keep a 600 file out of backups you do not control. Choose the management socket when a key passphrase or a PKCS#12 password is in play, when the wrapper already supervises the process, or when you need to watch state and prompts while debugging a reconnect.

Two conditions move the decision. If the wrapper is /bin/sh, a systemd unit that calls the binary directly, or a minimal container, the bash-only socket glue is not available and the file is the cheaper answer. If the secret rotates often, neither file is a good home for it: query a keyring from a small wrapper and let the unit call the wrapper, which keeps rotation in one place and the password out of shell history.

Process substitution sits between the two and belongs to neither. The one-liner below works because bash expands it before openvpn starts, not because the client understands the syntax:

openvpn --config client.ovpn --auth-user-pass <(printf "%s
%s
" "$USER" "$PASS")

It breaks under /bin/sh, in a unit that bypasses the shell, and in images without bash or /dev/fd. Keep it for interactive testing, never as the contract a service depends on.

Protect the secret, then verify from the log

Both mechanisms leave a secret readable by the OpenVPN process. Never put the password on the command line or in the unit file, where ps and systemctl show it to every local user; pass the path to the file, not the value inside it. Where certificates can replace the account pair, that removes the category of prompt instead of hiding it.

A correct run shows the prompt sequence completing and the session reaching "Initialization Sequence Completed". If the client stops at the same prompt, the match failed: print the exact lines the socket sent, check quoting and the prompt name, and fix the pattern rather than adding a sleep. A tunnel that reports success and carries nothing is a routing or push problem, not a credential one.

Then test the reconnect, not just the first start. Drop the server-side session or wait out its timeout; the client asks again, and that is where a file which looked correct at boot shows whether it is still readable by the account that reconnects.

Nuŋlɔɖi siwo ku ɖe eŋu