Stockio

Automate VPN Connections With Scripts and Task Tools

Build a systemd VPN health check that distinguishes interfaces, handshakes, and real reachability, with structured status output and safe recovery rules.

By Ravi Desai

DevOps & Security Engineer ·

On this page

A script that checks whether wg0 exists can report success while every useful request times out. An interface, an authenticated peer, a working route, and a reachable application are separate signals. Reliable automation makes those distinctions explicit.

This example uses a Linux WireGuard client managed by wg-quick and systemd. It creates a small, read-only health probe, schedules it, and explains how to add bounded recovery without replacing a real firewall kill switch.

Define states before writing actions

Use explicit states instead of a single connected flag. For a basic client, distinguish an absent interface, a reachable tunnel endpoint, and a failed application probe. More advanced checks can separately record DNS resolution, recent peer handshakes, and public egress identity.

Use the JSON Formatter & Syntax Linter below to validate a sample status document before integrating it with monitoring. Consistent field names and types prevent downstream tools from treating the string false as a Boolean value. Test with invented interface names and generic error messages rather than real credentials.

Define success criteria for your policy. In this walkthrough, success means an HTTPS health endpoint under your control answers through the tunnel interface with a successful HTTP status. That is deliberately narrower than proving every application is protected. A split-tunnel client may need different checks for private services and public internet traffic.

Establish a manual baseline

First verify the existing VPN by hand. Install the distribution's WireGuard tools and curl if necessary. Do not create a second network manager for an interface already controlled by a desktop VPN client; choose one owner for lifecycle operations.

For a wg-quick profile at /etc/wireguard/wg0.conf, inspect its service and peer state:

sudo systemctl status wg-quick@wg0.service --no-pager
sudo wg show wg0 latest-handshakes
ip route get 192.0.2.10
sudo curl --interface wg0 --fail --silent --show-error   --connect-timeout 5 --max-time 10 https://health.example.com/vpn

Replace the health URL with an endpoint you operate that returns a 2xx response without authentication. The documentation hostname is a placeholder, not a public test service. Use a small response body, stable certificate, and no sensitive query parameters.

The route lookup uses a documentation address only to inspect routing; it does not test an actual server. Curl's interface binding checks this particular request, not the entire routing policy. DNS resolution may follow a separate system path, so treat resolver behavior as an additional test. Review WireGuard's quick start before changing peer routes or addresses.

Write a probe with predictable output

Create /usr/local/sbin/vpn-health as a root-owned executable. The script below emits fixed JSON fields and uses no untrusted text in its output. It intentionally does not restart anything, change routes, or alter firewall rules.

#!/usr/bin/env bash
set -u
PATH=/usr/sbin:/usr/bin:/sbin:/bin
iface=wg0
url=https://health.example.com/vpn
state=interface_down
ok=false
if ip link show dev "$iface" >/dev/null 2>&1; then
state=probe_failed
if curl --interface "$iface" --fail --silent --show-error       --connect-timeout 5 --max-time 10 "$url" >/dev/null 2>&1; then
  state=healthy
  ok=true
fi
fi
printf '{"interface":"wg0","ok":%s,"state":"%s"}
' "$ok" "$state"
if [ "$ok" = true ]; then
exit 0
fi
exit 1

Set ownership to root and permissions to 0750 using an administrator account. Keep the script directory unwritable by ordinary users. Do not let a web request choose the interface, URL, or command arguments for a privileged health check.

A timeout is ambiguous evidence: the remote application might be down, the hotspot may require sign-in, or the route might be broken. Do not translate every failure into a tunnel restart. Consider a second independent endpoint for diagnosis, while remembering that multiple probes add traffic and operational dependencies.

For scripts that include arbitrary error text, construct JSON with a real serializer such as jq instead of interpolating strings. Quotes, backslashes, and newlines can otherwise invalidate the status document or mislead log processors.

Schedule checks without overlapping jobs

Create a systemd service and timer using the following content in the two named files. The first unit runs one probe; the timer starts it after boot and repeats after the previous activation completes. A oneshot unit does not run overlapping copies of itself.

# /etc/systemd/system/vpn-health.service
[Unit]
Description=Read-only WireGuard health probe
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/vpn-health
TimeoutStartSec=20
User=root
UMask=0077

# /etc/systemd/system/vpn-health.timer
[Unit]
Description=Periodic WireGuard health probe

[Timer]
OnBootSec=45s
OnUnitInactiveSec=60s
Unit=vpn-health.service

[Install]
WantedBy=timers.target

Reload systemd's configuration with systemctl daemon-reload, then enable and start vpn-health.timer. Inspect results with journalctl -u vpn-health.service and check scheduling with systemctl list-timers. A nonzero probe exit is expected for an unhealthy state, so a failed service entry does not necessarily mean the script itself crashed.

The unit uses minimal assumptions, not a complete sandbox. Root is used for interface binding compatibility; review whether a less privileged account works on your system. Keep network access available to the probe when adding service hardening. The systemd project documentation provides background on unit behavior and service management.

Add recovery only after observing failures

Start with notification-only automation for several real network transitions: boot, Wi-Fi roaming, sleep and resume, and temporary endpoint downtime. Preserve timestamps and state changes, but do not collect private keys or packet payloads. Reduce repetitive healthy messages if they overwhelm useful evidence.

If recovery is needed, require several consecutive failures and a cooldown before one authorized restart. Count attempts persistently enough to avoid a restart storm. Repeated failure should stop automated action and alert an operator, not endlessly reset networking. Ensure the script cannot terminate the SSH session used to repair a remote machine without an alternate access path.

Keep traffic blocking independent from health reporting. A script that runs once a minute leaves gaps if it is the only protection against direct egress. Use the VPN client's documented kill switch or a carefully tested firewall policy, following the kill-switch configuration guide. For firewall concepts, consult Ubuntu's UFW documentation.

Finally, copy a sanitized status line into the JSON validation tool and confirm its shape matches monitoring expectations. Test both healthy and unhealthy output. Good automation reports uncertainty honestly instead of promising protection from one green indicator.

Key takeaways

  • Model interface state and application reachability as different observations.
  • Use short timeouts, fixed JSON fields, and an endpoint under your control.
  • Schedule read-only checks before adding restart privileges or recovery actions.
  • Bound retries and preserve an alternate administrative access path.
  • Enforce leak prevention independently of the monitoring script.
Featured sponsor

Reach designers & developers at work

Put your product next to the tools people use every day. One sponsor per placement, no tracking pixels.

Become a sponsor

Keep reading

Related articles

More in VPN & Online Privacy
VPN & Online Privacy

Prevent DNS Leaks on Linux With Custom Network Setups

Configure Linux resolver ownership and VPN DNS routing, then verify UDP, TCP, IPv6, and browser behavior with repeatable tests and a redacted audit record.

Includes: JSON formatterRavi DesaiRavi Desai
Backend & APIs

10 Best API Testing Tools

Choose an API testing tool for quick requests, shared collections, or automated checks, with clear notes on each option’s workflow, strengths, limitations, and pricing model.

Includes: JSON formatterChidi NwosuChidi Nwosu
Dev Tools & AI Coding

8 Best AI Code Review Tools

Compare eight AI-assisted code review tools for pull requests, security checks, and repository workflows, with practical cautions about false alarms, context, and human responsibility.

Includes: JSON formatterSofia MarchettiSofia Marchetti
AI Builders & APIs

10 Best Vector Databases Compared for RAG and AI Apps

Compare ten vector database choices for AI search and retrieval, from managed services to open-source projects, with practical notes on setup, scale, trade-offs, and choosing a fit.

Includes: JSON formatterChidi NwosuChidi Nwosu
Design Systems

8 Best Design Token Tools

Compare eight ways to create, organise, and ship design tokens across design files and code, including approachable options for small teams and advanced pipelines.

Includes: JSON formatterJonah WeissJonah Weiss
JavaScript & Frameworks

10 Best JavaScript Charting Libraries

Compare ten JavaScript chart libraries for dashboards, reports, and interactive data stories. See where each excels, what it costs, and how to choose based on your app and team.

Includes: JSON formatterSofia MarchettiSofia Marchetti