A system tray utility for managing VPN connections with a modern dark UI.
Designed to work on Windows 10/11 and Linux (X11 and Wayland). Built around a Raspberry Pi gateway architecture where a Pi running OpenVPN sits between your LAN and the internet, with this tray app managing routing on each client machine.
VPN provider: The included backend is written for ExpressVPN
.ovpnconfigs, but the shell script and web UI are straightforward to adapt to any OpenVPN-compatible provider.
- Location Switching:
- Searchable dialog with flags
- Grouped by continent (Europe, Asia, Americas, etc.)
- Favorites system
- Smart Automation (configured in Settings):
- Auto-Connect: Connects to the last used location on app launch.
- Auto-Reconnect: Optionally re-enables the VPN automatically when the server comes back online after a drop. Disabled by default - see the security note below.
- Run on Startup: (Windows) Option to launch automatically on system login.
- Security & Safety:
- Kill Switch: Blocks internet access if the VPN connection drops unexpectedly. When the VPN drops, the route is immediately removed and all traffic stops until you manually re-enable it - or until the server returns if Auto-Reconnect is enabled.
- Drop Debouncing: Requires two consecutive failed checks before triggering the kill switch, avoiding false positives.
- DNS Leak Protection: Automatically flushes DNS caches and enforces VPN DNS.
- Real-time Monitoring:
- Dashboard showing connection status, gateway IP, network adapter, and session duration.
- Tray icon changes colour based on status (green=Connected, blue=Enabling, yellow=Disabled, red=Dropped, grey=Server Unreachable).
- System notifications for critical events (kill switch, unexpected drops, server unreachable).
- Localization: Available in English, German, French, Spanish, Portuguese (Brazil), Italian, Russian, Simplified Chinese, Japanese, and Korean. Language is auto-detected from your system locale and can be changed in Settings.
By default, when the VPN drops the kill switch removes the routing rule and all internet traffic stops until you manually click "Enable VPN". This is intentional - it ensures no unprotected traffic ever leaves your machine without your explicit action.
The Auto-Reconnect option (in the Settings window under Behavior) will automatically re-enable the VPN when the server comes back online. This is convenient for situations like a scheduled Pi reboot, but it comes with a trade-off: you are trusting that the reconnection will succeed and that no traffic slips through in the window between the kill switch firing and the VPN being restored. If you use the VPN for strict privacy or to protect sensitive downloads, leave Auto-Reconnect off and reconnect manually.
[Client PC] → [Raspberry Pi (OpenVPN gateway)] → [Internet via VPN]
tray app ← backend web UI + switch script
The tray app modifies the client machine's routing table to send all traffic through the Pi.
The Pi runs OpenVPN and a small PHP web UI (backend/) that the tray app queries to fetch
available locations and trigger server switches. Each client machine runs the tray app
independently - no router configuration required.
sudo apt install openvpn apache2 php libapache2-mod-php
sudo systemctl restart apache2All backend configuration lives in one place. Copy the example and edit it:
sudo mkdir -p /etc/dragonfoxvpn
sudo cp backend/dragonfox.conf.example /etc/dragonfoxvpn/config.conf
sudo nano /etc/dragonfoxvpn/config.confThe file is well-commented. The values you'll need to change are:
| Setting | How to find it |
|---|---|
LAN_IF |
Run ip link - it's the interface with your LAN IP (usually eth0) |
LAN_NET |
Your router's subnet, e.g. 192.168.1.0/24 - check your router's DHCP settings |
PI_IP |
The Pi's own LAN IP address |
CONF_PREFIX |
The common prefix of your .ovpn filenames, e.g. my_expressvpn_ |
Everything else can be left as the default unless you have a specific reason to change it.
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -psudo mkdir -p /etc/openvpn/client/configs
sudo cp *.ovpn /etc/openvpn/client/configs/ExpressVPN users: Log into your account → Downloads → Manual Config → OpenVPN. Download the configs for the locations you want and copy them to the Pi. Each user must download their own - the files contain account-specific credentials.
If you want the full server list (160+ locations) without clicking every link by hand, ExpressVPN OVPN Scraper automates the bulk download. Leave
CONF_PREFIXempty in your config and the filenames will be picked up and grouped correctly without any adjustments.
sudo cp backend/vpn-route-up.sh /etc/openvpn/client/
sudo chmod +x /etc/openvpn/client/vpn-route-up.shYou never run this script manually. OpenVPN calls it automatically every time the tunnel comes up, via the
updirective incommon.conf. It reads its settings from your config file.
sudo cp backend/common.conf.example /etc/openvpn/client/common.confCreate a credentials file with your VPN provider username and password:
sudo nano /etc/openvpn/client/credentials.txt
# Line 1: your username
# Line 2: your password
sudo chmod 600 /etc/openvpn/client/credentials.txtProviders with embedded credentials: Some providers include credentials directly inside the
.ovpnfiles rather than using a separate auth file. If yours does, comment out theauth-user-passline incommon.conf- otherwise OpenVPN will fail to start.
sudo cp backend/switch-openvpn.sh /usr/local/bin/
sudo chmod +x /usr/local/bin/switch-openvpn.shAllow the web server to run it as root:
echo "www-data ALL=(root) NOPASSWD: /usr/local/bin/switch-openvpn.sh" \
| sudo tee /etc/sudoers.d/switch-openvpnEnable the OpenVPN service:
sudo systemctl enable --now openvpn-client@activesudo cp -r backend/. /var/www/vpn/
sudo chown -R www-data:www-data /var/www/vpn/sudo cp backend/apache-vhost.conf.example /etc/apache2/sites-available/vpn.confOpen the file and edit two lines to match your setup:
ServerName- the hostname or IP you'll use to reach the Pi (e.g.vpn.localor10.0.0.20)Require ip- your LAN subnet (e.g.192.168.1.0/24)
Caution
The web UI has no authentication. The Require ip directive is the only thing preventing
unauthorised access to the location switcher. If your Pi is directly reachable from the internet
(e.g. via a port-forwarded public IP), anyone on the internet can switch your VPN location.
Ensure the Pi's web port is not exposed beyond your LAN, or add HTTP Basic Auth on top.
sudo a2ensite vpn
sudo systemctl reload apache2Pre-built binaries for Windows and Linux are available on the GitHub Releases page. Download the binary for your platform and skip straight to the configuration steps below.
- Windows:
DragonFoxVPN.exe- Windows will prompt for administrator rights on launch - Linux:
DragonFoxVPN- make it executable (chmod +x DragonFoxVPN) and run it normally
If you'd rather build from source, see Building the Executables Yourself at the bottom of this file.
The tray app uses ip, resolvectl, and sysctl to manage routing. Rather than running the
whole app as root, grant your user passwordless access to just those commands:
sudo nano /etc/sudoers.d/dragonfoxvpnAdd this line, replacing yourusername with your actual username:
yourusername ALL=(root) NOPASSWD: /sbin/ip, /usr/bin/resolvectl, /sbin/sysctl, /usr/bin/systemd-resolve
Then run the app normally (no sudo).
On first launch the app shows a setup dialog. Here's what each field means:
| Field | What to enter |
|---|---|
| VPN Switcher URL | http:// or https:// followed by your Pi's IP or hostname, e.g. http://10.0.0.20 |
| VPN Server IP | Your Pi's LAN IP address (the same IP you SSH into it with). Auto-filled when you enter the URL. |
| Router IP | Your router's LAN IP - usually 192.168.1.1 or 10.0.0.1; check via ip route | grep default |
The VPN Server IP field is automatically populated via DNS lookup when you enter a valid Switcher URL. You can override it manually if needed.
Use the Test Connection button to verify all three values before saving - it checks that the switcher page is reachable and returns locations, and that both IPs respond to pings.
Access Settings... from the tray menu while the VPN is disconnected to update network configuration or adjust behaviour. Settings are organised into three sections:
- Network: VPN Switcher URL, VPN Server IP, Router IP, and the Test Connection button.
- Behavior: Auto-Connect on start, Auto-Reconnect if server returns, and Run on Startup (Windows only).
- Language: Select from 10 supported languages. The app restarts automatically when you save.
Run DragonFoxVPN.exe - Windows will automatically prompt for administrator rights (required to modify the routing table).
Run DragonFoxVPN from wherever you placed the binary. No sudo needed if you followed the sudoers step above.
The application stores its configuration in:
- Windows:
%APPDATA%\DragonFoxVPN\dragonfox_vpn.json - Linux:
~/.config/dragonfox_vpn.json
Flag icons are cached locally in a flags subdirectory alongside the config file.
- Confirm Apache is running:
sudo systemctl status apache2 - Check the vhost
Require ipline matches your client's subnet - Try accessing by IP directly:
http://<pi-ip>/to rule out DNS issues
- Check service status:
sudo systemctl status openvpn-client@active - View logs:
sudo journalctl -u openvpn-client@active -n 50 - Verify your credentials file has the correct username on line 1 and password on line 2
- Confirm at least one
.ovpnfile exists inEXPRESS_DIRand runswitch-openvpn.sh --refresh
- Confirm the sudoers entry for
www-datais in place:sudo cat /etc/sudoers.d/switch-openvpn - Test the script manually:
sudo /usr/local/bin/switch-openvpn.sh --refresh - Check Apache error log:
sudo tail /var/log/apache2/error.log
- The Pi is not responding to pings from the client - check they are on the same LAN
- Confirm the VPN Gateway IP in Settings matches the Pi's actual LAN IP
- This usually means
tracerouteto8.8.8.8isn't seeing the VPN gateway as the first hop - Confirm
vpn-route-up.shexecuted successfully by checking OpenVPN logs - Verify IP forwarding is still enabled:
sysctl net.ipv4.ip_forward(should return1)
- The app removes the VPN route but your default route should return automatically
- If internet is still broken, run:
sudo ip route add default via <your-router-ip>
If you prefer to build from source rather than using a pre-built release binary:
- Rust stable toolchain - install via rustup.rs
- Windows: The default Rust toolchain on Windows uses the MSVC linker, so you need Build Tools for Visual Studio (free, standalone) or a full Visual Studio install with the "Desktop development with C++" workload - this provides the linker, not any C++ compilation
- Linux:
libappindicator3-devorlibayatana-appindicator3-devfor the system tray
# Arch-based
sudo pacman -S libayatana-appindicator
# Debian/Ubuntu
sudo apt install libayatana-appindicator3-devcargo build --releaseThe output binary is placed at:
- Linux:
target/release/DragonFoxVPN - Windows:
target\release\DragonFoxVPN.exe
Move it somewhere on your PATH (e.g. /usr/local/bin on Linux) before running.
On Windows you can also use the included PowerShell script:
.\build_windows.ps1MIT License - see LICENSE for details.
Bundled dependency: flag-icons by Panayiotis Lipiridis (MIT).