Document two lessons from the RTSP-camera-via-subnet-route diagnosis: 1. Existing NetBird clients don't automatically re-apply new route config; daemons stuck in old state need explicit restart per platform (launchctl kickstart / App reconnect / compose restart). Shows up as 'Networks: -' on status output and no entry in the system routing table. 2. Some iOS apps (notably certain RTSP players) don't use the system VPN route and bypass NetBird entirely even when route is correctly installed. Safari test distinguishes 'route broken' from 'app doesn't respect VPN'.
8.4 KiB
8.4 KiB
NetBird 部署摘要
部署結果
NetBird Self-Hosted 已部署於 192.168.42.127
- 部署日期:2026-04-17
- 存取方式:
http://192.168.42.127 - 部署模式:HTTP-only(無 TLS)、IP-only(無域名)
部署時的關鍵決策
1. 為何不用官方 getting-started.sh 腳本?
官方腳本是互動式的(讀取 /dev/tty),且預設選項需要公網域名與 Let's Encrypt。本次為內網 IP 部署,手動產生設定檔更直接。
腳本原始碼中支援 NETBIRD_DOMAIN=use-ip 模式(line 308),會自動偵測 IP 並切換為 HTTP、port 80、rel:// relay 協定。本次部署採用了相同的邏輯但以手動設定檔實現。
2. 為何選 Caddy 而非 Traefik 作為反向代理?
- Traefik 內建 Let's Encrypt ACME,但無域名時會反覆嘗試失敗
- Caddy 設定檔更精簡(約 10 行),純 HTTP 路由對本場景足夠
- 兩者都能正確處理 gRPC (h2c) 後端
3. 為何需要 crun 取代 runc?
遇到的錯誤:
OCI runtime create failed: runc create failed: unable to start container process:
error during container init: open sysctl net.ipv4.ip_unprivileged_port_start file:
reopen fd 8: permission denied
根因分析:
- Proxmox 非特權 LXC 將
/proc/sys掛載為唯讀(ro) - runc 1.2+ 為防止 TOCTOU 攻擊,會用「reopen fd」機制存取 sysctl 檔案
- 此機制在 LXC 的
/procmount namespace 限制下失敗
嘗試但無效的方法:
--privilegedflag--security-opt apparmor=unconfined --security-opt seccomp=unconfinedmount -o remount,rw /proc/sys- apt 安裝的 crun 1.14.1(OCI spec 太舊,Docker 不接受)
最終解法: 下載 GitHub 最新 crun binary (1.20),並設為 Docker 預設 runtime。
安裝步驟摘要
1. apt 安裝前置套件 (curl, jq, openssl) ← 約 1 分鐘
2. 安裝 Docker Engine ← 約 2 分鐘(網速慢)
3. 下載 crun binary,設定為 Docker 預設 runtime ← 約 30 秒
4. 建立 /opt/netbird/ 下四個設定檔 ← 約 30 秒
5. docker compose pull + up -d ← 約 1 分鐘
6. 等待 netbird-server 下載 GeoLite2 資料庫 ← 約 2-5 分鐘
總耗時約 10 分鐘(含 GeoIP 下載)。
服務驗證
所有服務已驗證正常運作:
| 端點 | 狀態 | 說明 |
|---|---|---|
http://192.168.42.127/ |
200 | Dashboard Web UI |
http://192.168.42.127/oauth2/.well-known/openid-configuration |
200 | OIDC Discovery |
STUN :3478/udp |
NAT 穿透 | |
| 內部 gRPC (Signal + Management) | h2c via Caddy |
疑難排解記錄
| 問題 | 原因 | 解法 |
|---|---|---|
| 初次 apt install 卡住 | 背景執行的 apt 行程未結束就被觸發第二個,導致 lock 競爭 | 清除 /var/lib/dpkg/lock* 與 /var/lib/apt/lists/lock,重啟 apt |
curl: command not found |
apt 仍在安裝中 | 等待安裝完成 |
| 所有 Docker 容器啟動失敗 | LXC /proc/sys 唯讀 + runc 1.3 的 reopen fd 機制 |
改用 crun 1.20 |
Docker Compose crun runtime 未知 |
/etc/docker/daemon.json 設定未生效 |
systemctl restart docker |
apt 的 crun 1.14.1 報 unknown version specified |
crun 的 OCI spec 1.0.0 與 Docker 29.4 不相容 | 從 GitHub 下載 crun 1.20 |
| 502 Bad Gateway(所有 API 端點) | netbird-server 下載 GeoLite2 中,尚未開始監聽 port 80 | 等待 2-5 分鐘 |
手機 App:server closed the stream without sending trailers |
透過 NPM 對外公開時,NPM 用 proxy_pass 而非 grpc_pass 轉 gRPC;加上 Caddy :80 預設不支援 h2c |
NPM 加 advanced_config(gRPC 路由)+ Caddy 全域啟用 protocols h1 h2 h2c;見 npm-grpc-fix.md |
PUT /api/setup-keys/{id} 回 200 但 name 沒改 |
NetBird API 只更新 auto_groups 和 revoked,name 是 read-only |
改 store.db 的 setup_keys.name → 重啟 netbird-server;見 setup-keys-and-pat.md |
peer 之間一直走 Relay,[stun:...:3478] is Checking... |
STUN port 3478 被 Headscale (.126) 搶先佔用 DNAT |
NetBird 改用 3479/udp(server config + OpenWrt DNAT);見 stun-port-conflict.md |
STUN 都通了,仍然 Connection type: Relayed、ICE candidate: -/- |
客戶端 Force Relay Connection 開關被打開,跳過 ICE gathering | App Settings → Advanced → 關掉 Force Relay;見 client-troubleshooting.md |
| ping RTT 尖峰到 2-4 秒 | 當下 WiFi/ISP 有 bufferbloat | 用 networkQuality -v 確認(看 RPM),與 NetBird 無關 |
Mac netbird status 卡在 Management: Disconnected, rpc error |
daemon 本身卡死,netbird up/down 救不回 |
sudo launchctl kickstart -k system/netbird(服務名是 netbird 而非 io.netbird.client);見 peer-deployment-ops.md |
LXC CT100 SSH root 密碼被擋(Permission denied) |
sshd_config 內有 PermitRootLogin prohibit-password 比 yes 先出現,first-match-wins |
加 /etc/ssh/sshd_config.d/99-allow-root.conf 覆蓋;socket activation 不需重啟 sshd |
新 peer netbird status 顯示 Connected 但互 ping 都不通 |
主機同時跑 Tailscale kernel-mode,iptables-legacy ts-input 把 100.64.0.0/10 非 tailscale0 封包全 drop,NetBird 100.71.0.0/16 中彈 |
iptables-legacy -I INPUT 1 -i wt0 -j ACCEPT + FORWARD 同理;systemd 持久化;詳見 peer-deployment-ops.md |
加 subnet route 後部分裝置拿不到路由(Networks: -、Status: Connecting) |
既有 NetBird daemon 不會自動重新套用 route,卡在舊 state | 各裝置重連 daemon:macOS launchctl kickstart、iOS App Disconnect→Connect、Docker compose restart;見 network-routes.md |
| iPhone RTSP app 連不到 LAN 攝影機(Safari 打 HTTP 可以) | 某些 iOS app 不走系統 VPN 路由 | 換支援 VPN 的 app(VLC 等),或改用 NetBird FQDN |
檔案位置
本機(/Users/timmy/dev/42_127/)
├── README.md ← 專案總覽
├── QUICKSTART.md ← 快速部署步驟
├── SUMMARY.md ← 本檔(部署摘要)
├── netbird-selfhosted-setup.md ← 完整安裝指南
├── npm-grpc-fix.md ← 經 NPM 對外公開時的 gRPC 修復
├── setup-keys-and-pat.md ← Setup Keys / PAT 操作與 API 限制繞道
├── stun-port-conflict.md ← STUN 改用 3479 (3478 被 Headscale 佔用)
├── client-troubleshooting.md ← 客戶端 Force Relay / bufferbloat 排錯
├── peer-deployment-ops.md ← Docker 部署客戶端、改名、Mac daemon 重啟
├── groups-and-policies.md ← Groups 與 Access Control 設計與操作
└── network-routes.md ← Subnet Router 讓 peer 存取 LAN 非 NetBird 裝置
遠端伺服器(192.168.42.127:/opt/netbird/)
├── docker-compose.yml ← 服務編排
├── Caddyfile ← 反向代理路由
├── config.yaml ← NetBird Server 設定
└── dashboard.env ← Dashboard 環境變數
Docker Volume
netbird_netbird_data:NetBird SQLite 資料庫與 GeoIP 資料
後續維運
日常指令
ssh root@192.168.42.127
cd /opt/netbird
# 查看日誌
docker compose logs -f
# 重啟
docker compose restart
# 更新映像
docker compose pull && docker compose up -d
# 備份資料庫
docker compose exec netbird-server sqlite3 /var/lib/netbird/store.db ".backup /tmp/backup.db"
docker cp netbird-server:/tmp/backup.db ./backup-$(date +%F).db
可能的擴展
- 加上 TLS:若之後取得域名,可切換為 Traefik + Let's Encrypt(或本機自簽憑證)
- 外部 IdP:需支援 OIDC 的 IdP(如 Authentik、Keycloak、Google)可取代內建 Dex
- 高可用性:SQLite 不支援叢集,需改用 PostgreSQL;Relay 可獨立部署
- 監控:已暴露
metricsPort: 9090的 Prometheus 指標
參考資料
- 官方文件:https://docs.netbird.io/selfhosted/selfhosted-quickstart
- Getting Started 腳本:https://github.com/netbirdio/netbird/releases/latest/download/getting-started.sh
- crun release:https://github.com/containers/crun/releases
- Proxmox LXC Docker 相容性:https://pve.proxmox.com/wiki/Linux_Container#_features