想在 VPS 上管理多台手机和电脑的 WireGuard 连接,又不想每加一台设备就手工改配置,可以用 wg-easy。它把设备配置、二维码和管理页面放在一起,但 HTTPS 面板、UDP 入口和数据备份仍然要自己配好。
这篇按 wg-easy 15、Docker Compose 和 Caddy 编写,适合个人设备接入与小规模远程访问。采用 IPv4 隧道方案,正文配置依据官方文档整理,未做真实 VPS 吞吐或客户端互通实测。已有原生 WireGuard 的机器先检查端口和网段冲突;只想手工管理密钥,可以看 WireGuard 基础搭建教程。
个人少量设备可以从 1 vCPU、1GB RAM、10GB SSD 评估;还要运行其他应用时,建议从 2GB RAM 起步。这是资源建议,不对应固定带宽或在线人数。持续传输能力还取决于 CPU、共享带宽、丢包和客户端网络。
购买前确认 Linux 内核支持 WireGuard、容器可用 NET_ADMIN、服务商允许所需 UDP 入站,且出站流量额度足够。KVM VPS 通常更容易自己管理内核和模块;受限容器型 VPS 要先问清权限,单纯加内存不能解决权限缺失。
本例需要三个地址,别混在一起:
| 地址 | 用途 | 该填什么 |
|---|---|---|
wg-admin.example.com | 浏览器管理页面 | A 记录指向 VPS 公网 IPv4 |
vpn.example.com | WireGuard 客户端 Endpoint | A 记录指向 VPS 公网 IPv4 |
| 管理员公网 IPv4 | 限制谁能打开面板 | 你当前上网出口地址,不是 VPS 地址 |
两个域名都按 DNS 直连配置,本文不叠加 CDN 或其他反向代理。只按 IPv4 部署时不要留下指向错误地址的 AAAA 记录。WireGuard 的 UDP 流量直接进入 VPS,Caddy 只处理管理页面的 HTTPS。
本例使用 ghcr.io/wg-easy/wg-easy:15。官方说明中,15 跟随该大版本更新,latest 仍可能指向旧 v14;因此不要省略标签。正式运行后记录实际镜像 digest,升级前备份。标签说明见 wg-easy Getting Started。
v15 重写了配置方式,许多 v14 环境变量已经移到管理界面。新装不要照抄旧版的 WG_HOST、PASSWORD_HASH 等整套配置。v14 用户需要保留 wg0.json 和旧环境变量,按 官方迁移指南 在初始化向导中导入;这不等于把 v15 数据回退给 v14。
下面在 Ubuntu 24.04 的 root shell 中操作,假设已经安装 Docker Engine 与 Compose 插件。安装步骤可参考 VPS Docker 部署教程。
uname -r
docker version
docker compose version
modprobe wireguard
ss -lntup
ip route
docker network ls
install -d -m 700 /opt/wg-easy
cd /opt/wg-easy
install -d -m 700 wireguard caddy-data caddy-config backups
确认 TCP 80、443 和 UDP 51820 没被其他服务占用。下面的 Docker 网段 10.42.42.0/24 也不能与现有 Docker 网络、家庭网段或公司内网重叠;必要时用 docker network inspect 查看现有网段,再整体替换示例中的子网和固定地址。
把下面保存为 /opt/wg-easy/compose.yaml:
name: wg-easy
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy:15
environment:
INSECURE: "false"
DISABLE_IPV6: "true"
volumes:
- ./wireguard:/etc/wireguard
- /lib/modules:/lib/modules:ro
ports:
- "51820:51820/udp"
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
net.ipv4.ip_forward: "1"
net.ipv4.conf.all.src_valid_mark: "1"
networks:
wg:
ipv4_address: 10.42.42.42
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
caddy:
image: caddy:2-alpine
ports:
- "80:80/tcp"
- "443:443/tcp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./caddy-data:/data
- ./caddy-config:/config
networks:
- wg
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
networks:
wg:
driver: bridge
ipam:
config:
- subnet: 10.42.42.0/24
这是基于 官方 Compose 示例 调整的 IPv4 方案:去掉宿主机 51821 映射,增加 Caddy,并用本地目录保存状态。NET_ADMIN、SYS_MODULE 权限较高,应使用官方镜像并保护宿主机管理权限,不需要再加 privileged: true。
DISABLE_IPV6=true 会让 wg-easy 不给隧道和客户端配置 IPv6,含义见 可选配置说明。它不会替你关闭手机或电脑原来的 IPv6 网络;涉及全流量接管时,后面还需要单独检查 IPv6。
创建 /opt/wg-easy/Caddyfile。把域名替换成自己的管理域名,198.51.100.25/32 替换成你当前管理电脑的真实公网 IPv4:
wg-admin.example.com {
@outside not remote_ip 198.51.100.25/32
respond @outside "Forbidden" 403
reverse_proxy wg-easy:51821
}
这个示例地址不能原样使用。来源限制用于保护首次初始化页面,也可以长期保留;换网络后要通过 SSH 更新允许地址。它仅限制面板 HTTP 请求,不限制 WireGuard 客户端的 UDP 接入。这里按客户端直连 Caddy 设计,额外套代理会改变 remote_ip 的含义,参见 Caddy 请求匹配规则。
防火墙和云安全组放行 TCP 80、443,以及 UDP 51820;SSH 端口继续保留现有管理规则。不要额外开放 TCP 51821,也不要把 51820 错开成只有 TCP。Docker 的端口发布需要结合宿主机实际防火墙机制检查,不能只看 UFW 的显示结果。
域名正确解析后,在 VPS 执行:
cd /opt/wg-easy
docker compose config --quiet
docker compose pull
docker compose run --rm --no-deps caddy \
caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
docker compose up -d
docker compose ps
docker compose logs --tail=100 caddy
docker compose logs --tail=100 wg-easy
从允许的管理电脑打开 https://wg-admin.example.com。使用真实公网域名时,Caddy 会按 自动 HTTPS 机制 申请并续期证书;不要为这个公网入口照抄 tls internal。面板反向代理结构也可对照 wg-easy 的 Caddy 示例。
本例保留 INSECURE=false,从浏览器使用 HTTPS。不要为了绕过证书或登录问题,直接公开明文管理端口。已有 Nginx/Caddy 占用 80、443 的机器,应把站点合并进现有代理配置,不能同时启动第二个占用相同端口的代理;可参考 Caddy 反向代理指南。
在向导中创建管理员账号与长随机密码,新安装选择没有旧配置。Host 填 vpn.example.com,Port 填 51820;Host 是客户端实际连接的服务器地址,不是 https:// URL,也不是 Docker 内部地址。字段含义见 初始化说明。
进入 Account 页面开启 TOTP 两步验证,验证动态码并妥善保存恢复所需信息,操作见 2FA 指南。面板 2FA 保护管理登录,不会要求每次 WireGuard 握手都输入动态码。
为手机、笔记本分别建客户端,例如 phone-main、laptop-work。手机用官方 WireGuard 客户端扫描二维码,电脑导入下载的配置文件。二维码和配置包含私钥,别发到公开工单或截图里;设备丢失时禁用或删除对应客户端,不要让多台设备共用同一份配置。
连接后,在 VPS 检查握手:
cd /opt/wg-easy
docker compose exec wg-easy wg show
确认目标 peer 有近期握手和传输计数,再从客户端访问计划中的目标服务。面板能打开只代表 HTTPS 正常,不代表 UDP 隧道正常;手机 Wi-Fi 和移动网络最好各测试一次。
只访问某个受控服务时,用精确的 IP 或网段作为客户端 AllowedIPs;需要通过 VPS 传输全部 IPv4 流量时,才考虑 0.0.0.0/0。目标网段必须确实能从服务器路由到达,仅改客户端 AllowedIPs 不会凭空建立回程路由。
AllowedIPs 主要控制客户端路由,不能代替服务器访问控制。用户可以修改自己的本地配置;限制可访问目标时,应启用版本支持的 Per-Client Firewall 并设置允许范围,或在服务端实现等效规则。操作前核对 客户端设置文档,并测试允许与拒绝两类请求。
本例不提供 IPv6 隧道出口。如果客户端仍有公网 IPv6,部分连接可能走原网络。对需要全流量接管的设备,应先验证 IPv4、IPv6 和 DNS 的实际路径;使用支持的客户端阻断隧道外流量,或改为完整的双栈部署。不要只是加上 ::/0 就认为 IPv6 已经可用。
客户端 DNS 填你实际能够访问的解析器。如果填内网 DNS,其地址也需要被路由和防火墙允许。只有域名打不开时,先区分“IP 本身不可达”和“DNS 查询失败”,不要一上来改 MTU。
/etc/wireguard 不只是手工版的一个配置文件,备份应保留整个挂载目录、Compose、代理配置和镜像版本。下面用停机归档保证文件状态一致,会断开所有隧道;务必使用不依赖这条 VPN 的 SSH 连接,并保留服务商控制台入口。
保存为 /opt/wg-easy/backup.sh:
#!/usr/bin/env bash
set -euo pipefail
umask 077
cd /opt/wg-easy
backup_file="backups/wg-easy-$(date -u +%Y%m%dT%H%M%SZ).tar.gz"
test ! -e "$backup_file"
test ! -e "${backup_file}.partial"
{
for service in wg-easy caddy; do
image_id=$(docker compose images -q "$service")
test -n "$image_id"
printf '%s\n' "$service"
docker image inspect "$image_id" --format '{{json .RepoDigests}}'
done
} > image-digests.txt
trap 'docker compose up -d' EXIT
docker compose down
tar -czf "${backup_file}.partial" \
compose.yaml Caddyfile image-digests.txt wireguard caddy-data caddy-config
tar -tzf "${backup_file}.partial" > /dev/null
mv "${backup_file}.partial" "$backup_file"
sha256sum "$backup_file" > "${backup_file}.sha256"
docker compose up -d
trap - EXIT
printf 'Backup ready: %s\n' "$backup_file"
chmod 700 /opt/wg-easy/backup.sh
/opt/wg-easy/backup.sh
docker compose ps
这里使用 down / up,不使用 down -v。脚本失败会尝试恢复服务,仍需检查容器状态与一次客户端握手。归档校验通过仅证明文件可读,不代表业务恢复验证完成。
备份包含私钥、管理数据和证书,应加密后存到异地,并单独保管解密凭据。每次新增、删除设备或修改路由后都应纳入备份计划。不要把已被撤销的旧客户端配置误当成当前状态恢复。
在干净的恢复 VPS 上安装同样的 Docker 环境,把可信归档及其 .sha256 文件放到 /root/wg-restore/backups/,保留原文件名。先校验并查看归档,再解压到新目录;下面的时间占位符需替换为实际文件名:
cd /root/wg-restore
backup_file='backups/wg-easy-YYYYMMDDTHHMMSSZ.tar.gz'
sha256sum -c "${backup_file}.sha256"
tar -tzf "$backup_file"
restore_dir=$(mktemp -d /opt/wg-easy-restore.XXXXXX)
tar -xzf "$backup_file" -C "$restore_dir"
cd "$restore_dir"
docker compose config --quiet
校验文件记录的是 backups/文件名,所以这里保留相同的相对目录结构。校验失败时先停止,检查传输和文件来源。
将 Compose 的两个镜像改为 image-digests.txt 记录的对应 digest,确认没有端口、网络冲突,再启动。正式接管时更新域名指向和面板来源允许地址;如果服务器 Endpoint 改了,还需同步客户端配置或重新连接,让域名重新解析。
docker compose up -d
docker compose logs --tail=100 wg-easy
docker compose exec wg-easy wg show
恢复演练应使用隔离测试 VPS、测试域名与临时客户端,先检查管理员登录、设备清单、握手和目标服务访问。不要让测试机与生产机同时承接同一批客户端。更完整的核验步骤可参考 VPS 备份恢复演练。
日常升级前做一次可恢复备份,记录旧 digest,再拉取新镜像、按维护窗口重建。若已固定 digest,需要显式换成核验过的新 digest。回退时使用旧镜像及升级前完整状态目录,不假设旧版本能直接读取升级后的数据库。
先检查 Caddy 的管理来源允许地址。网络切换或全隧道启用后,浏览器出口 IP 可能变化;通过备用 SSH 更新来源规则并重载 Caddy。不要把所有来源都放开来掩盖地址配置错误。
核对 Endpoint 域名、UDP 端口、云安全组和服务端日志。客户端网络可能限制 UDP;换一个自己可控的网络比较。TCP 端口检测结果不能证明 UDP 51820 可达。
按目标路由、IP 转发、NAT、服务监听地址和 DNS 顺序检查。WireGuard 接口在容器里,排查时执行 docker compose exec wg-easy ip route,不要只看宿主机是否存在 wg0。路径 MTU 有问题时再逐步调整并重新测试。
NAT 后面的客户端确有需要时,可考虑 25 秒的 Persistent Keepalive,参考 WireGuard 官方 Quick Start。它会增加周期性流量,不需要给所有设备统一开启。
不会因此自动撤销 WireGuard peer。设备访问使用各自密钥;需要撤销设备时,在面板禁用或删除相应客户端,并验证它不能继续访问。
先让一台设备完成“生成配置—握手—访问目标—撤销—恢复测试”,再批量添加其他设备。这样能尽早发现入口、路由和备份中的问题。
