如何在 VPS 上部署 UniFi controller
了解在 VPS 執行 UniFi Network Application 的實務設定:2 GB RAM 是最低需求、Docker 搭配 MongoDB、透過 set-inform 進行 Layer 3 adoption,以及應保持私有的連接埠。
VPS 上的 UniFi controller 實際負責什麼
VPS 上的 UniFi controller 是一台集中管理伺服器。即使受管理的站點停止運作,它仍能保持可連線。這套軟體是 Ubiquiti 的 UniFi Network Application:以 Java 撰寫,後端使用 MongoDB 資料庫。它會設定 access point 與 switch、儲存統計資料,並提供管理介面。它不會承載 client traffic。
這一點決定了 controller 應該部署在哪裡。若將 controller 放在所管理的辦公室內,辦公室的網路和檢視該網路的工具可能會在同一時間失去連線。若將它放在具備穩定 public address 的 VPS 上,它便能持續執行、持續收集資料,並從同一處採用多個站點的裝置。它需要的是高可用性,而不是強大的硬體效能。
controller 離線時,已完成採用的 access point 與 switch 仍會依照先前推送的設定轉送 traffic。你會失去 dashboard 和統計資料,以及所有需要 controller 保持在線的功能,例如 guest portal 登入,或 controller 本身作為 RADIUS server 時所提供的 RADIUS(remote authentication dial-in user service)。client 仍會保持連線。
UniFi controller 需要多少 RAM?
2 GB 是最低需求,4 GB 才是建議購買的規格。同一台主機中有兩個記憶體使用者:Java 和 MongoDB。兩者會各自獨立配置所需資源。
Java heap 上限由 MEM_LIMIT 控制,container image 預設設為 1024 MB。另一部分是 MongoDB。其 WiredTiger storage engine 會將 cache 設為 1 GB 以上 RAM 的一半,或 256 MB,取兩者中較大者。以 2 GB VPS 為例,大約會使用 512 MB cache、1 GB heap、JVM 本身的 non-heap memory,以及作業系統所需的記憶體。平時可以運作,但遇到繁忙日就可能耗盡記憶體,接著 kernel out-of-memory killer 會終止其中一個程序。若服務無法解釋地重新啟動,請執行 dmesg -T | grep -i 'killed process',確認是否發生這種情況。如果只有 2 GB,請加入 swap file。
CPU 和磁碟需求不高。一或兩個 vCPU 足以處理幾十台裝置。先配置 20 GB 磁碟並持續監控,因為資料庫會隨著用戶端數量和統計資料保留時間增加。單獨執行 controller 時,4 GB 主機大多數時間都處於閒置狀態。因此,如果打算在同一台主機上執行其他服務,應先依其他服務的需求配置資源,因為 PhotoPrism 和 Immich 的 RAM 最低需求差異很大,而且兩者所需資源都高於 controller。
有一項 CPU 功能會影響是否能執行,而且在低價方案中很容易被忽略:
grep -m1 -o avx /proc/cpuinfoMongoDB 5.0 及後續版本需要 x86_64 硬體支援 AVX(advanced vector extensions)。如果該命令沒有輸出內容,mongod 會在啟動期間終止,container 也會不斷重新啟動,因為該 binary 執行了 CPU 不支援的指令。較舊的 Intel Celeron 和 Pentium 主機是常見原因,hypervisor 未將 CPU flags 提供給 guest 也可能造成相同問題。MongoDB 4.4 不需要 AVX,是唯一的替代方案,但 upstream 已不再修補這個資料庫版本。改用配備較新 CPU 的主機是較好的做法。在 ARM VPS 上不會遇到這個問題,因為 AVX 是 x86 instruction set,而兩種 image 都提供 arm64 builds。如果要在兩者之間選擇,ARM 與 x86 VPS 方案的差異不只在價格。
使用 Docker Compose 安裝 UniFi Network Application
Docker 是最不容易出現意外的方式,因為您可以將 MongoDB 固定在應用程式支援的版本,而不是直接使用發行版提供的版本。如果主機尚未安裝 Docker,請先在 VPS 上安裝 Docker。
mkdir -p ~/unifi/config ~/unifi/db
cd ~/unifiMongoDB 需要先建立使用者,應用程式才能登入。官方 MongoDB image 會在第一次啟動時執行 /docker-entrypoint-initdb.d 中找到的任何 script。將以下內容儲存為 ~/unifi/init-mongo.sh:
#!/bin/bash
if which mongosh > /dev/null 2>&1; then
mongo_init_bin='mongosh'
else
mongo_init_bin='mongo'
fi
"${mongo_init_bin}" <<EOF
use ${MONGO_AUTHSOURCE}
db.auth("${MONGO_INITDB_ROOT_USERNAME}", "${MONGO_INITDB_ROOT_PASSWORD}")
db.createUser({
user: "${MONGO_USER}",
pwd: "${MONGO_PASS}",
roles: [
"clusterMonitor",
{ db: "${MONGO_DBNAME}", role: "dbOwner" },
{ db: "${MONGO_DBNAME}_stat", role: "dbOwner" },
{ db: "${MONGO_DBNAME}_audit", role: "dbOwner" },
{ db: "${MONGO_DBNAME}_restore", role: "dbOwner" }
]
})
EOF該 script 只會在資料庫目錄為空時執行。若第一次以錯誤的密碼啟動 stack,使用者就會以錯誤密碼建立。之後編輯 compose file 不會改變任何設定,因為該 script 不會再次執行。症狀是 application container 記錄 MongoDB authentication failure,而 web interface 始終不會出現。全新安裝時,修正方式是停止 stack、刪除 ~/unifi/db,再重新啟動。
接著建立 ~/unifi/compose.yaml:
services:
unifi-db:
image: docker.io/mongo:8.0
container_name: unifi-db
environment:
- MONGO_INITDB_ROOT_USERNAME=root
- MONGO_INITDB_ROOT_PASSWORD=change-this-root-password
- MONGO_USER=unifi
- MONGO_PASS=change-this-unifi-password
- MONGO_DBNAME=unifi
- MONGO_AUTHSOURCE=admin
volumes:
- ./db:/data/db
- ./init-mongo.sh:/docker-entrypoint-initdb.d/init-mongo.sh:ro
restart: unless-stopped
unifi-network-application:
image: lscr.io/linuxserver/unifi-network-application:10.5.67-ls141
container_name: unifi-network-application
depends_on:
- unifi-db
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
- MONGO_USER=unifi
- MONGO_PASS=change-this-unifi-password
- MONGO_HOST=unifi-db
- MONGO_PORT=27017
- MONGO_DBNAME=unifi
- MONGO_AUTHSOURCE=admin
- MEM_LIMIT=1024
- MEM_STARTUP=1024
volumes:
- ./config:/config
ports:
- "8080:8080"
- "3478:3478/udp"
- "127.0.0.1:8443:8443"
restart: unless-stopped這兩個 image tag 都是刻意固定的。10.5.67-ls141 是 2026 年 8 月當時的 current application release,因此請檢查 image 的 release list,並在安裝時固定當下的 current version。database tag 更為重要。MongoDB 不會自行跨 major version 升級資料檔案,因此 mongo:latest 日後可能會拉取新的 major version、拒絕開啟現有檔案,然後反覆重新啟動。請固定 major version,並依計畫手動升級。UniFi Network 8.1 及後續版本支援 MongoDB 3.6 至 7.0,而 9.0 新增了對 MongoDB 8.0 的支援。
PUID 和 PGID 必須對應主機上的實際使用者,否則 ./config 下的檔案會由無法寫入這些檔案的 identity 擁有。執行 id 取得目前值。瞭解 PUID 和 PGID 在 container image 中的運作方式說明不一致時的情況。
啟動 stack 並監看:
docker compose up -d
docker compose ps
docker compose logs -f unifi-network-applicationdocker compose ps 應顯示兩個 container 都是 running。若 unifi-db 停留在 restarting,原因可能是上述 AVX 問題,或是 ./db 的權限問題。日誌穩定後,檢查兩個 listening port:
curl -sk -o /dev/null -w '%{http_code}\n' https://127.0.0.1:8443/
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/inform只要收到任何 HTTP status code,就表示 listener 已綁定並回應。Connection refused 表示應用程式仍在啟動;首次執行時,小型 VPS 可能需要一到兩分鐘,也可能表示應用程式從未成功啟動。
在不公開管理介面的情況下存取管理介面
上方檔案在 127.0.0.1 上發布了 8443 埠,因此 VPS 外部的任何來源都無法連線至管理介面。透過 SSH 轉送連線,以執行設定精靈:
ssh -L 8443:127.0.0.1:8443 you@vps.example.com保持該工作階段開啟,然後瀏覽 https://127.0.0.1:8443。憑證是自我簽署憑證,因此瀏覽器只會警告一次。建立管理員帳戶、設定網站名稱,暫時略過裝置採用。
對單一管理員而言,使用 SSH tunnel 即可。若是團隊使用,請為 VPS 指定私有位址,並將介面繫結至該位址。在自己的 VPS 上設定 WireGuard VPN 和 設定 Tailscale subnet router 都能提供只有團隊成員可路由到的位址。使用 WireGuard 時,請將發布的埠變更為 10.8.0.1:8443:8443;使用 Tailscale 時,則變更為 Tailscale 指派的位址。請注意:Docker 無法在尚不存在的位址上發布連接埠,因此 tunnel 介面必須在容器啟動前先建立,否則容器會因 bind 錯誤而啟動失敗。
為什麼遠端 UniFi 裝置無法採用
UniFi 裝置出廠後會透過本機網路上的 UDP port 10001 廣播來尋找 controller。廣播不會離開 LAN,因此位於另一個城市辦公室的裝置,永遠無法在 VPS 上找到 controller。這就是 Layer 3 adoption,也是多數人卡住的地方。裝置本身沒有問題,controller 也沒有問題。只是沒有任何設定告訴裝置應該去哪裡尋找。
首先,告訴 controller 要提供哪個位址。在 controller 的 Settings、System 區段中,有一個可啟用 override 選項的 inform host 設定。將它設為 VPS 的公開 hostname 或 IP。若未設定,controller 會公告自身介面所看到的位址;在 Docker bridge network 中,這通常是類似 172.18.0.3 的私有位址。裝置收到該位址後無法路由到它,便會繼續搜尋。
接著,讓裝置連到該位址。透過 SSH 從遠端 LAN 連入裝置。恢復原廠設定的裝置接受使用者名稱 ubnt 與密碼 ubnt:
ssh ubnt@192.168.1.20
set-inform http://vps.example.com:8080/inform較新的裝置 firmware 會進入選單,而不是 shell。請將相同內容作為單一命令執行:
ssh ubnt@192.168.1.20 mca-cli-op set-inform http://vps.example.com:8080/inform現在,裝置會在 controller 中顯示為可採用。按一下 Adopt,狀態會變更為 Adopting。以下是最容易讓人意外的部分:通常必須再次執行 set-inform。裝置會重新啟動並進入 provisioning,接著使用自身設定中儲存的 inform URL;controller 尚未完成替換該設定。當狀態顯示為 Adopting 時再次執行該命令,即可完成交接。在裝置上輸入 info,即可查看目前儲存的 inform URL 與狀態。
如果裝置先前曾被其他 controller 採用,僅執行 set-inform 並不足以完成操作,因為裝置仍保留該 controller 的認證資訊。請先將裝置恢復原廠設定,可以按下 reset 按鈕,或使用舊認證透過 SSH 執行 set-default。
如果裝置數量超過少數幾台,請改用 DHCP。DHCP(dynamic host configuration protocol)的 option 43 可攜帶廠商專用值,而 UniFi 裝置會從 suboption 2 讀取 inform URL。在任何 Linux 主機上建立十六進位字串:
URL="http://vps.example.com:8080/inform"
HEX=$(printf '%s' "$URL" | od -An -tx1 | tr -d ' \n')
printf '02%02x%s\n' "${#URL}" "$HEX"對於 http://192.168.3.10:8080/inform 這個 31 byte 字串,該命令會輸出 021f687474703a2f2f3139322e3136382e332e31303a383038302f696e666f726d。將結果以十六進位值貼入路由器的 DHCP option 43 欄位。之後,每台在該網路上開機的裝置都會從 DHCP lease 取得 controller 位址,完全不需要 SSH。較舊的指南會使用 suboption 1,也就是 0104 後接 IPv4 位址的 4 個十六進位位元組;裝置仍接受這種格式。
如果你在該站點執行 DNS,還有第三種方式。UniFi 裝置開機時會嘗試解析 hostname unifi,因此只要建立一筆指向 VPS 位址的 unifi A record,即可在不逐台設定的情況下採用裝置。這只有在你能控制裝置實際使用的 resolver 時才有效。
要開放哪些 UniFi 連接埠,哪些應維持私有
只有 2 個連接埠需要讓遠端站點連線。
- TCP 8080 是 inform 通道,所有已採用的裝置都會連線至此。其內部負載使用 controller 在採用裝置時提供的金鑰進行 AES 加密,因此這裡通常使用純 HTTP 設定。
- UDP 3478 是 STUN(NAT 的 session traversal utilities),裝置會使用它維持回到 controller 的路徑。
在 VPS 上,其他連接埠都應保持關閉。
- TCP 8443 是管理介面。這個連接埠絕不能公開。它存放 controller 管理之所有站點的設定,而且只由 1 個密碼保護。
- UDP 10001 和 UDP 1900 用於廣播探索。廣播不會跨越網際網路,因此開放這些連接埠沒有作用。
- TCP 8880 和 TCP 8843 用於 guest portal 重新導向。只有在執行 guest portal 時才開放。
- TCP 6789 是行動裝置速度測試,UDP 5514 是遠端 syslog。使用這些功能時再加入。
- TCP 27117 是 MongoDB。在上方的 compose file 中,database 完全沒有發布連接埠,因此只存在於內部 Docker network。請維持這種設定。
如果站點具有固定的公開位址,只允許這些位址:
sudo ufw allow OpenSSH
sudo ufw allow proto tcp from 203.0.113.4 to any port 8080
sudo ufw allow proto udp from 203.0.113.4 to any port 3478
sudo ufw enable
sudo ufw status verboseVPS 防火牆的 ufw 基本設定涵蓋這些規則所假設的預設拒絕設定。
這裡有一個經常讓人誤判的陷阱。Docker 發布的連接埠會繞過 ufw。發布連接埠時,Docker 會直接將 NAT 與轉送規則寫入 iptables,而該流量會在 Docker 自己的 chain 中篩選,不會經過 ufw 管理的 INPUT chain。因此,ufw deny 8443 在 ufw status 中看似正確,但該連接埠仍會對全世界開放。請從另一台機器測試,絕不要從 VPS 本身測試:
nc -vz vps.example.com 8443出現拒絕連線或逾時才是預期結果。如果能連線,無論 ufw 顯示什麼,該連接埠都已公開。可靠的修正方式就是 compose file 中已採用的方式:將連接埠發布到 127.0.0.1 或 tunnel 位址,讓 Docker 不會將其繫結至公開介面。也可以在 DOCKER-USER chain 中加入規則,但繫結連接埠較簡單,且規則順序錯誤也不會使其失效。
Ubiquiti 自家的安裝程式呢?
Ubiquiti 提供 Network Application 的 Debian 套件。這個套件可以運作,但在目前的 Ubuntu 上會遇到發行版無法自行處理的 MongoDB 問題:Ubuntu 22.04 和 24.04 不提供 MongoDB server 套件,因此必須自行加入 MongoDB 的 repository,並手動配對相容版本。上方的 container 透過單一固定 tag 完成版本配對,因此本指南採用這種方式。
Ubiquiti 較新的自架產品是 UniFi OS Server。它會在 Podman container 中執行 UniFi 應用程式,並提供與其硬體主控台相同的 UniFi OS。截至 2026 年 8 月,它需要 x86_64 Ubuntu 22.04 或 24.04,以及具備 slirp4netns 的 Podman 4.3.1 或更新版本。最低需求為 2 vCPU 與 4 GB RAM,建議使用 4 vCPU 與 8 GB RAM。安裝程式位於其下載頁面,必須使用免費的 Ubiquiti 帳戶才能取得,因此沒有穩定的單行 URL 可直接貼入指南。它會建立名為 uosserver 的 system user,並以該使用者執行 container。若希望採用供應商提供的套件,請選擇這個方案。若希望自行固定版本,並讓這台主機保留給其他工作,請選擇 container stack。
UniFi 備份存放位置,以及如何將備份移出主機
Controller 會依照您在 Settings 的 backup 區段中設定的排程建立備份,並依設定保留指定數量的檔案。備份檔會寫入容器內的 /config/data/backup/autobackup,而該路徑對應至主機上的 ~/unifi/config/data/backup/autobackup,檔名格式類似 autobackup_10.5.67_20260813_1200_1755086400004.unf。
確認備份檔確實出現:
ls -l ~/unifi/config/data/backup/autobackup設定排程後經過 1 天,目錄仍然是空的,這是新建容器安裝中已知的失敗情況。應用程式預期 autobackup 目錄已存在,但不會自行建立,因此排程工作會靜默地不寫入任何檔案。請使用容器執行時所用的相同使用者建立該目錄,然後等待下一次執行:
mkdir -p ~/unifi/config/data/backup/autobackup
docker compose restart unifi-network-application.unf 檔案包含 site 設定與 administrator 帳戶,因此應將其視為加密金鑰。請將副本擷取至您能控制的電腦,並妥善保密:
rsync -av you@vps.example.com:~/unifi/config/data/backup/autobackup/ ~/unifi-backups/還原只需一個步驟。新安裝的 setup wizard 第一頁會提供從備份檔還原的選項;執行中的 Controller 則可在相同的設定頁面執行還原。請還原至相同版本或較新的版本。若備份檔是由比目標版本更新的應用程式建立,還原會遭拒絕;因此應將版本號碼與檔案一併記錄。
控制器升級可能造成的問題
每次升級前,請手動建立備份並下載備份檔。接著:
docker compose pull
docker compose up -d
docker compose logs -f unifi-network-application資料庫是最先可能出問題的部分。在同一次編輯中,將 mongo 標籤改為新的主要版本,並同時升級應用程式,是導致控制器無法啟動的最快方式,因為 MongoDB 未經分階段升級時,無法開啟其他主要版本建立的資料檔案。請單獨升級應用程式。MongoDB 則分開處理,每次只升級一個主要版本,並事先準備新的備份。
接下來是記憶體問題。較大的版本需要較大的 heap。如果應用程式能啟動、執行幾分鐘後終止,請將 MEM_LIMIT 和 MEM_STARTUP 提高至 1536 或 2048,然後重新啟動。主機上的 dmesg -T | grep -i 'killed process' 可確認是否由 kernel 終止該程序。
裝置韌體是容易被忽略的風險。控制器自行升級後,會提供已採用裝置的韌體升級。請勿在同一個工作階段接受這些升級。如果裝置升級與控制器升級重疊執行,兩者之間的連線又中斷,裝置可能停留在未完成佈建的狀態;此時你可能又得透過 SSH 連線到另一棟建築物內的硬體,處理 set-inform。
升級時段本身通常比想像中溫和。控制器重新啟動期間,裝置仍會轉送網路流量,因此使用者不會察覺。會停止的是訪客入口網站與 RADIUS,前提是這些服務由控制器提供。因此請選擇兩者都未使用的時段。控制器在 3 a.m. 無聲終止時,最好能及時得知;請將 Uptime Kuma 狀態監控 指向 port 8080,讓它通知你。
誠實的替代方案:Ubiquiti 託管的主控台
Ubiquiti 也以服務形式提供相同功能。截至 2026 年 8 月,Official UniFi Cloud Console 的月費為 $29 起,可管理最多 500 台 UniFi 裝置,更新與備份由 Ubiquiti 負責。剛才安裝的自架應用程式免費使用,且不需訂閱。
如果你只管理一個網站,且不想自行套用修補程式,可以選擇託管主控台。如果你管理多個網站,或希望將控制器放在自己管理的網路中,並與其他服務共用同一台主機,則可以選擇 VPS。小規模部署時,兩者的成本差異確實存在,但這不是唯一需要考量的因素:託管主控台的可用性取決於其他人的維運,而 VPS 則由你負責,包括磁碟空間耗盡的那個夜晚。如果這台主機無論如何都能發揮效益,接下來應閱讀你還能在 VPS 上執行哪些服務。
FAQ
為什麼我的 UniFi 裝置無法採用 VPS 上的 controller?
裝置會透過 UDP port 10001 廣播來探索 controller,但廣播不會離開區域網路,因此遠端站點的裝置無法找到公網上的 controller。請在 controller 的系統設定中,將 inform host override 設為 VPS 的主機名稱,再使用 ssh ubnt@<device-ip> 後接 set-inform http://vps.example.com:8080/inform,將裝置指向該主機。如果裝置停留在 Adopting 狀態,請在該狀態下再次執行 set-inform。如果裝置先前已被其他 controller 採用,請先將其重設為原廠預設值,因為裝置仍保留舊 controller 的認證資訊。
自架 UniFi controller 需要多少 RAM?
2 GB 是最低可用容量,4 GB 則較為充裕。此應用程式由 Java 與 MongoDB 組成,兩者會分別配置記憶體:container image 預設將 Java heap 上限設為 1024 MB,而 MongoDB 的 WiredTiger cache 會使用超過 1 GB 的 RAM 中一半容量。在 x86_64 上,也請使用 grep -m1 -o avx /proc/cpuinfo 確認 CPU 提供 AVX,因為 MongoDB 5.0 以上版本若沒有 AVX 就無法啟動,database container 會持續重新啟動。
我應該將 port 8443 暴露到網際網路嗎?
不應該。Port 8443 是管理介面,包含 controller 管理之每個站點的設定。請透過 127.0.0.1 發布該介面,並使用 ssh -L 8443:127.0.0.1:8443 you@vps.example.com 存取,或將其綁定至 WireGuard 或 Tailscale 位址。只有 TCP 8080 與 UDP 3478 必須能從各站點連入;如果站點使用固定的公網位址,也可以將這些 port 限制為僅允許站點位址。請注意,Docker published port 不會受到 ufw 過濾,因此應從外部機器測試,不要只依賴 ufw status。
VPS controller 停止運作時,我的網路會中斷嗎?
不會。已採用的 access point 與 switch 會使用 controller 已推送的設定繼續轉送流量,因此用戶端仍可保持連線,Wi-Fi 也會繼續運作。停止的是管理功能。您會失去 dashboard 與統計資料收集功能,以及由 controller 提供的即時功能,例如 guest portal 認證,或 controller 作為 RADIUS server 時提供的 RADIUS 服務。
UniFi controller 將自動備份儲存在哪裡?
在此使用的 container image 中,備份會寫入 /config/data/backup/autobackup。該路徑會對應到主機上的資料路徑加上 data/backup/autobackup,檔案格式為 .unf,檔名包含版本與時間戳記。在某些全新安裝中,autobackup 目錄不存在;排程備份會因此不寫入任何內容,也不會回報錯誤。因此,設定排程一天後請列出該目錄;如果目錄為空,請自行建立。請將檔案複製到 VPS 以外的位置,因為 .unf 包含站點設定與管理員帳號。