Introducing the WG-Easy Installer by LINUXexpert.org
If you’ve ever stood up WireGuard on a fresh server and then layered on reverse proxying, TLS, firewalls, fail2ban, unattended updates, health checks, backups, and autostart… you know that “quick weekend project” can spiral. We built a single, quiet, interactive installer that does it all — and keeps the web UI HTTPS-only behind Caddy with Let’s Encrypt.
Today we’re open-sourcing it:
👉 GitHub: https://github.com/LINUXexpert-org/WG-Easy-Installer
What it is
A hardened, production-minded deployment of WireGuard + wg-easy using Docker, fronted by Caddy with automatic Let’s Encrypt certificates and an explicit HTTP→HTTPS redirect. The installer is interactive (CLI or optional TUI), runs quietly after prompts with a percentage progress bar, and logs everything. It also sets up:
- UFW firewall rules (22/tcp, 80/tcp, 443/tcp, WG UDP)
- fail2ban jails for SSH and the Caddy UI
- unattended-upgrades for security updates
- Autostart for Docker, Caddy, and the compose stack
- Backup/restore utility
- Uninstall utility (safe & reversible)
- Healthcheck tool (ports/services/TLS/DNS) with JSON & webhook support
Why you might want this
- Security by default: UI bound to loopback; all external access is over HTTPS via Caddy (optionally with Basic Auth and IP allowlisting).
- Fast & repeatable: A single command on a fresh Debian 13 host; re-run safely to update/repair.
- Ops-friendly: Logs every step; includes timers/cron examples; exit codes for monitoring.
One-command install (on Debian 13)
# As root (or sudo -i), after cloning the repo:
git clone https://github.com/LINUXexpert-org/WG-Easy-Installer.git
cd WG-Easy-Installer
sudo bash install-wg-easy.sh
You’ll choose TUI or CLI prompts, set your domain (e.g., vpn.example.com
), email for ACME, and (optionally) enable Basic Auth, IP allowlist, UFW, fail2ban, and unattended-upgrades.
Ensure your DNS A/AAAA records point to the server before running, and that ports 80/443 are free.
What you get after install
- Web UI:
https://<your-domain>/
(wg-easy proxied via Caddy) - wg-easy bound privately on
127.0.0.1:<port>
- Explicit 308 redirect from HTTP to HTTPS
- HSTS & security headers
- Optional Basic Auth: one more layer for the UI
- Autostart via systemd + Docker restart policies
Included tools
Backup & restore
# Install helper
sudo install -m 0750 wg-easy-backup.sh /usr/local/sbin/wg-easy-backup.sh
# Backup (creates ./wg-easy-backup-*.tgz)
sudo wg-easy-backup.sh backup
# Include Caddy cert/key state (sensitive!)
sudo wg-easy-backup.sh backup --include-certs
# Restore from a tarball
sudo wg-easy-backup.sh restore ./wg-easy-backup-YYYYmmdd-HHMMSS.tgz
Uninstall (safe & interactive)
sudo install -m 0750 wg-easy-uninstall.sh /usr/local/sbin/wg-easy-uninstall.sh
sudo wg-easy-uninstall.sh
Lets you stop the stack, remove the compose unit, optionally remove configs, fail2ban snippets, Caddyfile entries, and even the packages — with backups.
Healthcheck (with JSON & webhook)
sudo install -m 0755 wg-easy-healthcheck.sh /usr/local/sbin/wg-easy-healthcheck.sh
# Human output + log
sudo wg-easy-healthcheck.sh --domain vpn.example.com
# JSON output + webhook (e.g., healthchecks.io)
sudo wg-easy-healthcheck.sh --domain vpn.example.com --json --webhook "https://hc-ping.com/UUID"
Checks Docker, Caddy, optional fail2ban/UFW/unattended-upgrades, wg-easy container, port listeners (80/443 + WG UDP), HTTP→HTTPS redirect, HTTPS reachability, TLS days to expiry, and DNS vs public IP. Exit codes: 0 OK
, 1 WARNING
, 2 CRITICAL
, 3 UNKNOWN
.
Systemd timer (recommended) for health checks
Create /etc/systemd/system/wg-easy-health.service
:
[Unit]
Description=wg-easy stack healthcheck
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/wg-easy-healthcheck.sh --domain vpn.example.com --warn-days 30 --crit-days 10 --log /var/log/wg-easy-health.log
Create /etc/systemd/system/wg-easy-health.timer
:
[Unit]
Description=Run wg-easy healthcheck every 15 minutes
[Timer]
OnBootSec=2m
OnUnitActiveSec=15m
AccuracySec=1m
Unit=wg-easy-health.service
[Install]
WantedBy=timers.target
Enable:
sudo systemctl daemon-reload
sudo systemctl enable --now wg-easy-health.timer
Under the hood
- Docker Engine + compose plugin enabled at boot
- wg-easy container with
restart: unless-stopped
- Optional systemd unit to re-
up -d
the compose stack on boot - Caddy (enabled) with ACME (staging/production) + security headers
- UFW (optional) opens only SSH, HTTP(S), and the WG UDP port
- fail2ban (optional) for SSH + Caddy HTTP errors (401/403)
- unattended-upgrades (optional) with optional email notices
net.ipv4.ip_forward=1
(+ optional IPv6 forwarding)
Requirements & scope
- OS: Debian 13 “Trixie” (Debian 12+ likely compatible)
- Access: root/sudo on a clean server
- Networking: DNS A/AAAA → server; ports 80/443 available externally
Get the code
- Repo: https://github.com/LINUXexpert-org/WG-Easy-Installer
- Issues/PRs welcome — we’d love your feedback and contributions.
License
All scripts are released under GPLv3.
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; see the full license for details. https://www.gnu.org/licenses/