Deploy VPN on Headless Servers
FreeGuard CLI runs natively on headless Linux servers with no desktop environment required. Whether you are securing a cloud instance, protecting a home lab, or routing container traffic through VPN, this guide covers installation, authentication, service configuration, and monitoring for production server deployments.
Why Run VPN on a Server?
Servers handle sensitive traffic: database connections, API calls, backup transfers, and inter-service communication. Running VPN on the server itself ensures all outbound traffic is encrypted without configuring individual applications.
Common use cases:
- Cloud instances accessing geo-restricted APIs
- CI/CD runners that need consistent network identity
- Self-hosted services that benefit from IP masking
- Development servers used by distributed teams
Installation
Ubuntu / Debian
curl -fsSL https://cli.freeguard.com/install.sh | bash
Or install from the APT repository:
curl -fsSL https://cli.freeguard.com/gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/freeguard.gpg
echo "deb [signed-by=/usr/share/keyrings/freeguard.gpg] https://apt.freeguard.com stable main" | sudo tee /etc/apt/sources.list.d/freeguard.list
sudo apt update
sudo apt install freeguard-cli
CentOS / RHEL / Fedora
curl -fsSL https://cli.freeguard.com/install.sh | bash
Or use the RPM repository:
sudo rpm --import https://cli.freeguard.com/gpg.key
sudo tee /etc/yum.repos.d/freeguard.repo <<EOF
[freeguard]
name=FreeGuard CLI
baseurl=https://rpm.freeguard.com/stable
gpgcheck=1
gpgkey=https://cli.freeguard.com/gpg.key
EOF
sudo yum install freeguard-cli
Alpine Linux
curl -fsSL https://cli.freeguard.com/install.sh | bash
Alpine users may need to install libc6-compat first:
apk add libc6-compat
curl -fsSL https://cli.freeguard.com/install.sh | bash
Verify Installation
freeguard --version
Headless Authentication
On servers without a browser, you cannot use the default OAuth login flow. FreeGuard CLI provides email-based authentication for headless environments.
Email Login
freeguard login --method email
The CLI prompts you to enter your account email. A verification code is sent to that address. Enter the code in the terminal to complete authentication.
$ freeguard login --method email
Enter your email: [email protected]
Verification code sent. Check your inbox.
Enter code: 483291
Login successful.
Token-Based Login
For automated deployments, use a service token:
freeguard login --method token --token YOUR_SERVICE_TOKEN
Generate service tokens from your account dashboard. Tokens can be scoped to specific servers and have configurable expiration.
Verify Authentication
freeguard status --json | jq '.authenticated'
Running as a systemd Service
For production deployments, run FreeGuard CLI as a systemd service so it starts on boot and restarts on failure.
Create the Service File
sudo tee /etc/systemd/system/freeguard.service <<EOF
[Unit]
Description=FreeGuard VPN
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/freeguard connect --server us-east --no-color
ExecStop=/usr/local/bin/freeguard disconnect
Restart=on-failure
RestartSec=10
User=root
[Install]
WantedBy=multi-user.target
EOF
Enable and Start
sudo systemctl daemon-reload
sudo systemctl enable freeguard
sudo systemctl start freeguard
Check Service Status
sudo systemctl status freeguard
View Logs
sudo journalctl -u freeguard -f
Scripting with --json and --no-color
Production scripts should parse structured output rather than human-readable text.
JSON Output
Every command supports --json:
# Connection status
freeguard status --json
Output:
{
"connected": true,
"server": "us-east-1",
"server_name": "New York",
"protocol": "hysteria2",
"uptime": 86400,
"bytes_sent": 1048576,
"bytes_received": 5242880
}
No-Color Mode
Strip ANSI color codes for clean log files:
freeguard status --no-color >> /var/log/freeguard-status.log
Scripting Example
A health-check script that reconnects on failure:
#!/bin/bash
STATUS=$(freeguard status --json 2>/dev/null)
CONNECTED=$(echo "$STATUS" | jq -r '.connected')
if [ "$CONNECTED" != "true" ]; then
echo "$(date): VPN disconnected, reconnecting..." >> /var/log/freeguard-health.log
freeguard connect --server us-east --json >> /var/log/freeguard-health.log 2>&1
fi
Add it to cron for regular checks:
# Check VPN health every 5 minutes
*/5 * * * * /opt/scripts/freeguard-health.sh
Monitoring and Health Checks
Basic Health Check
freeguard status --json | jq -r '.connected'
Returns true or false. Simple enough for any monitoring system.
Integration with Monitoring Tools
Export status to Prometheus, Datadog, or any monitoring system that can execute shell commands:
#!/bin/bash
# prometheus-exporter.sh
STATUS=$(freeguard status --json)
CONNECTED=$(echo "$STATUS" | jq -r '.connected')
UPTIME=$(echo "$STATUS" | jq -r '.uptime')
if [ "$CONNECTED" = "true" ]; then
echo "freeguard_connected 1"
echo "freeguard_uptime_seconds $UPTIME"
else
echo "freeguard_connected 0"
echo "freeguard_uptime_seconds 0"
fi
Alerting
Combine the health check with your alerting system. Example with a simple webhook:
#!/bin/bash
CONNECTED=$(freeguard status --json | jq -r '.connected')
if [ "$CONNECTED" != "true" ]; then
curl -X POST https://hooks.example.com/alert \
-H "Content-Type: application/json" \
-d '{"text": "FreeGuard VPN disconnected on '"$(hostname)"'"}'
fi
Security Considerations
- Run as root: The VPN tunnel requires root privileges to modify network routes. The systemd service runs as root by default.
- Token storage: Service tokens are stored in
~/.config/freeguard/. Ensure appropriate file permissions (chmod 600). - Firewall rules: If you use UFW or iptables, ensure the VPN tunnel interface is allowed.
- Kill switch: Built in, with nothing to enable. While FreeGuard is running, if the VPN drops, traffic routed through FreeGuard fails instead of going out over the server’s regular connection until FreeGuard reconnects.
Next Steps
- FreeGuard CLI — full command reference
- Developer workflow guide — integrate VPN into your development tools
- Pricing — server deployments work with any plan
Last updated: March 2026