直接在宿主机上安装 WireGuard 需要处理内核模块、工具链和系统服务,多人共用一台机器时还容易互相影响。把 WireGuard 放进容器里跑,相当于把依赖和配置打包成独立单元,既能快速启停,也能在不同主机之间迁移。不过容器网络默认隔离,要让 WireGuard 在容器里正常转发流量,必须理解端口映射与网络模式的关系。下面这张图展示了典型的部署架构,宿主机只做端口转发,真正的加密隧道进程完全运行在容器内部。

先准备一台安装了 Docker 的 Linux 主机,内核版本建议 5.6 以上,因为较新的内核已经内置了 WireGuard 模块,容器里不需要再额外加载。如果宿主机内核较老,则需要先升级内核或安装 wireguard-dkms 包。容器镜像可以选择官方维护的 linuxserver/wireguard,它基于 Alpine,体积小、配置简单,还内置了自动生成密钥和客户端配置的脚本。另一个常见选择是 weejewel/wg-easy,它带有一个简单的 Web 管理界面,适合不想手写配置文件的用户。本文以 linuxserver/wireguard 为例,因为它更贴近原生 WireGuard 的使用方式。
启动容器前,先创建一个目录用来存放配置和密钥,比如 /opt/wireguard。然后执行下面的命令拉取镜像并运行容器。注意 --cap-add=NET_ADMIN 和 --sysctl=net.ipv4.conf.all.src_valid_mark=1 这两项不能少,否则容器内无法创建 WireGuard 接口或设置路由。
mkdir -p /opt/wireguard docker run -d \ --name=wireguard \ --cap-add=NET_ADMIN \ --sysctl="net.ipv4.conf.all.src_valid_mark=1" \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Asia/Shanghai \ -e SERVERURL=your.domain.com \ -e SERVERPORT=51820 \ -e PEERS=3 \ -e PEERDNS=auto \ -p 51820:51820/udp \ -v /opt/wireguard:/config \ --restart unless-stopped \ linuxserver/wireguard
上面的参数里,SERVERURL 填写客户端连接时使用的公网域名或 IP,PEERS=3 表示自动生成 3 个客户端配置文件。容器第一次启动时会自动生成服务器私钥、公钥以及每个客户端的密钥对,并输出到宿主机挂载的 /opt/wireguard 目录中。启动完成后,用 docker logs wireguard 可以看到每个客户端的配置内容,甚至可以直接把二维码打印出来,方便手机扫码添加。
理解容器网络与端口映射
很多人在容器里跑 WireGuard 后,客户端能连上但无法访问外部网络,或者能访问外部但无法 ping 通容器内的其他服务。这通常与容器的网络模式有关。默认的 bridge 模式下,WireGuard 的 UDP 监听端口需要通过 -p 51820:51820/udp 映射到宿主机,数据包到达容器后,WireGuard 接口会对流量进行解密和重新路由。如果希望容器直接使用宿主机的网络栈,可以改用 --network=host,这样就不用做端口映射,但会牺牲一定的隔离性。
使用 bridge 模式时,还需要注意容器内的路由表。WireGuard 客户端连接后,其流量会通过隧道到达容器内的 wg0 接口。为了让这些流量能够访问容器所在网段或外部互联网,必须确保容器开启了 IP 转发,并且设置了正确的 NAT 规则。linuxserver 镜像默认会在启动脚本里处理好这些,但如果你自己用基础镜像构建,就需要手动执行 sysctl -w net.ipv4.ip_forward=1 并添加 iptables MASQUERADE 规则。
另一个容易忽略的点是 MTU。WireGuard 默认 MTU 是 1420,如果底层网络路径中某个环节的 MTU 更小,就会出现大包丢失、连接卡顿的现象。容器网络通常会在虚拟网桥上增加一些开销,建议在客户端配置里把 MTU 改成 1380 或者更低。可以在生成的客户端配置文件里修改 MTU = 1380,或者通过环境变量 -e MTU=1380 让镜像自动生成时写入。
配置持久化与多客户端管理
把 /config 目录挂载到宿主机是容器化部署的关键,这样即使容器被删除重建,密钥和配置仍然保留。该目录下会有 server 和 peer1、peer2 等子目录,每个客户端目录里都包含私钥、公钥和预生成的配置文件。对于管理员来说,可以直接查看 peer1/peer1.conf 获取客户端所需内容。如果后续需要增加新的客户端,只需修改 PEERS 环境变量并重启容器,或者直接向容器发送信号让它重新生成。
如果不想每次新增客户端都修改环境变量,也可以手动编辑服务器配置 wg0.conf,添加新的 [Peer] 段。修改后需要执行 docker exec wireguard wg syncconf wg0 <(wg-quick strip wg0) 来让配置生效,而不用重启整个容器。这种方式更灵活,但要求管理员熟悉 WireGuard 的配置文件语法。对于团队环境,建议配合版本管理工具(如 Git)来跟踪 /opt/wireguard 目录的变化,避免误删密钥造成隧道中断。
多客户端管理时还要注意流量隔离。默认情况下,所有客户端都在同一个虚拟子网内,互相可以访问。如果希望客户端之间不能互访,可以在服务器配置里为每个客户端设置不同的 AllowedIPs,并配合防火墙规则限制转发。容器化环境里,这些规则都写在 wg0.conf 的 [Interface] 段中,可以使用 PostUp 和 PostDown 指令动态添加和删除 iptables 规则。
常见问题排查与优化建议
部署完成后,如果客户端无法建立握手,首先要确认 UDP 51820 端口在宿主机防火墙中是否放行。可以执行 docker logs wireguard 查看是否有握手请求进入。如果日志里根本没有数据包到达,多半是云服务商的安全组或宿主机防火墙拦截了 UDP。此外,检查客户端配置里的 Endpoint 是否填写正确,最好使用域名而不是 IP,因为家庭宽带的公网 IP 可能经常变化。
握手成功但无法 ping 通服务器或访问外网时,可以进入容器内部执行 docker exec -it wireguard bash,然后运行 wg show 查看接口状态和最近握手时间。如果接口存在但数据包没有转发,检查 iptables -t nat -L POSTROUTING -v 中是否有针对 wg0 的 MASQUERADE 规则。linuxserver 镜像一般会自动配置,但如果容器被非正常终止,iptables 规则可能残留或丢失,建议在宿主机上写一个清理脚本定期检查。
性能方面,容器化部署带来的额外开销非常小,因为 WireGuard 加密解密主要在内核态完成。如果担心容器网络桥接的 CPU 消耗,可以使用 --network=host 模式,让容器直接使用宿主机网络栈,吞吐量几乎无损失。另外,对于需要长期稳定运行的场景,建议在 docker run 命令中加入 --log-driver=json-file --log-opt max-size=10m 限制日志大小,并配置监控脚本检测容器健康状态。总体来看,WireGuard 容器化部署既保留了原生性能,又获得了极高的可维护性,是个人或小团队搭建私有 VPN 的优秀方案。

