You have received a .ovpn file, you launch OpenVPN on a Debian or Ubuntu VM, and then the terminal displays an error or gets stuck before the connection. The file may be valid while pointing to a missing certificate, a filtered port, or a route that cuts off your SSH session. Check the profile, note the first error, and then verify the route and the internal service after connecting.

Read the .ovpn file before launching OpenVPN
Keep a local console, a provider console, iDRAC/IPMI, or a validated separate management network. Run the test from this independent access. redirect-gateway may be written in the file or pushed by the server after connection. The default route may then pass through the tunnel. A second SSH session that uses the same route is not a backup access.
If you are connected via SSH before the test, display and note the client address. From the control console used after connection, redefine this address before checking its route. A recovery console allows you to return to the server, but it does not guarantee that your SSH session remains open.
# In the SSH session before launching:
printf '%sn' "$SSH_CLIENT"
# In the control console after connection:
SSH_CLIENT_IP='198.51.100.42' # replace with the noted client address
ip route get "$SSH_CLIENT_IP"
Place the file in a directory reserved for your account and limit its permissions if keys are embedded. Do not put any username or password in the demonstration file or in a Git repository.
mkdir -p ~/vpn-test
install -m 600 /chemin/recu/client.ovpn ~/vpn-test/client.ovpn
cd ~/vpn-test
grep -nE '^[[:space:]]*(remote|proto|port|ca|cert|key|pkcs12|tls-auth|tls-crypt|tls-verify|auth-user-pass|auth-user-pass-verify|redirect-gateway|redirect-private|route|route-ipv6|route-gateway|route-table|route-noexec|route-nopull|pull-filter|config|plugin|script-security|up|down|route-up|route-pre-down|ipchange|dns-updown|management|daemon|log|log-append|syslog|status|writepid|askpass|cd|chroot|user|group|learn-address|client-connect|client-disconnect)' client.ovpn
This reading shows the remote host, the protocol, certificate files, and the authentication method without displaying their contents. If config loads another file, re-read it from the profile directory, redo this filter, and follow each config down to the last file. The entire chain must be understood before launching. up, down, route-up, route-pre-down, ipchange, and tls-verify run external commands. plugin loads a library into the OpenVPN process. management opens a control interface that should not be exposed. A client profile that contains learn-address, client-connect, client-disconnect, or auth-user-pass-verify is rejected for this diagnostic. Stop also if any other paths are unknown. External scripts require a script-security value of 2 or 3, a value that the profile can set itself. Also check auth-user-pass: without an argument, OpenVPN will ask for credentials at the prompt; with a path, it will read this file and send its contents to the server of the profile. Launching with sudo is normal to create the interface and routes, but no referenced file should be unknown.
# For each path found after “config”, from ~/vpn-test:
sed -n '1,220p' chemin-inclus.conf
A file whose origin or directives remain unknown should never be launched on a production server or on a machine whose SSH access is critical. The first attempt should be made in a disposable VM with independent console access. This reading reduces risk, it does not transform an arbitrary profile into a safe profile. Also reject daemon, log, log-append, and syslog for this diagnostic: they move messages out of the terminal and may leave the process in the background.
If the profile references relative paths, do not only move client.ovpn. Keep the received directory in a controlled location or copy each dependency re-read with the same structure and restrictive rights. For example, a profile that cites ca ca.crt must find ca.crt in ~/vpn-test.
install -m 600 /chemin/recu/ca.crt ~/vpn-test/ca.crt
An absent or unreadable dependency is a profile failure, distinct from a TLS error after the network attempt. A .ovpn file can also embed certificates between tags, and this check will then display no external path.
find ~/vpn-test -maxdepth 1 -type f -printf '%f %mn'
# For proto udp4/tcp4:
getent ahostsv4 vpn.example.net
# For proto udp6/tcp6:
getent ahostsv6 vpn.example.net
ip -brief link show > ~/vpn-test/links-before.txt
Replace the host and family with the values from remote and proto. A profile may contain multiple remote lines. If the family remains open, check both outputs and keep the address actually reported by OpenVPN in the log. The OpenVPN connection itself remains the useful test of the protocol and port, whether UDP or TCP.
Run the client in the foreground and keep the log
Run the client in the foreground. The terminal should then show the contacted address and the first actionable error. Install the package if openvpn is absent:
command -v openvpn || { sudo apt update && sudo apt install openvpn; }
sudo openvpn --config ~/vpn-test/client.ovpn --verb 3
With --verb 3, keep the first explicit error, the server address, and the phase where the client stops.
- Options error or a missing file indicates a path, directive, or certificate to correct before another attempt.
- TLS Error after the connection attempt requires checking the server, port, UDP/TCP protocol, network filtering,
tls-authortls-crypt, certificates, system time, and cryptographic settings of the profile. - A prompt for credentials may come from
auth-user-pass. Enter it at the prompt or use the mechanism provided by your provider, without adding the secret toclient.ovpn. - The message
Initialization Sequence Completedconfirms that the client has finished its initialization. It does not yet prove that the expected internal resource is reachable.
Keep the command open during the next check. Stop it with Ctrl+C only after verifying the interface, routes, and internal service.
Check the interface and route after connection
Record the interfaces before the connection and then compare them after. tun transports IP packets, tap emulates an Ethernet interface. The dev directive may also impose a name. Use only the name actually created. ip route get shows the device that Linux would choose, without sending a packet.
# After “Initialization Sequence Completed”:
ip -brief link show | diff -u ~/vpn-test/links-before.txt -
VPN_IF='name-of-the-created-interface'
ip -brief address show dev "$VPN_IF"
ip route get 10.8.0.10
ip route get 1.1.1.1
ip route get "$SSH_CLIENT_IP"
Replace 10.8.0.10 with a documented internal target by the team providing the VPN. Its route must mention the tunnel interface if this network passes through OpenVPN. Also re-read the route to SSH_CLIENT_IP if you had a remote session before the test.
The route does not prove that the internal service responds. Then test the expected service with the address and port documented by your team. For an HTTPS service that exposes a health page, for example:
getent hosts intranet.example.lan
curl --fail --silent --show-error --connect-timeout 5 --max-time 10 --output /dev/null --write-out '%{http_code}n' https://intranet.example.lan:443/health
Replace this name, the port, and the path with those of the actual service. The first command should return an internal address. The second should display the expected code from this service, for example 200 or 204. curl also verifies the TLS certificate: install the internal CA or use the service documentation if required, without adding --insecure. For an internal DNS, instead query a name and a resolver documented by your team.
I would choose this one-time launch before a service openvpn-client@... on a server, as the diagnostics remain separate from the startup. After Ctrl+C, check that the interface has disappeared, that the route to the internal target no longer uses the tunnel, and that the route to the noted SSH address returns to the expected device. A profile with down or route-pre-down scripts imposes this check, it does not allow assuming that the cleanup succeeded.
ip link show "$VPN_IF"
ip route get 10.8.0.10
ip route get "$SSH_CLIENT_IP"
The first command should indicate that the interface no longer exists. Compare both routes to their outputs recorded before and during the tunnel. A route still linked to the deleted interface or an error in the cleanup script requires verification before another attempt.