本文是系列教程的后篇,记录一套适用于 Debian 13.x 最小系统的 AimiliVPN + Docker 3x-ui 部署方案。
主要目标是让 Docker 中的 3x-ui 通过 AimiliVPN 提供的本地 HTTP 代理实现指定网站分流,同时使用独立的 Docker 防火墙规则控制节点端口。
DD 重装后的 Debian VPS、Docker 3x-ui、AimiliVPN、指定域名分流,以及需要长期稳定运行的节点环境。
本文不包含任何具体服务器的公网 IP、SSH 端口、域名、后台地址或其他可识别服务器的信息。
发布时请根据自己的环境替换相关参数。
前置步骤:安装基础环境
DD 重装后的 Debian 最小系统通常只包含最基本的软件包。正式部署之前,需要安装 Docker、Docker Compose、Git、curl、Python 3 以及防火墙持久化工具。
0.1 更新系统
apt update
apt full-upgrade -y
0.2 安装基础工具
apt install -y \
ca-certificates \
curl \
git \
python3 \
iptables \
iptables-persistent \
netfilter-persistent
如果安装过程中出现是否保存当前 IPv4/IPv6 防火墙规则的提示,可以选择 Yes。
这里安装
iptables-persistent 和 netfilter-persistent,主要用于保存宿主机基础防火墙规则。Docker 自己创建的 Docker 专用链则由后面的 systemd 服务重新应用。
0.3 安装 Docker Engine + Docker Compose
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg \
-o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
> /etc/apt/sources.list.d/docker.list
apt update
apt install -y \
docker-ce \
docker-ce-cli \
containerd.io \
docker-buildx-plugin \
docker-compose-plugin
systemctl enable --now docker
0.4 检查基础环境
docker --version
docker compose version
git --version
python3 --version
curl --version
iptables --version
检查 Docker 服务:
systemctl status docker --no-pager
Active: active (running),说明 Docker 已经正常运行。
第 1 步:创建 Docker 网络
本教程统一创建一个名为 web 的 Docker Bridge 网络。
docker network create \
--driver bridge \
--subnet 172.20.0.0/16 \
--gateway 172.20.0.1 \
web
检查网络:
docker network inspect web
| 项目 | 配置 |
|---|---|
| 网络名称 | web |
| Subnet | 172.20.0.0/16 |
| Gateway | 172.20.0.1 |
容器 IP 规划:
| 容器 | IP |
|---|---|
| mysql57 | 172.20.0.10 |
| npm | 172.20.0.20 |
| wordpress | 172.20.0.30 |
| 3x-ui | 172.20.0.50 |
第 2 步:部署 AimiliVPN
AimiliVPN 项目目录统一放在:
/opt/aimilivpn
mkdir -p /opt
cd /opt
git clone https://github.com/xiaoxinkeji/aimili-vpngate.git aimilivpn
cd /opt/aimilivpn
2.1 修改 Dockerfile
sed -i 's/COPY proxy_server.py/COPY metrics.py proxy_server.py/' Dockerfile
grep "COPY metrics.py" Dockerfile
应该看到:
COPY metrics.py proxy_server.py
2.2 创建 AimiliVPN 数据目录
mkdir -p /opt/aimilivpn/vpngate_data
AimiliVPN 数据通过宿主机目录挂载到容器中。即使以后删除并重新创建容器,数据仍然保存在宿主机上。
2.3 创建 docker-compose.yml
cd /opt/aimilivpn
nano docker-compose.yml
services:
aimilivpn:
build:
context: .
args:
IMAGE_VERSION: dev
BUILD_DATE: unknown
GIT_COMMIT: unknown
image: ghcr.io/xiaoxinkeji/aimili-vpngate:latest
container_name: aimilivpn
restart: unless-stopped
network_mode: host
cap_add:
- NET_ADMIN
- NET_RAW
devices:
- /dev/net/tun:/dev/net/tun
volumes:
- /opt/aimilivpn/vpngate_data:/opt/aimilivpn/vpngate_data
environment:
- VPNGATE_DATA_DIR=/opt/aimilivpn/vpngate_data
- LOCAL_PROXY_HOST=172.20.0.1
- LOCAL_PROXY_PORT=7928
- UI_HOST=172.20.0.1
- UI_PORT=8787
- METRICS_ENABLED=true
- METRICS_PORT=9798
- METRICS_REFRESH_INTERVAL=15
2.4 构建并启动 AimiliVPN
cd /opt/aimilivpn
docker compose build --no-cache aimilivpn
docker compose up -d aimilivpn
docker ps
确认 aimilivpn 容器状态为 Up。
第 3 步:配置系统网络参数
nano /etc/sysctl.d/99-aimilivpn.conf
net.ipv4.ip_forward=1
net.ipv4.conf.all.rp_filter=2
net.ipv4.conf.default.rp_filter=2
sysctl --system
检查:
sysctl net.ipv4.ip_forward
sysctl net.ipv4.conf.all.rp_filter
正常结果应包含:
net.ipv4.ip_forward = 1
net.ipv4.conf.all.rp_filter = 2
第 4 步:配置 Docker daemon
不要直接使用
cat > /etc/docker/daemon.json 覆盖整个文件。如果系统中已经存在其他 Docker 配置,这样操作会把原有配置覆盖掉。
使用 Python 对现有 JSON 配置进行合并,只修改需要的项目。
mkdir -p /etc/docker
python3 - <<'PY'
import json
from pathlib import Path
p = Path("/etc/docker/daemon.json")
if p.exists():
try:
data = json.loads(p.read_text())
if not isinstance(data, dict):
data = {}
except Exception:
data = {}
else:
data = {}
data["ip-forward-no-drop"] = True
p.write_text(
json.dumps(data, indent=2, ensure_ascii=False) + "\n"
)
PY
检查:
cat /etc/docker/daemon.json
确认包含:
"ip-forward-no-drop": true
为什么使用 ip-forward-no-drop?
Docker 在启用 IPv4 forwarding 时可能调整 Linux 的 FORWARD 默认策略。
设置:
"ip-forward-no-drop": true
表示让 Docker 不要自动修改 FORWARD 默认策略。
本教程并不会因此将 FORWARD 设置成 ACCEPT。相反,后面的自定义防火墙脚本会明确设置:
iptables -P FORWARD DROP
↓
不自动管理 FORWARD 默认策略
↓
自定义防火墙脚本
↓
FORWARD = DROP
第 5 步:确保 AimiliVPN 随 Docker 自动启动
AimiliVPN 使用 Docker Compose 管理,并通过 restart: unless-stopped 让容器在 Docker 启动后自动恢复。
首先确保 Docker 设置为开机启动:
systemctl enable docker
确认 AimiliVPN 使用自动重启策略:
cd /opt/aimilivpn
docker compose up -d aimilivpn
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' aimilivpn
正常应该看到:
unless-stopped
VPS 重启后,systemd 先启动 Docker;Docker 启动完成后,会根据
restart: unless-stopped 自动恢复 AimiliVPN 容器。后面的
docker-firewall.service 再负责重新应用 Docker 专用防火墙规则。
第 6 步:配置 Docker → AimiliVPN 防火墙
AimiliVPN 使用:
network_mode: host
因此 HTTP 代理监听在宿主机:
172.20.0.1:7928
3x-ui 位于:
172.20.0.50
172.20.0.50
│
▼
172.20.0.1:7928
│
▼
AimiliVPN
│
▼
VPN 出口
6.1 创建 Docker 防火墙脚本
nano /usr/local/sbin/docker-firewall.sh
#!/bin/bash
set -e
IPTABLES="/usr/sbin/iptables"
# ============================================================
# 1. 明确设置 FORWARD 默认策略
# ============================================================
$IPTABLES -P FORWARD DROP
# ============================================================
# 2. 允许 Docker 网络访问 AimiliVPN HTTP 代理
# ============================================================
$IPTABLES -C INPUT \
-s 172.20.0.0/16 \
-d 172.20.0.1 \
-p tcp \
--dport 7928 \
-j ACCEPT 2>/dev/null || \
$IPTABLES -I INPUT 1 \
-s 172.20.0.0/16 \
-d 172.20.0.1 \
-p tcp \
--dport 7928 \
-j ACCEPT
# ============================================================
# 3. 允许 Docker 网络访问 AimiliVPN 管理页面
# ============================================================
$IPTABLES -C INPUT \
-s 172.20.0.0/16 \
-d 172.20.0.1 \
-p tcp \
--dport 8787 \
-j ACCEPT 2>/dev/null || \
$IPTABLES -I INPUT 1 \
-s 172.20.0.0/16 \
-d 172.20.0.1 \
-p tcp \
--dport 8787 \
-j ACCEPT
# ============================================================
# 4. 创建独立的 3x-ui 防火墙链
# ============================================================
$IPTABLES -N AIMILI-3XUI 2>/dev/null || true
$IPTABLES -F AIMILI-3XUI
# ============================================================
# 5. 已建立连接直接放行
# ============================================================
$IPTABLES -A AIMILI-3XUI \
-m conntrack \
--ctstate ESTABLISHED,RELATED \
-j ACCEPT
# ============================================================
# 6. 当前开放节点
# ============================================================
$IPTABLES -A AIMILI-3XUI \
-d 172.20.0.50 \
-p tcp \
--dport 12701 \
-j ACCEPT
$IPTABLES -A AIMILI-3XUI \
-d 172.20.0.50 \
-p udp \
--dport 12703 \
-j ACCEPT
# ============================================================
# 7. 其他预留端口拒绝
# ============================================================
for PORT in 12700 12702 12704 12705 12706 12707 12708 12709 12710
do
$IPTABLES -A AIMILI-3XUI \
-d 172.20.0.50 \
-p tcp \
--dport "$PORT" \
-j DROP
done
# ============================================================
# 8. 删除旧的跳转
# ============================================================
while $IPTABLES -C DOCKER-USER \
-j AIMILI-3XUI 2>/dev/null
do
$IPTABLES -D DOCKER-USER \
-j AIMILI-3XUI
done
# ============================================================
# 9. 将独立链加入 DOCKER-USER
# ============================================================
$IPTABLES -I DOCKER-USER 1 \
-j AIMILI-3XUI
赋予执行权限:
chmod +x /usr/local/sbin/docker-firewall.sh
第 7 步:创建 Docker 防火墙开机服务
Docker 启动时会创建和重建 Docker 自己的防火墙链,因此 Docker 专用规则应该在 Docker 启动完成后重新应用。
↓
Docker 启动
↓
Docker 创建 DOCKER-USER 等链
↓
docker-firewall.service
↓
重新应用自定义规则
创建 systemd 服务:
nano /etc/systemd/system/docker-firewall.service
写入:
[Unit]
Description=Apply custom Docker firewall rules after Docker starts
Requires=docker.service
After=docker.service
PartOf=docker.service
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/docker-firewall.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
这是更标准的 systemd 启用方式。
WantedBy=multi-user.target 负责开机启动,After=docker.service 负责确保 Docker 在该服务之前启动。
重新加载 systemd:
systemctl daemon-reload
设置开机启动:
systemctl enable docker-firewall.service
立即执行:
systemctl start docker-firewall.service
检查:
systemctl status docker-firewall.service --no-pager
正常情况下:
Active: active (exited)
第 8 步:保存系统防火墙规则
本教程将防火墙规则分为两部分管理:
| 规则类型 | 负责组件 |
|---|---|
| 宿主机基础防火墙规则 | netfilter-persistent |
| Docker 动态防火墙链 | docker-firewall.service |
Docker 启动后会创建
DOCKER-USER 等 Docker 专用链,因此 Docker 专用规则不依赖netfilter-persistent 在 Docker 启动之前恢复,而是由后面的docker-firewall.service 在 Docker 启动完成后重新应用。
检查 INPUT:
iptables -L INPUT -n -v --line-numbers
检查 FORWARD:
iptables -L FORWARD -n -v --line-numbers
重点确认:
Chain FORWARD (policy DROP)
保存宿主机基础防火墙规则:
netfilter-persistent save
netfilter-persistent 主要负责宿主机静态防火墙规则。DOCKER-USER、AIMILI-3XUI 等 Docker 专用规则由docker-firewall.service 在 Docker 启动后重新应用。
检查持久化规则文件:
iptables-restore --test < /etc/iptables/rules.v4
echo $?
正常返回:
0
第 9 步:配置 3x-ui 分流
3x-ui 使用固定 Docker IP:
172.20.0.50
Docker 端口映射可以保留:
12700-12710/TCP
12703/UDP
以后增加 3x-ui 节点时,不需要重新创建容器。只需要调整防火墙规则即可。
9.1 当前开放节点
| 协议 | 端口 | 状态 |
|---|---|---|
| TCP | 12701 |
允许公网访问 |
| UDP | 12703 |
允许公网访问 |
9.2 预留节点端口
以下端口继续保留 Docker 映射,但由防火墙拒绝公网访问:
12700
12702
12704
12705
12706
12707
12708
12709
12710
以后如果新增节点,例如使用 12705/TCP,只需要修改 AIMILI-3XUI 中对应端口的防火墙规则,无需重新创建 Docker 容器。
9.3 AimiliVPN HTTP 代理
3x-ui 使用:
http://172.20.0.1:7928
│
├── 指定分流域名
│ │
│ ▼
│ AIMILI-VPN
│ │
│ ▼
│ 172.20.0.1:7928
│ │
│ ▼
│ AimiliVPN
│ │
│ ▼
│ VPN 出口
│
└── 其他网站
│
▼
direct
9.4 推荐的 OpenAI / ChatGPT 分流域名
openai.com
chatgpt.com
oaistatic.com
oaiusercontent.com
ai.com
上述域名设置为走 AIMILI-VPN。
其他流量继续使用 direct。
如果已经在 3x-ui 中完成 routing 配置,则不需要重复修改。保持现有分流规则即可。
第 10 步:最终验证、持久化与重启测试
10.1 验证 AimiliVPN HTTP 代理
curl -4 \
--proxy http://172.20.0.1:7928 \
https://api.ipify.org
如果能够返回 VPN 出口 IP,说明 AimiliVPN HTTP 代理正常。
10.2 验证 3x-ui → AimiliVPN
docker exec 3xui sh -c \
'curl -4 --connect-timeout 10 \
--proxy http://172.20.0.1:7928 \
https://api.ipify.org'
如果能够正常返回 VPN 出口 IP,说明以下链路已经打通:
↓
Docker Gateway
↓
172.20.0.1:7928
↓
AimiliVPN
↓
VPN 出口
10.3 验证 NPM → AimiliVPN 管理页面
如果使用 NPM 反向代理 AimiliVPN 管理页面,可以从 NPM 容器内部测试。
将下面的 你的面板路径 替换成自己设置的路径:
docker exec npm sh -c \
'curl -4 --connect-timeout 10 \
http://172.20.0.1:8787/你的面板路径/ \
-o /dev/null \
-w "%{http_code}\n"'
正常情况下返回:
200
docker exec npm sh -c \
'curl -4 --connect-timeout 10 \
http://172.20.0.1:8787/ \
-o /dev/null \
-w "%{http_code}\n"'
10.4 验证 Docker 防火墙
iptables -L DOCKER-USER -n -v --line-numbers
应该能够看到:
AIMILI-3XUI
检查独立链:
iptables -L AIMILI-3XUI -n -v --line-numbers
应该包含:
12701/TCP ACCEPT12703/UDP ACCEPT- 其他预留 TCP 端口 DROP
10.5 验证 FORWARD 策略
iptables -L FORWARD -n -v
确认:
Chain FORWARD (policy DROP)
10.6 验证防火墙服务
systemctl status docker-firewall.service --no-pager
正常应该为:
Active: active (exited)
10.7 再次保存宿主机防火墙规则
netfilter-persistent save
iptables-restore --test < /etc/iptables/rules.v4
echo $?
返回 0 即表示规则文件语法正常。
Docker 专用规则不依赖这里的恢复过程。
VPS 重启后,Docker 启动并创建 Docker 专用链,再由
docker-firewall.service 自动重新应用 AIMILI-3XUI 等规则。
10.8 重启 VPS
只有前面的测试全部正常后,再执行重启。
reboot
10.9 重启后检查 Docker
docker ps
确认需要的容器均正常运行。
10.10 重启后检查防火墙
iptables -L FORWARD -n -v
确认:
policy DROP
继续检查:
iptables -L DOCKER-USER -n -v --line-numbers
确认:
AIMILI-3XUI
10.11 重启后重新测试 3x-ui → AimiliVPN
docker exec 3xui sh -c \
'curl -4 --connect-timeout 10 \
--proxy http://172.20.0.1:7928 \
https://api.ipify.org'
最终架构
│
┌─────────┴─────────┐
│ │
TCP 节点 UDP 节点
│ │
└─────────┬─────────┘
│
3x-ui
172.20.0.50
│
┌───────────┴───────────┐
│ │
指定分流域名 其他流量
│ │
▼ ▼
AIMILI-VPN direct
│
▼
172.20.0.1:7928
│
▼
AimiliVPN
│
▼
VPN 节点
│
▼
Internet
最终配置总结
| 项目 | 配置 |
|---|---|
| 系统 | Debian 13.x |
| Docker 网络 | web |
| Docker 子网 | 172.20.0.0/16 |
| Docker Gateway | 172.20.0.1 |
| 3x-ui | 172.20.0.50 |
| AimiliVPN | network_mode: host |
| HTTP 代理 | 172.20.0.1:7928 |
| 管理服务 | 172.20.0.1:8787 |
| AimiliVPN 数据 | /opt/aimilivpn/vpngate_data |
| Docker daemon | ip-forward-no-drop: true |
| FORWARD 默认策略 | DROP |
| Docker 自定义防火墙 | AIMILI-3XUI |
| Docker 防火墙入口 | DOCKER-USER |
| 防火墙持久化 | netfilter-persistent |
| Docker 防火墙启动 | systemd 开机自动执行 |
| WARP | 不使用 |
| OpenAI / ChatGPT 分流 | AIMILI-VPN |
| 其他流量 | direct |
常见问题
为什么 Docker 容器访问不了 172.20.0.1:7928?
因为 AimiliVPN 使用 host 网络模式,172.20.0.1:7928 实际属于宿主机监听端口。如果宿主机 INPUT 默认策略为 DROP,Docker 容器访问宿主机端口也可能被 INPUT 防火墙拦截。
因此本教程允许:
172.20.0.0/16 → 172.20.0.1:7928
为什么不直接把 FORWARD 改成 ACCEPT?
因为这样会降低主机默认的网络转发限制。本教程采用:
FORWARD = DROP
再通过 Docker 自身规则以及自定义规则放行合法流量。
为什么不关闭 Docker 的 iptables 管理?
不建议简单使用:
"iptables": false
Docker 的容器网络、端口映射和 NAT 依赖其防火墙规则。关闭 Docker 的 iptables 管理可能导致容器网络异常。
本教程采取:
+
通过 DOCKER-USER 管理自定义访问控制
+
FORWARD 默认策略明确设置为 DROP
以后增加新的 3x-ui 节点怎么办?
例如新增:
12705/TCP
无需重新创建 3x-ui 容器。只需要调整 AIMILI-3XUI 中对应端口的防火墙规则,将该端口从 DROP 改为 ACCEPT。
结语
这套方案的核心并不是简单地把 AimiliVPN 和 3x-ui 安装在一起,而是分别处理容器网络、宿主机网络、Docker 防火墙、AimiliVPN HTTP 代理以及 3x-ui 分流。
最终实现:
- Docker 使用独立 Bridge 网络;
- AimiliVPN 使用 host 网络运行;
- 3x-ui 通过 Docker Gateway 访问 AimiliVPN;
- 指定网站通过 AimiliVPN 出口;
- 其他流量保持 direct;
- FORWARD 默认策略保持 DROP;
- Docker 公网端口通过独立防火墙链控制;
- 宿主机基础防火墙规则支持持久化;
- Docker 启动后自动恢复 Docker 专用防火墙规则;
- AimiliVPN 数据持久化保存;
- 重启 VPS 后整套环境仍可自动恢复。
3x-ui 可以通过
172.20.0.1:7928 获得 VPN 出口,同时 FORWARD 仍保持 DROP,即表示本教程部署成功。
本文中的公网 IP、SSH 端口、域名、管理地址等均未提供,发布时请根据自己的实际环境配置。

评论(0)
暂无评论