网上关于 Remnawave 的教程不多,官方文档看着也累。折腾了一套 Docker Compose 方案,顺手优化了一下:把不必要的端口映射全去掉了,改走 Cloudflare Tunnel 内网穿透,安全性比直连高不少。
如果你是单机,还是建议用 X-UI;如果手里机器多,或者想玩 XHTTP/Reality 上下行分离,这个面板确实顺手。
1. 准备工作
- 面板机:Debian/Ubuntu,内存 2G+(主要是 Java 和 PG 数据库吃点内存)。
- 节点机:随便啥能装 Docker 的机器。
- 域名:托管在 Cloudflare。
2. 面板端部署
直接用 Docker 梭哈,不用配 Nginx。
初始化环境 & 生成密钥
连上 SSH,一把梭:
# 装 Docker
curl -fsSL https://get.docker.com | sh
# 准备目录和环境变量
mkdir -p /opt/remnawave && cd /opt/remnawave
curl -o .env https://raw.githubusercontent.com/remnawave/backend/refs/heads/main/.env.sample
# 生成一堆随机密钥填进 .env,省得手动改
sed -i "s/^JWT_AUTH_SECRET=.*/JWT_AUTH_SECRET=$(openssl rand -hex 64)/" .env
sed -i "s/^JWT_API_TOKENS_SECRET=.*/JWT_API_TOKENS_SECRET=$(openssl rand -hex 64)/" .env
sed -i "s/^METRICS_PASS=.*/METRICS_PASS=$(openssl rand -hex 64)/" .env
sed -i "s/^WEBHOOK_SECRET_HEADER=.*/WEBHOOK_SECRET_HEADER=$(openssl rand -hex 64)/" .env
# 设置数据库密码
pw=$(openssl rand -hex 24)
sed -i "s/^POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=$pw/" .env
sed -i "s|^\(DATABASE_URL=\"postgresql://postgres:\)[^\@]*\(@.*\)|\1$pw\2|" .env
配置文件 (docker-compose.yml)
在 /opt/remnawave 下新建文件。 注意:为了安全,这里数据库端口只开了本地 127.0.0.1,面板端口也没映射,全靠最后那个 Tunnel 容器往外透。
services:
remnawave-db:
image: postgres:17.6
restart: always
env_file: .env
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
- TZ=UTC
ports:
- '127.0.0.1:6767:5432'
volumes:
- remnawave-db-data:/var/lib/postgresql/data
networks:
- remnawave-network
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}']
interval: 3s
timeout: 10s
retries: 3
remnawave-redis:
image: valkey/valkey:8.1-alpine
restart: always
networks:
- remnawave-network
volumes:
- remnawave-redis-data:/data
healthcheck:
test: ['CMD', 'valkey-cli', 'ping']
interval: 3s
timeout: 10s
retries: 3
remnawave:
image: remnawave/backend:2
container_name: 'remnawave'
restart: always
env_file: .env
networks:
- remnawave-network
depends_on:
remnawave-db:
condition: service_healthy
remnawave-redis:
condition: service_healthy
healthcheck:
test: ['CMD-SHELL', 'curl -f http://localhost:${METRICS_PORT:-3001}/health']
interval: 30s
timeout: 5s
retries: 3
remnawave-subscription-page:
image: remnawave/subscription-page:latest
restart: always
environment:
- REMNAWAVE_PANEL_URL=http://remnawave:3000
- META_TITLE=Subscription
- SUBSCRIPTION_UI_DISPLAY_RAW_KEYS=true
networks:
- remnawave-network
depends_on:
- remnawave
# Cloudflare Tunnel
remnawave-cloudflared:
image: cloudflare/cloudflared:latest
env_file: .env
networks:
- remnawave-network
restart: always
command: tunnel --no-autoupdate run --token ${CLOUDFLARE_TOKEN}
depends_on:
- remnawave
- remnawave-subscription-page
networks:
remnawave-network:
enable_ipv6: true
volumes:
remnawave-db-data:
driver: local
remnawave-redis-data:
driver: local
配置 CF Tunnel & 环境变量
- 去 Cloudflare 后台 -> Zero Trust -> Tunnels -> Create a tunnel。
- 拿到那个以
ey开头的 Token。 -
配置 Public Hostnames(把域名指过来):
- 面板域名 ->
http://remnawave:3000 - 订阅域名 ->
http://remnawave-subscription-page:3010
- 面板域名 ->
- 找
@BotFather要个 TG Bot Token,找@userinfobot拿你自己的 ID。
编辑 .env 文件,把最后这几项补齐:
Bash
IS_TELEGRAM_NOTIFICATIONS_ENABLED=true
TELEGRAM_BOT_TOKEN=你的BotToken
TELEGRAM_NOTIFY_USERS_CHAT_ID=你的TG_ID
TELEGRAM_NOTIFY_NODES_CHAT_ID=你的TG_ID
TELEGRAM_NOTIFY_CRM_CHAT_ID=你的TG_ID
CLOUDFLARE_TOKEN=你的CF_Tunnel_Token
SUB_PUBLIC_DOMAIN=你的订阅域名
FRONT_END_DOMAIN=你的面板域名
启动: docker compose up -d
3. 核心配置:Xray 模板
Remnawave 的核心就是这个配置模板。下面这个是我自己在用的,含 VLESS-Reality 和 XHTTP,直接复制进面板的“配置文件”里就行。
记得把 privateKey 换成你自己生成的。
JSON
{
"log": { "loglevel": "error" },
"dns": { "servers": ["1.1.1.1"], "queryStrategy": "UseIP" },
"inbounds": [
{
"tag": "SS-IN",
"port": 1145,
"protocol": "shadowsocks",
"settings": { "clients": [], "network": "tcp,udp" }
},
{
"tag": "REALITY-TCP",
"port": 8443,
"listen": "0.0.0.0",
"protocol": "vless",
"settings": { "clients": [], "decryption": "none" },
"sniffing": { "enabled": true, "destOverride": ["http", "tls", "quic"] },
"streamSettings": {
"network": "raw",
"security": "reality",
"realitySettings": {
"target": "osxapps.itunes.apple.com:443",
"shortIds": ["b2c86d5449d237fa"],
"privateKey": "替换这里!!!",
"serverNames": ["osxapps.itunes.apple.com"]
}
}
},
{
"tag": "REALITY-XHTTP",
"port": 80,
"listen": "0.0.0.0",
"protocol": "vless",
"settings": { "clients": [], "decryption": "none" },
"sniffing": { "enabled": true, "destOverride": ["http", "tls", "quic"] },
"streamSettings": {
"network": "xhttp",
"security": "reality",
"xhttpSettings": { "host": "", "mode": "auto", "path": "/pandl_ul_api" },
"realitySettings": {
"target": "osxapps.itunes.apple.com:443",
"shortIds": ["b2c86d5449d237fa"],
"privateKey": "替换这里!!!",
"serverNames": ["osxapps.itunes.apple.com"]
}
}
}
],
"outbounds": [
{ "tag": "direct", "protocol": "freedom" },
{ "tag": "block", "protocol": "blackhole" }
],
"routing": {
"rules": [
{ "sourceIP": ["geoip:cn"], "inboundTag": ["SS-IN"], "outboundTag": "block" },
{ "ip": ["geoip:private"], "outboundTag": "block" }
],
"domainStrategy": "IPIfNonMatch"
}
}
4. 节点接入
面板添加节点拿到 Key,然后在节点机上跑个 Docker 完事。
节点端 docker-compose.yml:
services:
remnanode:
image: remnawave/node:latest
network_mode: host
restart: always
volumes:
- './ssl:/var/lib/remnawave/configs/xray/ssl'
environment:
- NODE_PORT=11451 # 填你在面板设的端口
- SECRET_KEY=面板给的Key
docker compose up -d 跑起来就行。
5. 避坑指南
- 逻辑顺序:先去“内部分组”建个组,绑定上面的
Inbound Tag(比如REALITY-TCP),再去“主机”里添加节点 IP,最后去“用户”里开号。 - XHTTP:如果用 XHTTP,可以在“主机”的高级设置里把 Xray 配置覆盖一下,做下载流量分离。
- 安全性:面板反代千万别把 3000 端口直接露给公网,被扫了就是送人头,用 Tunnel 最稳。