Files
netbird-selfhosted/SUMMARY.md
timmy b6138095f0 docs: clarify NetBird built-in SSH vs regular SSH
Dashboard's 'SSH Access is disabled' warning confuses new users
who can clearly ssh into peers just fine. These are two different
systems: Dashboard's SSH Access is NetBird's own in-daemon SSH
tunnel server (accessed via Dashboard Connect button or
'netbird ssh'), while the regular ssh command uses the target
machine's OpenSSH running on port 22 and has nothing to do with
the Dashboard SSH toggle.

Home-lab use cases rarely need the NetBird built-in SSH — regular
OpenSSH over the NetBird overlay is sufficient. Doc explains when
you would actually want it (browser-based terminal from Dashboard,
credential offloading to NetBird identity, etc) and how to enable
if needed.
2026-04-18 22:43:34 +08:00

8.7 KiB
Raw Blame History

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 的 /proc mount namespace 限制下失敗

嘗試但無效的方法

  • --privileged flag
  • --security-opt apparmor=unconfined --security-opt seccomp=unconfined
  • mount -o remount,rw /proc/sys
  • apt 安裝的 crun 1.14.1OCI 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 分鐘
手機 Appserver closed the stream without sending trailers 透過 NPM 對外公開時NPM 用 proxy_pass 而非 grpc_pass 轉 gRPC加上 Caddy :80 預設不支援 h2c NPM 加 advanced_configgRPC 路由)+ Caddy 全域啟用 protocols h1 h2 h2c;見 npm-grpc-fix.md
PUT /api/setup-keys/{id} 回 200 但 name 沒改 NetBird API 只更新 auto_groupsrevokedname 是 read-only store.dbsetup_keys.name → 重啟 netbird-server;見 setup-keys-and-pat.md
peer 之間一直走 Relay[stun:...:3478] is Checking... STUN port 3478 被 Headscale (.126) 搶先佔用 DNAT NetBird 改用 3479/udpserver config + OpenWrt DNATstun-port-conflict.md
STUN 都通了,仍然 Connection type: RelayedICE candidate: -/- 客戶端 Force Relay Connection 開關被打開,跳過 ICE gathering App Settings → Advanced → 關掉 Force Relayclient-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-passwordyes 先出現first-match-wins /etc/ssh/sshd_config.d/99-allow-root.conf 覆蓋socket activation 不需重啟 sshd
新 peer netbird status 顯示 Connected 但互 ping 都不通 主機同時跑 Tailscale kernel-modeiptables-legacy ts-input100.64.0.0/10 非 tailscale0 封包全 dropNetBird 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 各裝置重連 daemonmacOS launchctl kickstart、iOS App Disconnect→Connect、Docker compose restart;見 network-routes.md
iPhone RTSP app 連不到 LAN 攝影機Safari 打 HTTP 可以) 某些 iOS app 不走系統 VPN 路由 換支援 VPN 的 appVLC 等),或改用 NetBird FQDN
Dashboard 顯示 "SSH Access is disabled" 但 ssh 指令照通 Dashboard 的 "SSH Access" 是 NetBird 內建 SSH 通道,不等於目標機器的 OpenSSH 兩種 SSH 是不同東西,不用理會那個提示;見 ssh-access.md

檔案位置

本機(/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 裝置
└── ssh-access.md                     ← 一般 SSH vs NetBird 內建 SSH 的差別

遠端伺服器192.168.42.127:/opt/netbird/

├── docker-compose.yml    ← 服務編排
├── Caddyfile             ← 反向代理路由
├── config.yaml           ← NetBird Server 設定
└── dashboard.env         ← Dashboard 環境變數

Docker Volume

  • netbird_netbird_dataNetBird 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 不支援叢集,需改用 PostgreSQLRelay 可獨立部署
  • 監控:已暴露 metricsPort: 9090 的 Prometheus 指標

參考資料