Homepage(gethomepage/homepage)是一套 GPL-3.0 授權、GitHub 33,478 星的自架儀表板:一個 Docker 容器、五個 YAML 檔,就能把 Uptime Kuma、Jellyfin、Paperless、Beszel 這些散在十幾個 port 的服務收成一頁,還能在卡片上直接顯示即時狀態。 本文用 v2.7.0 實測安裝,附繁體中文介面設定、Docker 整合、MCP 端點,以及兩個會讓你卡半小時的坑。
為什麼需要一個「首頁」
自架玩半年之後,你一定遇過這個畫面:瀏覽器書籤列有 20 個分頁,Portainer 在 9443、Uptime Kuma 在 3001、Jellyfin 在 8096、Paperless 在 8000、Beszel 在 8090,還有一堆你上個月才架好、現在已經忘了 port 的服務。
| 沒有儀表板的痛點 | Homepage 怎麼解 |
|---|---|
| 記不住 port 與網域 | 一頁列出所有服務,點卡片就進去 |
| 不知道服務現在活著沒有 | 卡片上直接顯示 running/健康狀態、API 回傳的即時數字 |
| 每次都要一個一個登入確認 | widget 幫你抓狀態、佇列數、下載速度、資源用量 |
| 新加的服務沒人知道 | 用 Docker label 自動發現,容器起來就自動出現在儀表板 |
| 首頁想要好看一點 | 12 種主題色、多種卡片樣式、背景圖、PWA 支援 |
它跟 Uptime Kuma、Grafana 的定位完全不同:那兩套回答「有沒有壞」,Homepage 回答「東西在哪、現在怎樣」。三者可以並存,Homepage 當門面,監控系統當後台。
一表看懂:規格與現況
| 項目 | 內容 |
|---|---|
| 專案 | gethomepage/homepage |
| GitHub 星數 | 33,478(2026-11-01 以 GitHub API 查詢) |
| Fork 數 | 2,159 |
| 授權 | GPL-3.0 |
| 建立時間 | 2022-08-24 |
| 最新版本 | v2.7.0(2026-10-23 發布) |
| 技術棧 | Next.js(容器內為 16.4.1)+ Node.js |
| 服務整合(widget) | 官方文件收錄 165 種 service widget(docs/widgets/services/ 檔案數) |
| 資訊整合(info widget) | 14 種(資源、搜尋、日期、天氣、股市、Kubernetes…) |
| 容器記憶體 | 實測閒置時 158.7 MiB(docker stats,本機單一容器) |
| 設定方式 | 五個 YAML 檔(settings/services/widgets/bookmarks/docker) |
| 介面語言 | 內建 zh-Hant(繁體中文) |
| 額外能力 | Docker 自動發現、Kubernetes、Proxmox、MCP 端點、custom.css/js |
一句話定位:它是「自架服務的門面」,不是監控系統、不是反向代理、也不是密碼管理器。
Docker Compose 安裝(官方樣板+繁中設定)
官方 Docker 安裝頁的 compose 只有 9 行,這裡直接沿用並補上一個容易漏掉的環境變數:
services:
homepage:
image: ghcr.io/gethomepage/homepage:latest
container_name: homepage
ports:
- 3000:3000
volumes:
- /path/to/config:/app/config # 本機設定目錄,要先建好
- /var/run/docker.sock:/var/run/docker.sock:ro # 選用:Docker 整合
environment:
HOMEPAGE_ALLOWED_HOSTS: gethomepage.dev # 必要,可含 port
restart: unless-stopped
啟動:
mkdir -p /opt/homepage/config
docker compose up -d
docker logs -f homepage # 看到 Ready 就可以開 http://你的IP:3000
實測(本機 Docker 29.2.1/Compose v5.1.0):容器啟動約 3 秒進入 healthy,首頁回應 HTTP 200、HTML 約 66 KB,/api/healthcheck 回 up。第一次啟動時 Homepage 會自動把缺少的 kubernetes.yaml 補進設定目錄,日誌會印 kubernetes.yaml was copied to the config folder——不是錯誤,不用理它。

上面這張是本機實測畫面:介面已是繁體中文,頂部資源列顯示 CPU/記憶體/磁碟/開機時間,右下角版本 v2.7.0 (2e5f101, 2026年10月23日)。「本頁容器」卡片右上角的綠色「健康」標籤來自 Docker 整合;Uptime Kuma 那張紅色的「API 錯誤 資訊」則是因為測試機上沒有跑 3001 埠的服務——這正好示範了 widget 失敗時的樣子,後面會教你怎麼查。
五個設定檔的分工
Homepage 的設定全部在 /app/config 底下,各檔職責分明:
| 檔案 | 負責什麼 | 內容形式 |
|---|---|---|
settings.yaml | 站名、語言、主題、版面配置、是否讓搜尋引擎索引 | 單一 mapping |
services.yaml | 服務分組與服務卡片(href、icon、description、Docker 對應、widget) | 最外層是陣列 |
widgets.yaml | 頁面上方的資訊列(資源、搜尋、日期、天氣) | 陣列 |
bookmarks.yaml | 書籤區(純連結,不帶狀態) | 陣列 |
docker.yaml | Docker 連線設定(socket 或 HTTP API) | mapping |
kubernetes.yaml | Kubernetes 整合(沒有用就留空或別放) | mapping |
⚠️ 最常見的低級錯誤:services.yaml、widgets.yaml、bookmarks.yaml 的外層是陣列(每行以 - 開頭),settings.yaml、docker.yaml 是單一 mapping(不用 -)。兩種格式互換就會整頁空白。
實測設定:繁中、四組服務、資訊列
settings.yaml(實測可用):
title: 龍潭機房 Dashboard
description: 自架服務總覽
language: zh-Hant # 繁體中文;zh-CN 會被自動對應成 zh-Hans
theme: dark
color: slate
headerStyle: clean
disableIndexing: true # 不讓搜尋引擎索引
target: _blank
layout:
監控與通知:
style: row
columns: 4
多媒體:
style: row
columns: 3
services.yaml 片段(實測可用,注意縮排:group → service → 屬性 各進兩格):
- 監控與通知:
- Uptime Kuma:
icon: uptime-kuma.png
href: http://127.0.0.1:3001/
description: 站台存活監控
siteMonitor: http://127.0.0.1:3001/
widget:
type: uptimekuma
url: http://127.0.0.1:3001
slug: main
- Beszel:
icon: beszel.png
href: http://127.0.0.1:8090/
description: 主機資源監控
widgets.yaml(頁面上方那一排):
- resources:
cpu: true
memory: true
disk: /
uptime: true
cpus: true
- search:
provider: duckduckgo
target: _blank
- greeting:
text_size: xl
text: 龍潭機房
- datetime:
text_size: xl
format:
dateStyle: long
timeStyle: short
hour12: false
改完存檔不用重啟,重整瀏覽器就會生效(Homepage 每次請求都會重讀設定)。要驗證設定有沒有被讀進去,不用開瀏覽器:
curl -s http://127.0.0.1:3000/api/services | head -c 500
curl -s http://127.0.0.1:3000/api/widgets | head -c 300
這兩個端點會回傳解析後的 JSON,服務名稱、icon、description、widget 清單全在裡面。設定有問題時,這裡比畫面更容易看出來(下一節會用到)。
Docker 整合:卡片上直接看容器狀態
這是最值得開的功能。兩個步驟:
第一步,把 socket 掛進容器(唯讀),並在 docker.yaml 指定:
my-docker:
socket: /var/run/docker.sock
第二步,在服務裡指定 server 與 container:
- 本頁容器:
icon: homepage.png
href: http://127.0.0.1:3999/
server: my-docker
container: hp-test
驗證方式(實測輸出):
$ curl -s "http://127.0.0.1:3999/api/docker/statuses?server=my-docker"
{"statuses":{"hp-test":{"status":"running","health":"healthy"}}}
畫面就會出現綠色「健康」標籤,點標籤展開還能看到 CPU/記憶體/網路用量。
安全提醒:把 /var/run/docker.sock 掛進容器等於給容器「看你 Docker 的能力」。官方文件明確建議用 socket proxy 取代直掛:
dockerproxy:
image: ghcr.io/tecnativa/docker-socket-proxy:latest
environment:
- CONTAINERS=1
- SERVICES=1
- POST=0 # 禁止任何寫入操作
ports:
- 127.0.0.1:2375:2375
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
然後 docker.yaml 改成 host: dockerproxy / port: 2375 即可。直掛 socket 時官方也提醒:容器必須以 root 執行。
自動發現:用 Docker label 就不用寫 services.yaml
emby:
image: lscr.io/linuxserver/emby:latest
labels:
- homepage.group=Media
- homepage.name=Emby
- homepage.icon=emby.png
- homepage.href=http://emby.home/
- homepage.description=Media server
容器一起來就自動出現在儀表板,連 server/container 都不用填。注意:用 Docker Swarm 時要改寫在 deploy.labels 底下。
兩個實測踩到的坑(會讓你懷疑人生)
坑一:HOMEPAGE_ALLOWED_HOSTS 沒設好 → 整站 400
Homepage 會驗證請求的 Host 標頭,不符就回:
HTTP 400
{"error":"Host validation failed. See logs for more details."}
容器日誌同時給提示:
error: Host validation failed for: evil.example.com.
Hint: Set the HOMEPAGE_ALLOWED_HOSTS environment variable to allow requests from this host / port.
重點在**「host / port 要完全一致」。實測時只把 localhost:3998 放進白名單,結果用 127.0.0.1:3998 連線一樣被擋——因為那被視為不同主機名。所以正式環境要把所有會被用來連線的形式**都列進去:
environment:
HOMEPAGE_ALLOWED_HOSTS: "home.example.com,192.168.1.10:3000,localhost:3000,127.0.0.1:3000"
這也是為什麼很多人「本機可以開、從別台電腦連就 400」——不是防火牆,是白名單。反向代理後面記得放對外網域那一個。
坑二:YAML 縮排錯一格 → widget 無聲消失,不會報錯
實測把 widget 的位置寫錯(少一格縮排),結果:
- 頁面沒有錯誤訊息,服務卡片正常出現
- 但卡片上沒有 widget,看起來就像「這個服務不支援 widget」
用 API 一看就知道:
$ curl -s .../api/services
[{"name":"監控","services":[{"name":"Uptime Kuma","href":"...","widgets":[]}]}]
widgets 是空陣列——它被 YAML 解析成「另一個鍵」,而不是 widget 設定。更麻煩的是 Homepage 內建的 MCP 驗證工具也只驗 YAML 語法,這種「語法合法、結構錯誤」的情況會回 {"valid": true}。
| 現象 | 可能原因 | 怎麼查 |
|---|---|---|
| 整頁空白 | 外層該用陣列的檔案寫成 mapping(或反之) | curl /api/services 看有沒有 JSON |
| 服務出現但沒有 widget | 縮排層級錯、widget: 與 icon: 不同層 | 檢查 widgets 是否為空陣列 |
| 卡片顯示紅色「API 錯誤」 | widget 連不到目標服務或金鑰錯 | docker logs homepage 會印出實際打的 URL |
| Docker 狀態不出現 | 沒掛 socket、docker.yaml 沒設、server 名字不符 | 日誌出現 connect ENOENT /var/run/docker.sock |
| 整站 400 | HOMEPAGE_ALLOWED_HOSTS 沒含這個 host:port | 日誌有 Host validation failed |
| 圖示空白 | icon 名稱不在 dashboard-icons 清單,或容器沒有外網 | 改用 mdi-*/si-* 前綴或本地 /icons/x.png |
實測 widget 失敗時日誌長這樣(順帶揭露 widget 的實際呼叫路徑):
error: <httpProxy> Error calling http://127.0.0.1:3001/api//main...
Error: connect ECONNREFUSED 127.0.0.1:3001
前端則是 HTTP 500 加上 ECONNREFUSED 的錯誤物件。想讓使用者不要看到紅色錯誤框,可以在 settings.yaml 設 hideErrors: true 或單一 widget 設 hideErrors: true——但除錯階段千萬不要開,它會把唯一的線索藏起來。
進階:讓 AI 幫你改設定(MCP 端點)
v2.x 之後 Homepage 內建一個輕量的 MCP(Model Context Protocol)端點,預設關閉。啟用方式:
environment:
HOMEPAGE_MCP_ENABLED: "true"
HOMEPAGE_MCP_TOKEN: "用 openssl rand -base64 32 產生的 32 字元以上字串"
端點在 /api/mcp。實測結果:
$ curl -s http://127.0.0.1:3999/api/mcp \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $TOKEN" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '.result.tools[].name'
list_config_files
read_config_file
validate_config_file
write_config_file
add_service
add_info_widget
homepage_docs
七個工具、沒有 token 一律 401 Unauthorized。預設唯讀:呼叫 write_config_file 會回 Writing is disabled. Set HOMEPAGE_MCP_ALLOW_WRITE=true to enable MCP config edits.;要寫入必須另外開 HOMEPAGE_MCP_ALLOW_WRITE=true,而且寫入前會驗 YAML 語法(實測用 tab 縮排會回 tab characters must not be used in indentation (2:1) 並附行列位置)。
但請注意風險:官方文件寫得很明白——這個 token 等於「讀取所有設定的服務憑證」的權限,開啟寫入後還能改 custom.js(會在每個瀏覽器裡執行)。所以:只在內網開、務必配 TLS 與反向代理、token 至少 32 字元。MCP 官方也說 Claude Desktop 的「Add custom connector」不適用(它走 OAuth、無法帶自訂標頭),要改用 mcp-remote 加 X-Homepage-MCP-Token。
安全與治理檢查清單
| 項目 | 建議做法 |
|---|---|
| 對外曝露 | 預設只聽內網,要對外一律走反向代理+驗證(Authelia/Cloudflare Access) |
| 搜尋引擎 | disableIndexing: true(實測會讓 /robots.txt 變成 Disallow: /) |
| Docker socket | 優先 socket proxy;直掛也要 :ro,並理解容器需 root |
| 敏感值 | 用 HOMEPAGE_VAR_XXX 環境變數+設定檔寫 {{HOMEPAGE_VAR_XXX}},別把 API key 寫進 YAML |
| 檔案權限 | 用 PUID/PGID 以非 root 執行,並確認 config 目錄擁有者正確 |
| MCP | 預設關閉;要開就配 token+TLS,寫入權限另外評估 |
| 更新 | 官方建議看 release notes 再升版;本教學用 v2.7.0 固定 tag 較好追 |
金鑰不要寫死在 YAML,做法是:
# services.yaml
widget:
type: jellyfin
url: http://jellyfin:8096
key: "{{HOMEPAGE_VAR_JELLYFIN_KEY}}"
# compose
environment:
HOMEPAGE_VAR_JELLYFIN_KEY: "你的金鑰"
已知限制/沒做的事
誠實列出這篇的邊界,避免讀者以為什麼都驗過了:
- 沒有實測 165 種 widget:只實測了
uptimekuma(失敗情境)與 Docker 狀態 API,其餘 widget 的參數請以官方文件為準。 - 沒有接真實的 Jellyfin/Paperless 等後端:截圖中的服務是「連不到」的示範,紅色錯誤框就是實測證據;這些整合的實際數字沒有驗證。
- 沒有測試 Kubernetes、Proxmox 整合:只確認
kubernetes.yaml會被自動建立、相關端點存在。 - 記憶體 158.7 MiB 是閒置值:
docker stats單次取樣,掛上大量 widget 後會更高。 - 沒有做 Nginx/Traefik/Cloudflare Tunnel 的反向代理實測:只提醒
HOMEPAGE_ALLOWED_HOSTS要放對外網域;這部分請搭配本站的反向代理教學。 - STAR 數與版本會變:以 2026-11-01 查詢與
v2.7.0為準。 - MCP 端點只測了
tools/list、read_config_file、validate_config_file、write_config_file(被拒):沒有實測add_service寫入成功的情境。
誰適合、誰不需要
| 你的情況 | 建議 |
|---|---|
| 有 5 個以上自架服務、常忘記 port | 非常適合,設定 30 分鐘內完成 |
| 想要一頁顯示服務健康狀態 | 適合,但別取代 Uptime Kuma(告警仍要靠它) |
| 只有 1~2 個服務 | 不需要,瀏覽器書籤就好 |
| 想找「有登入驗證的入口」 | Homepage 有自己的 auth(HOMEPAGE_AUTH_ENABLED),但多數人會直接放反向代理後面 |
| 需要多節點、多使用者權限 | 不適合,它不是 CMDB 也不是監控平台 |
站內延伸閱讀
- Uptime Kuma 完整教學 2026:自架監控與 Telegram 通知
- Beszel 完整教學 2026:11 MB 記憶體的輕量伺服器監控
- Grafana 完整教學 2026:Prometheus+Loki 自架監控堆疊
- Cloudflare Tunnel 完整教學 2026:不開防火牆 port 也能對外
- Traefik 完整教學 2026:Docker Labels 自動路由與自動 HTTPS
- Portainer 完整教學 2026:Docker 圖形化管理與自架實戰
本文基於 2026-11-01 的 gethomepage/homepage v2.7.0 實機測試;文中沒有聯盟連結,也不構成任何商業推薦。
