输入搜索…

· ops · 9 min 阅读

NetBird 自托管部署记录(自定义域名 + Nginx 反代)

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

NetBird 自托管部署记录(自定义域名 + Nginx 反代)

@dzngriffin

环境:单台 Linux 服务器,对外域名 net-test.ll1025.cn,由外部 Nginx(宝塔面板)负责 HTTPS 与反向代理。 内部端口:Dashboard 127.0.0.1:8080,Management / Signal 127.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 路径路由对照表

路径协议上游作用
/relayWebSocketnetbird_serverRelay(中继)长连接
/ws-proxy/WebSocketnetbird_serverSignal / Management 的 WS 代理
/signalexchange.SignalExchange/gRPCnetbird_serverSignal 服务
/management.ManagementService/gRPCnetbird_serverManagement 服务
/management.ProxyService/gRPCnetbird_serverNetBird 反向代理功能(可选)
/api/HTTPnetbird_server管理 REST API
/oauth2/HTTPnetbird_server内置 IdP
/HTTPnetbird_dashboardDashboard 兜底

关键点: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.cn

macOS

系统设置 → 网络 → 代理 → 详细信息 → 忽略代理设置的主机与域,填入:

ll1025.cn
*.ll1025.cn

Linux / 终端环境

设置 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 传入。


六、部署后验证

  1. 控制台可访问:浏览器打开 https://net-test.ll1025.cn,能正常进入 Dashboard 登录页。
  2. API 正常:访问 https://net-test.ll1025.cn/api/ 不应返回 502/504。
  3. gRPC 正常:客户端执行 netbird up --setup-key <KEY> 能成功注册,netbird status 显示 Connected。
  4. 中继可用:Dashboard 的 Peers 列表中能看到节点,且 NAT 穿透失败时能走 relay。
  5. 容器日志无 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
返回文章列表