938 字
5 分钟
OpenVPN Docker 启动崩溃:Cannot open TUN/TAP dev /dev/net/tun

部署基于 Web 管理界面的 OpenVPN 容器时,容器会不断重启,Web 控制台也返回错误。本文记录完整的排查与解决过程。

现象描述#

主要有两个连带问题。

Web 控制台异常:尝试通过 Web 控制台重启 OpenVPN 服务时返回 HTTP 500,后台日志显示:

[error] dial tcp 127.0.0.1:7505: connect: connection refused

容器后台持续崩溃退出:查看 OpenVPN 容器详细日志(docker logs <container_name>),核心报错如下:

ERROR: Cannot open TUN/TAP dev /dev/net/tun: No such device (errno=19)
Exiting due to fatal error
WARN exited: openvpn (exit status 1; not expected)
INFO gave up: openvpn entered FATAL state, too many start retries too quickly

根因剖析#

Web 端报 500 的原因:OpenVPN 的 Web 管理端依赖配置中的 management 127.0.0.1 7505 端口,与 OpenVPN 守护进程进行 IPC 通信。由于 OpenVPN 进程启动失败退出,Web 端连接 127.0.0.1:7505 被拒绝,进而抛出 500。所以 500 只是表象,根源在 OpenVPN 本身没起来。

errno=19 (No such device) 的原因:OpenVPN 依赖 Linux 内核的 TUN/TAP 虚拟网络驱动来创建虚拟网卡(tun0)。虽然在 Docker 容器内部能看到 /dev/net/tun 这个字符设备节点文件(主次设备号为 10, 200),但宿主机底层的 Linux 内核根本没有加载 tun 内核模块。设备文件空有其表,内核驱动缺失,系统调用无法映射,因此报错 errno=19。

诊断步骤#

排查设备节点与容器权限#

先确认容器本身已获得必要的设备挂载和系统网络管理能力:

Terminal window
# 检查容器内是否存在 tun 节点
docker exec -it <container_name> ls -l /dev/net/tun

如果输出 crw-rw-rw- 1 root root 10, 200 ... /dev/net/tun,说明容器映射本身没问题,问题出在宿主机内核。

测试宿主机内核 TUN 驱动状态#

直接在宿主机(物理机/虚拟机)终端中读取 TUN 驱动状态:

Terminal window
cat /dev/net/tun
  • 正常返回:cat: /dev/net/tun: File descriptor in bad state,说明驱动正常加载并在工作,只是无法以常规文件方式读取。
  • 异常返回:cat: /dev/net/tun: No such device,说明宿主机内核缺失 tun 模块。

解决方案#

第一步:宿主机加载并持久化 TUN 模块#

手动即时加载模块:

Terminal window
modprobe tun

验证驱动工作状态:

Terminal window
# 查看已加载模块列表
lsmod | grep tun
# 再次测试驱动读取
cat /dev/net/tun
# 返回: cat: /dev/net/tun: File descriptor in bad state 即表示完全正常

设置开机持久化自动加载,防止服务器重启后模块再次丢失:

Terminal window
# 写入 systemd modules-load 配置
echo "tun" | tee /etc/modules-load.d/tun.conf
# 兼容旧版发行版(可选)
echo "tun" >> /etc/modules
# 确保 systemd 模块加载服务开机启动
systemctl enable systemd-modules-load

第二步:规范化 Docker Compose 配置#

设备挂载与网络特权必须在容器创建时绑定。修改 docker-compose.yml:

version: '3.8'
services:
openvpn:
image: yyxx/openvpn
cap_add:
- NET_ADMIN
restart: always
ports:
- "11194:1194/udp"
- "8833:8833"
volumes:
- ./data:/data
- /etc/localtime:/etc/localtime:ro
sysctls:
- net.ipv6.conf.default.disable_ipv6=0
- net.ipv6.conf.all.forwarding=1
networks:
default:
enable_ipv6: true

注意:修改 devices、cap_add 或 privileged 等底层属性后,单纯执行 docker compose restart 不会生效,必须重建容器:

Terminal window
docker compose down
docker compose up -d

第三步:验证结果#

查看容器日志:

Terminal window
docker logs --tail 30 openvpn

看到末尾输出 Initialization Sequence Completed 即表示成功。此时 OpenVPN 核心进程保持存活,Web 管理界面的控制、重启功能以及客户端握手都恢复正常。

补充场景:PVE / LXC 虚拟化环境#

如果 Docker 宿主机运行在 Proxmox VE (PVE) 的非特权 LXC 容器内,默认权限会被 PVE 母机拦截。需要在 PVE 母机宿主机的配置文件(/etc/pve/lxc/<CTID>.conf)中追加设备直通:

lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file

并在 PVE 母机上执行 modprobe tun 后重启该 LXC 容器。

相关文章#

OpenVPN Docker 启动崩溃:Cannot open TUN/TAP dev /dev/net/tun
https://iiii.fun/posts/docker/openvpn-tun-error-fix/
作者
庆灵
发布于
2026-10-04
许可协议
CC BY-NC-SA 4.0