· ops · 9 min 阅读
NetBird 自托管部署记录(自定义域名 + Nginx 反代)
记录一次 NetBird 自托管部署的完整过程:一键安装、自定义域名、Nginx 反向代理配置、DNS Zones 解析,以及国内网络环境下的常见问题排查。

环境:单台 Linux 服务器,对外域名
net-test.ll1025.cn,由外部 Nginx(宝塔面板)负责 HTTPS 与反向代理。 内部端口:Dashboard127.0.0.1:8080,Management / Signal127.0.0.1:8081。
一、一键安装
在服务器上直接执行官方安装脚本:
curl -fsSL https://github.com/netbirdio/netbird/releases/latest/download/getting-started.sh | bash脚本运行后会进入交互式配置,需要填写/选择以下两项:
| 交互项 | 填写内容 |
|---|---|
| 自定义域名(Domain) | net-test.ll1025.cn |
| 反向代理方式(Proxy) | 选择 5 |
选择 5 表示由外部已有的 Nginx 接管反向代理,安装脚本只负责拉起容器(dashboard / management / signal / coturn 等),不自动申请证书、不占用 80/443。
安装完成后,服务监听在本机回环地址:
127.0.0.1:8080— Dashboard(Web 控制台)127.0.0.1:8081— Management + Signal(HTTP API / gRPC / WebSocket 共用)
二、Nginx 反向代理配置
2.1 upstream 定义
在站点的 server 块之外(通常是 nginx.conf 的 http 段或 conf.d/ 下的独立文件)定义两个 upstream:
upstream netbird_dashboard {
server 127.0.0.1:8080;
keepalive 10;
}
upstream netbird_server {
server 127.0.0.1:8081;
}2.2 server 块(完整参考配置)
下面的
access_log、error_log、root、ssl_certificate路径由宝塔面板自动生成,请按自己站点的实际路径调整;核心是location部分。
server {
listen 80;
listen 443 ssl;
server_name net-test.ll1025.cn;
index index.php index.html index.htm default.php default.htm default.html;
access_log /www/sites/net-test.ll1025.cn/log/access.log main;
error_log /www/sites/net-test.ll1025.cn/log/error.log;
location ~ ^/(\.user.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README.md) {
return 404;
}
location ^~ /.well-known/acme-challenge {
allow all;
root /usr/share/nginx/html;
}
if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {
return 403;
}
root /www/sites/net-test.ll1025.cn/index;
error_page 404 /404.html;
http2 on;
if ($scheme = http) {
return 301 https://$host$request_uri;
}
ssl_certificate /www/sites/net-test.ll1025.cn/ssl/fullchain.pem;
ssl_certificate_key /www/sites/net-test.ll1025.cn/ssl/privkey.pem;
ssl_protocols TLSv1.3 TLSv1.2;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
proxy_set_header X-Forwarded-Proto https;
add_header Strict-Transport-Security "max-age=31536000";
# gRPC / WebSocket 是长连接,超时给到 1 天
client_header_timeout 1d;
client_body_timeout 1d;
send_timeout 1d;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $host;
grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 1) WebSocket:relay(路径没有尾斜杠,所以是 /relay 不是 /relay/)
location ^~ /relay {
proxy_pass http://netbird_server;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 1d;
proxy_send_timeout 1d;
proxy_buffering off;
}
# 2) WebSocket:signal / management 的 ws 代理
location ^~ /ws-proxy/ {
proxy_pass http://netbird_server;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 1d;
proxy_send_timeout 1d;
proxy_buffering off;
}
# 3) 原生 gRPC:signal(必须 grpc_pass,不能用 proxy_pass)
location ^~ /signalexchange.SignalExchange/ {
grpc_pass grpc://netbird_server;
grpc_read_timeout 1d;
grpc_send_timeout 1d;
grpc_socket_keepalive on;
}
# 4) 原生 gRPC:management
location ^~ /management.ManagementService/ {
grpc_pass grpc://netbird_server;
grpc_read_timeout 1d;
grpc_send_timeout 1d;
grpc_socket_keepalive on;
}
# 5) 原生 gRPC:NetBird 反向代理功能(用不到可删)
location ^~ /management.ProxyService/ {
grpc_pass grpc://netbird_server;
grpc_read_timeout 1d;
grpc_send_timeout 1d;
grpc_socket_keepalive on;
}
# 6) HTTP:管理 REST API
location ^~ /api/ {
proxy_pass http://netbird_server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_read_timeout 1d;
}
# 7) HTTP:内置 IdP
location ^~ /oauth2/ {
proxy_pass http://netbird_server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_read_timeout 1d;
}
# 8) Dashboard 兜底,放最后
location / {
proxy_pass http://netbird_dashboard;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Connection "";
}
}2.3 路径路由对照表
| 路径 | 协议 | 上游 | 作用 |
|---|---|---|---|
/relay | WebSocket | netbird_server | Relay(中继)长连接 |
/ws-proxy/ | WebSocket | netbird_server | Signal / Management 的 WS 代理 |
/signalexchange.SignalExchange/ | gRPC | netbird_server | Signal 服务 |
/management.ManagementService/ | gRPC | netbird_server | Management 服务 |
/management.ProxyService/ | gRPC | netbird_server | NetBird 反向代理功能(可选) |
/api/ | HTTP | netbird_server | 管理 REST API |
/oauth2/ | HTTP | netbird_server | 内置 IdP |
/ | HTTP | netbird_dashboard | Dashboard 兜底 |
关键点:gRPC 必须用
grpc_pass grpc://,用proxy_pass会导致客户端连接失败;WebSocket 必须显式转发Upgrade/Connection并关闭proxy_buffering。
保存后重载 Nginx:
nginx -t && nginx -s reload三、自定义域名解析(DNS Zones)
如果希望客户端/资源使用自定义域名(而非服务器公网 IP)来解析,可在 NetBird 管理后台 → DNS → DNS Zones 中新增区域并配置记录。
配置完成后,客户端会通过 NetBird 的 DNS 解析该域名。
四、本机代理接管 DNS 的问题
本机如果运行了代理软件,会接管系统 DNS,拦截上述自定义 DNS 解析,导致域名无法解析到 NetBird 分配的地址。
解决思路是让目标域名「不走代理」:要么在代理软件里改订阅的覆写配置,把该域名的解析交给 NetBird 的 DNS;要么在系统代理设置里把该域名加入绕过列表。下面两种方法任选其一。
4.1 方法一:修改订阅代理配置的覆写规则
在订阅配置文件(Merge / 覆写配置)中加入以下规则,把目标域名的解析交给 NetBird 的 DNS:
# 订阅覆写配置(Merge / Override)
dns:
nameserver-policy:
'+.ll1025.cn':
- 100.100.255.254说明:
+.ll1025.cn是示例域名,请替换为自己在 DNS Zones 中配置的域名。100.100.255.254为 NetBird 客户端内置 DNS 的监听地址,需确保该 DNS 地址与实际部署一致。- 覆写生效后,该域名后缀的查询不再走公共 DNS,而是交给 NetBird 解析。
4.2 方法二:修改系统代理的绕过列表
如果不想改动代理软件的配置,也可以直接在操作系统的代理设置里,把目标域名加进「不使用代理的地址」列表。
Windows
设置 → 网络和 Internet → 代理 → 手动设置代理 → 不使用代理的地址,填入:
ll1025.cn;*.ll1025.cnmacOS
系统设置 → 网络 → 代理 → 详细信息 → 忽略代理设置的主机与域,填入:
ll1025.cn
*.ll1025.cnLinux / 终端环境
设置 no_proxy 环境变量:
export no_proxy="localhost,127.0.0.1,ll1025.cn,.ll1025.cn"说明
- 绕过列表只影响「是否走代理」,不会改变 DNS 的解析结果。如果域名仍然解析不到 NetBird 分配的地址,说明 DNS 请求本身仍被代理接管,此时改用 4.1 的覆写规则。
- 两种方法可以叠加使用:绕过列表负责让流量直连,覆写规则负责让解析走 NetBird。
五、禁用地理位置数据库(国内网络环境)
NetBird 启动时会尝试下载 GeoLite2 地理位置数据库。国内网络环境通常下载失败,日志里会持续报错,建议直接禁用。
方式:修改 docker-compose.yml,为 management 服务添加环境变量:
services:
management:
environment:
- NB_DISABLE_GEOLOCATION=true修改后重建容器:
docker compose up -d该配置的本质是设置环境变量
NB_DISABLE_GEOLOCATION=true,也可以直接改.env或运行docker compose时通过-e传入。
六、部署后验证
- 控制台可访问:浏览器打开
https://net-test.ll1025.cn,能正常进入 Dashboard 登录页。 - API 正常:访问
https://net-test.ll1025.cn/api/不应返回 502/504。 - gRPC 正常:客户端执行
netbird up --setup-key <KEY>能成功注册,netbird status显示 Connected。 - 中继可用:Dashboard 的 Peers 列表中能看到节点,且 NAT 穿透失败时能走 relay。
- 容器日志无 GeoLite 报错:
docker compose logs -f management中不再出现地理位置数据库下载失败。
七、常见问题
| 现象 | 排查方向 |
|---|---|
| 打开域名 502 Bad Gateway | 容器未启动,或 upstream 端口与容器实际映射不一致(8080 / 8081) |
| 客户端一直 Connecting | /signalexchange.SignalExchange/ 未用 grpc_pass,或超时时间太短 |
| 登录后跳转异常 / 循环登录 | X-Forwarded-Proto、X-Forwarded-Host 未正确传递,HTTPS 被识别成 HTTP |
| 自定义域名解析不到 | 本机代理接管 DNS,按第四节处理(覆写规则或系统代理绕过列表) |
| management 容器日志刷 GeoLite 错误 | 按第五节添加 NB_DISABLE_GEOLOCATION=true |
| WebSocket 断连频繁 | proxy_read_timeout / proxy_send_timeout 调大,并确认 proxy_buffering off |