Deploying a self-hosted VPN often forces administrators to make a difficult trade-off between user-friendly management and production-grade security. Standard WireGuard installations with basic web dashboards often leave management ports exposed to the public internet, neglect automated SSL handling, and leave client networks vulnerable to lateral movement.
The wg-easy-billfoo-autoinstaller provides a hardened, zero-maintenance alternative. It pairs WG-Easy (WireGuard + Web-UI) with an automated Caddy reverse proxy on Ubuntu, enforcing strict Zero-Trust network access, automated Let’s Encrypt certificates, client-to-client isolation, and a self-healing Docker architecture.
Below is a comprehensive technical guide detailing the prerequisites, one-click installation, idempotent reconfiguration (smart auto-import), and underlying security architecture.
Prerequisites Before You Begin
Ensure the target environment meets the following specifications before launching the installer:
- Fresh Ubuntu Server: You must be logged in as
root. The script modifies kernel routing parameters and firewall rules, strictly requiring root execution. - Public IPv4 Address: The dedicated public IP address assigned to your server.
- Registered Domain Name: At least one domain or subdomain (e.g.,
vpn.yourdomain.com). - Configured DNS Records: An A-Record at your domain registrar pointing directly to your server’s public IPv4.
- Cloudflare DNS Users (Crucial): The A-Record for your Web-UI must be set to “DNS Only” (Grey Cloud). Enabling Cloudflare proxying (Orange Cloud) masks client IP addresses, which breaks the script’s internal Zero-Trust IP whitelisting rules.
- Cloud Provider Firewall: If hosted on IONOS, AWS, Azure, or Hetzner, you must log into your provider’s control panel and open incoming UDP traffic on Port 51820.
One-Click Installation
To deploy the server, launch the interactive setup wizard using the universal command below. The command automatically falls back to wget if curl is absent on a minimal installation:
bash <(curl -sSL https://raw.githubusercontent.com/billfoo/wg-easy-billfoo-autoinstaller/main/install.sh || wget -O - https://raw.githubusercontent.com/billfoo/wg-easy-billfoo-autoinstaller/main/install.sh)
The Configuration Wizard
The installer runs on an interactive state machine. You can type b and press Enter at any point to step back to the previous question:
- VPN Host: The primary domain or IP address your WireGuard clients will connect to.
- Web-UI Domain: The domain for accessing the administration dashboard. You can reuse the VPN domain or define a separate subdomain (e.g.,
admin.yourdomain.com). - Admin Password: A strong password to secure the dashboard.
- Access Control: Select between Public (accessible over the internet) or VPN-Only (Zero-Trust: the dashboard is strictly unreachable unless the client is actively connected to the VPN tunnel).
Re-Running the Script (Smart Auto-Import)
The script is fully idempotent and upgrade-safe. If you ever need to update your domain, modify passwords, switch from “Public” to “VPN-Only”, or apply updates, run the exact same installation command as root:
bash <(curl -sSL https://raw.githubusercontent.com/billfoo/wg-easy-billfoo-autoinstaller/main/install.sh || wget -O - https://raw.githubusercontent.com/billfoo/wg-easy-billfoo-autoinstaller/main/install.sh)
What Happens on an Existing Setup?
- Detection: The script checks for
/opt/wg-easy/docker-compose.yml. If present, it automatically enters Update Mode. - Configuration Extraction: It parses the active
docker-compose.ymlandCaddyfileto extract the active VPN Host, UI Host, and the existing bcrypt password hash. - Default Overrides: During the prompts, your current configuration values are displayed inside brackets
[...]. Simply pressing Enter retains the active configuration. - Password Retention: You can leave the password prompt empty. The script reuses the existing, securely hashed password.
- Seamless Application: The installer updates the configuration files and executes
docker compose up -dfollowed bydocker compose restart caddy. Docker only recreates modified containers. Your existing client configurations, keys, and QR codes located in/opt/wg-easy/wireguardremain untouched.
What the Script Does (Under the Hood)
Upon execution, the script completes several low-level provisioning tasks autonomously:
- Dependency Resolution: Refreshes
aptpackage repositories and installs required utilities:curl,ufw,sed, andgrep. - Kernel IP Forwarding: Creates
/etc/sysctl.d/99-wireguard.conf, setsnet.ipv4.ip_forward=1andnet.ipv6.conf.all.forwarding=1, and reloadssysctlto permit packet forwarding through the Linux kernel. - UFW Firewall Hardening: Applies a strict Default Deny incoming traffic policy. It opens only
22/tcp(SSH),80/tcp&443/tcp(Caddy/HTTPS), and51820/udp(WireGuard), then enables UFW. - Docker Provisioning: Inspects the system for a Docker runtime and executes the official Docker convenience installation script if missing.
- Native Bcrypt Hashing: Pulls the WG-Easy Docker image and runs its internal
wgpwtool to compute a standard bcrypt hash. It escapes all$symbols to$$usingsedto prevent variable expansion issues in Docker Compose. - Stack Generation: Creates the
/opt/wg-easydirectory structure and outputs a customizeddocker-compose.ymlfile managing WG-Easy, Caddy, and Autoheal. - Dynamic Proxy Rules: Generates the
Caddyfile. If the “VPN-Only” mode is selected, it calculates internal Docker NAT ranges and writes them into Caddy’s remote IP whitelist.
Architecture & Security Deep Dive
| Feature | Implementation | Security Benefit |
|---|---|---|
| Invisible Caddy Proxy | Caddy handles Let’s Encrypt validation. If “VPN-Only” is enabled, unauthorized external IP addresses receive a TCP abort drop. | No login page is loaded, and no HTTP status codes (401/403) are returned to external IP addresses, shielding the server from automated scanners. |
| Docker NAT Resolution | The script explicitly whitelists 172.16.0.0/12 within Caddy’s access rules. | Traffic routed from the WireGuard container to Caddy is masqueraded by the Docker bridge network. Whitelisting prevents administrators from being locked out. |
| Client Isolation | Applies iptables -I FORWARD -i wg0 -o wg0 -j REJECT during interface boot via WG_POST_UP. | Connected VPN clients can reach the internet but cannot scan, ping, or communicate with each other inside the VPN subnet. |
| Self-Healing | A Docker Autoheal container tests the wg0 interface every 30 seconds. | If health probes fail 3 consecutive times, the container restarts automatically to resolve frozen kernel states without manual intervention. |
Troubleshooting & FAQ
Connected to the VPN, but no internet traffic (0 Bytes received)?
This is almost always an external cloud firewall restriction. Ensure your cloud hosting control panel (Hetzner, AWS, IONOS, etc.) allows incoming UDP traffic on port 51820. WireGuard uses UDP exclusively.
“ERR_HTTP2_PROTOCOL_ERROR” when opening the Web UI?
This confirms that Caddy is actively dropping unauthorized connections via abort. Verify that your WireGuard tunnel is active on your device and ensure the Web-UI domain record on Cloudflare is configured as “DNS Only” (Grey Cloud).
502 Bad Gateway Error
This commonly occurs immediately after installation while Caddy negotiates SSL certificates with Let’s Encrypt. Wait 30 seconds and refresh. If the error persists, inspect container activity using:
sudo docker logs wg-easy
sudo docker logs caddy-proxy
Android WireGuard App shows “Unknown Section” on QR scan?
Ensure your web browser is not automatically translating the dashboard into another language (which changes configuration tags like [Interface]). The dashboard must remain in English to produce syntactically valid configuration files.
How to Completely Uninstall the Stack
To tear down the containers and remove all persistent data from your machine, execute:
cd /opt/wg-easy && sudo docker compose down -v
sudo rm -rf /opt/wg-easy
Backup & Disaster Recovery Strategy
All stateful assets—including client private/public keypairs, configuration templates, and internal database records—are stored in the directory:
/opt/wg-easy/wireguard
To back up your setup, copy this directory to an offsite location. If you migrate servers, copy the directory back to /opt/wg-easy/wireguard and execute the installation script. All existing clients will instantly reconnect without requiring new configurations or QR code scans.