- 按設定檔和角色組織 Docker Compose 可以簡化擁有數十個服務的家庭實驗室的管理。
- 將配置集中在 .env 檔案中,使用覆蓋規則,並在 Git 中進行版本控制,使得環境具有可移植性,易於遷移。
- 專用網路、Traefik 和健康檢查提高了服務的安全性、隔離性和韌性。
- 監控、受控日誌和自動備份使家庭實驗室成為一個長期穩定的平台。

使用容器搭建現代化家庭實驗室已成為許多科技愛好者的熱門嗜好。 Docker Compose 幾乎總是此類搭建的核心:用 YAML 定義服務,用 Git 進行版本控制,然後只需一條命令即可啟動整個環境。
然而,當你的業務開始成長時,情況就不同了:你會從兩三個容器發展到幾十個服務、內部網路、反向代理、資料庫和持續整合運行器。這時,一個重要的問題就出現了:是使用一個龐大的 Docker Compose 實例,還是使用許多小檔案?如何組織設定檔、網路、備份、安全設置,以及如何讓遷移變得輕鬆便捷?
在家庭實驗室中實際部署 Docker Compose 的方法

實際上,長期使用 Homelabs 的使用者通常會採用三種不同的 Compose 組織模型,每種模型都有其自身的優缺點。選擇合適的方法可以在擴展規模或遷移到新機器時省去很多麻煩。
一方面,有些人最初使用獨立的 Docker Run 指令,然後轉向 Portainer,最後使用 Docker Compose。這是一個典型的場景:Portainer 提供了出色的可視性、用戶友好的介面、模板等等,但最終,如果沒有文件記錄,編輯複雜的參數或遷移配置就會變得非常麻煩。
與之相反的極端情況是,有人將所有內容整合到一個「超級」docker-compose.yml檔案中,該檔案能夠運行所有家庭實驗室服務:反向代理、媒體、實用程式、監控、LLM、資料庫…所有這些都在一個堆疊中。
介於兩者之間,許多用戶堅持採用混合方法:幾個按上下文(例如,媒體、基礎設施、生產力、監控)分組的小型 docker-compose.yml 文件,所有這些文件都在同一個存儲庫下,並且通常共享全局環境變量。
一個相當巧妙的解決方案融合了兩種方式:一個包含其他檔案的「根」docker-compose檔案(每個檔案都位於apps或services的子資料夾中)。這樣,你既可以維護家庭實驗室的全域視圖,又無需忍受難以閱讀的上千行YAML檔案。
使用者畫像、按功能分組以及大型家庭實驗室

當你的家庭實驗室服務數量接近30、40 甚至 50 個(包括資料庫、快取或索引器等備份服務)時,對它們進行整理就顯得至關重要。這時,按功能分組和使用Docker Compose 配置就派上用場了。
一個非常常見的做法是將所有內容歸入一個 Compose「專案」中,但按設定檔進行邏輯劃分。例如:
- 核心概況:家庭實驗室核心,使用 Traefik 作為反向代理,並使用身分識別提供者(例如 OAuth 或 Authentik)對同一網域下的所有應用程式進行 HTTPS 驗證。
- 媒體簡介Plex、Sonarr、Radarr、Ombi、SABnzbd 或 qBittorrent 等服務負責整理、下載和提供多媒體內容。
- 實用工具簡介使用 Portainer、Watchtower(如果使用)、Diun、dockcheck 或類似工具來管理和監控容器和更新。
- 基礎設施/監控概況Traefik、cAdvisor、Prometheus、Grafana、Uptime Kuma、Dozzle 以及所有與監控和日誌記錄相關的工具。
- 實驗概況或LLM:LLM 或特殊應用程式(ChatGPT Next Web local、LibreOffice Online 等)的特定堆疊,通常預設是禁用的。
配置方案的優點在於,您可以根據需要僅部署部分基礎架構。例如,您可以在低功耗迷你電腦上運行核心+基礎架構配置方案,而僅在配備更多磁碟和GPU的大型伺服器上部署媒體配置方案。
在設計良好的程式碼倉庫中,根目錄通常會有一個「主」docker-compose.yml文件,它使用include指令將各個文件推送到apps/或services/資料夾中。此外,幾乎所有服務都透過一個全域的.env檔案進行配置,有些金鑰則儲存在secrets/目錄中,這大大簡化了初始設定。
依照這個模式,管理家庭實驗室基本上就簡化為編輯 .env 檔案和金鑰、啟用或停用設定文件,以及決定在每台主機上啟動哪些服務。如果您打算在多台機器上部署同一套應用程序,這非常理想。
一個巨大的 docker-compose 檔案 vs. 多個小文件

這是一個永恆的爭論:是用一個包含所有內容的 docker-compose.yml 文件,還是為每個服務/堆疊創建多個文件?真正的答案通常是「這取決於你優先考慮的是什麼:是遷移的簡易性,還是每個服務的清晰度」。
提倡使用單一主文件的人通常會強調以下幾個優點:
- 遷移主機非常簡單你只需克隆倉庫,複製 .env 檔案和密鑰,掛載卷,然後運行 `docker compose up -d` 即可。無需逐個目錄操作。
- 基礎設施即真理準則家庭實驗室的整個拓樸結構(服務、網路、磁碟區、依賴關係)都在一個地方。
- 集中更新如果您更改了映像版本、重新啟動策略或某些日誌記錄,您就知道要在哪裡進行修改。
但它也有明顯的缺點:龐大的 YAML 檔案更難維護,合併衝突也會增加,而且在調試特定問題時,你會發現自己要面對數百行程式碼的龐然大物。當一切都變得過於龐大時,感到些許後悔也是人之常情。
另一種方法是為每個應用程式或每個邏輯堆疊建立一個 docker-compose.yml 文件,結構如下:
docker/
├── bookstack/
│ └── docker-compose.yml
├── dashy/
│ └── docker-compose.yml
└── traefik/
└── docker-compose.yml
這樣,每個容器都會被命名為類似bookstack-app-1或traefik-reverse-proxy-1的名稱,這有助於您快速定位問題:如果 bookstack-app-1 容器崩潰,您就知道應該在哪個資料夾中找到。
從視覺上看,它更加簡潔,並且允許您獨立管理每個服務(啟動、停止或更新單一服務而不會影響其他服務)。此外,像 Dozzle 這樣的應用程式利用獨立的堆疊來更好地組織日誌。
缺點是,如果將所有內容分離得太厲害,則通用服務(如 Traefik 或共享網路)之間的協調需要更加謹慎:您必須聲明外部網路、特定的 Traefik 標籤,並記住其他 docker-compose 創建的網路的命名規則。
關於 .env、覆蓋和版本控制的最佳實踐
最容易被低估的技巧之一是將配置集中在 .env 檔案中。與其在 docker-compose.yml 檔案中堆砌環境變量,不如像這樣定義:
DB_USERNAME=myuser DB_PASSWORD=secretpassword
然後在 YAML 檔案中,它們被引用為${DB_USERNAME}或${DB_PASSWORD}。這使得 Compose 程式碼一目了然,允許你在多個服務之間共享變量,最重要的是,它將密碼儲存在一個單獨的檔案中(你可以將其從 Git 中排除)。
對於不同的環境(生產環境、測試環境、開發環境),利用 docker-compose.override.yml 檔案非常有用。其想法是創建一個基礎的 docker-compose.yml 文件,然後在 override 文件中僅覆蓋需要更改的內容:連接埠、路徑、調試標誌等。
例如,在開發過程中,您可以載入一個覆蓋配置,該配置會暴露不同的連接埠、啟用偵錯並掛載本機原始碼。您無需修改主 YAML 文件,只需根據運行環境調整堆疊即可。
顯然,如果你想讓你的家庭實驗室達到哪怕只有一點點專業水準,使用 Git 進行版本控制是必不可少的。你通常會得到類似這樣的配置:
homelab-docker/ ├── docker-compose.yml ├── .env.example ├── services/ │ ├── media/ │ ├── infra/ │ └── ... └── scripts/
接下來,您可以初始化程式碼倉庫,提交基礎架構變更,如果出現問題,只需幾秒鐘即可回滾到先前的 Compose 版本。對於雄心勃勃的家庭實驗室來說,這不僅是一個選項,更是避免崩潰的唯一途徑。
網路、Traefik 和安全服務暴露
幾乎所有中等程度的家庭實驗室都採用相同的組合:Traefik 作為反向代理,以及集中式身分提供者(Auth 或 Authentik)。這使得可以透過 HTTPS 和 SSO 在子網域下公開多個應用程式。
一個經典的方法是建立一個專用的 Docker 網絡,例如reverse_proxy或類似網絡,將 Traefik 和所有需要對外提供的 Web 服務連接到該網絡。其餘容器(資料庫、快取等)則保留在隔離的內部網路中。
如果您使用 Traefik 並將服務分割到不同的 Docker Compose 實例中,則需要定義一個共用的外部網路。例如:
services:
bookstack:
image: lscr.io/linuxserver/bookstack
networks:
- traefik-net
labels:
- "traefik.docker.network=traefik_default"
networks:
traefik-net:
name: traefik_default
external: true
在這裡,traefik_default 網路由 Traefik 協定堆疊創建,其他服務透過名為 traefik-net 的外部網路添加到該網路。標籤告訴 Traefik使用哪個網路來路由流量。
當單一堆疊包含後端服務(例如,Web 容器及其資料庫)時,您可以將它們連接到共用的預設網絡,並僅授予 Web 容器對 Traefik 網路的存取權。資料庫將被標記為 `traefik.enable=false`,以便 Traefik 忽略它。
這種架構具有兩大優點:服務隔離和可控暴露。只有使用 Traefik 標籤標記且位於代理網路上的容器才能從外部存取。
資料持久性、磁碟區和磁碟結構
沒有持久化資料的家庭實驗室幾乎毫無用處:資料庫、配置、媒體、文件…所有東西都必須在 Docker Compose 崩潰後仍然存在。捲和綁定掛載就是你的生命線。
許多人使用類似這樣的結構來整理儲物空間:
/mnt/storage/
├── downloads/
│ ├── movies/
│ └── tv/
├── media/
│ ├── movies/
│ ├── tv/
│ └── music/
└── srv/
└──
其理念是,下載器(qBittorrent、SABnzbd 等)只能看到下載資料夾,像 Radarr/Sonarr 這樣的管理器可以存取下載內容和媒體檔案(用於移動/建立硬連結),而像 Plex 或 Jellyfin 這樣的伺服器只能看到媒體資料夾。
這樣就應用了最小權限原則:每個容器只能存取它實際需要的資源。這種清晰的劃分也有助於決定將哪些磁碟區或路徑備份到雲端或外部磁碟機。
srv 目錄通常用於儲存應用程式配置(例如,/srv/jellyfin/config、/srv/traefik、/srv/paperless 等)。目錄通常只包含部分版本控制的內容(模板、Caddyfile 等),而不會包含任何關鍵或資源密集的內容。
在某些情況下,在下載鏈中使用硬連結非常有用:像 Radarr 或 Sonarr 這樣的服務可以將下載的檔案連結起來,從而在不佔用重複磁碟空間的情況下保持做種。像 TRaSHGuides 這樣的指南所推薦的目錄結構正是基於這項原則。
使用 GitHub Actions 和本地運行器實現自動化部署
如果您想更進一步,可以使用 CI/CD 實現家庭實驗室更新的自動化。一些用戶已經用 GitHub Actions 和託管在家庭實驗室內部的運行器建立的工作流程,取代了 Jenkins 和類似的工具。
機制很簡單:每次向家庭實驗室倉庫的主分支推送程式碼時,都會啟動一個 GitHub Actions 工作流程,該工作流程會執行測試、程式碼檢查,如果一切順利,也會將變更部署到伺服器。
典型的工作流程包括以下步驟:
- Gitleaks 型秘密掃描器:萬一您不小心將密碼或令牌上傳到了程式碼庫。
- 林亭 為了保持 YAML 或基礎設施程式碼的可讀性和一致性,需要進行修改。
- 在家庭實驗室內部更新儲存庫在目標伺服器上執行 git pull 指令。
- 受控的容器再造停止舊進程,啟動新進程並檢查狀態。
優點:安全性更高(您可以控制金鑰外洩)、程式碼品質更佳,只需一次推送即可重複部署。由於您使用的是本機運作器,因此鏡像和磁碟區不會離開您的網路;您只需利用 GitHub 介面即可視覺化管道。
為什麼 Docker Compose 能讓家庭實驗室的搭建變得如此輕鬆?
許多人多年來一直依賴Docker Run 和 Portainer,直到發生故障或需要遷移時,他們才被迫重新評估他們的方法。當主機遺失或必須將服務遷移到另一台機器時,僅依賴 Portainer 內部的獨立命令或配置是一個陷阱。
切換到 Compose 後最大的區別在於,整個服務定義都變成了文字:磁碟區、連接埠、網路、標籤、變數…所有內容都包含在一個 YAML 檔案中,您可以複製、共用、版本控制和重複使用該檔案。
編輯服務不再是「手動重建容器」;現在只需修改檔案中的一行,儲存,然後執行 `docker compose up -d`即可。您無需記住原始指令,也無需點擊多個 Portainer 畫面。
此外,如果您使用多台伺服器(迷你電腦、NAS、桌上型電腦),能夠將同一個 Compose 檔案複製到另一台機器,調整四個路徑,並在不同的硬體上運行相同的堆疊,這將非常方便。事實上,許多人都承認,在經歷了資料遺失或混亂的遷移之後,Compose 為他們節省了大量後續時間。
此外,從舊服務建立新服務也變得非常簡單:例如,如果透過複製 YAML 區塊來複製 Plex 配置以重複使用相同的媒體路徑和轉碼裝置來設定 Jellyfin,則只需幾分鐘。
最佳化:建構上下文、多階段建置和資源
雖然許多 Homelab 容器都來自公共鏡像,但在某些情況下,您需要自行編譯鏡像。在這種情況下,管理建置環境至關重要:不要上傳整個未經篩選的倉庫,而是將建置範圍限制在專案資料夾內(使用嚴格的 `.dockerignore` 指令),以確保建置快速且輕量級。
另一個非常有用的技巧是在 Dockerfile 中使用多階段建置:第一階段安裝依賴項並編譯,第二階段僅將必要的建置產物複製到一個小型基礎映像中。這樣一來,最終生成的鏡像體積更小、更安全,因為它們不會攜帶不必要的工具鏈或庫。
在 Compose 端,您可以設定CPU 和記憶體限制(尤其是在 Swarm 環境或 Docker 支援這些參數的情況下),以防止資源密集型應用程式佔用過多資源。在 Homelabs 中,這有助於防止配置錯誤的服務拖垮系統的其他部分。
不要忘記重新啟動策略(重新啟動:始終、除非停止、故障時):透過這些策略,您可以確保關鍵服務(反向代理、VPN、金鑰資料庫)在重新啟動或一次性故障後自動重新啟動。
最後,建議使用諸如docker image prune、docker container prune 和 docker volume prune之類的命令來安排定期清理任務,以刪除舊建置、已停止的容器或孤立磁碟區的殘留文件,從而回收磁碟空間。
健康服務、記錄和監測
為了防止你的家庭實驗室變成一個黑盒,需要專注於三個關鍵方面:健康檢查、受控日誌記錄和監控。 Docker Compose 允許你為每個服務聲明健康檢查(使用類似 `curl -f http://localhost` 的命令或特定的腳本),以確定容器是否健康。
這樣可以確保只有「健康」的容器才能接收流量(例如,透過 Traefik),並且如果容器停止回應,則會根據配置的策略重新啟動它們。這可以以最小的努力顯著提高系統的彈性。
關於日誌,調整 json-file 驅動程式的max-size 和 max-file限制可以防止磁碟被大量遺忘的日誌佔滿。像 Dozzle 這樣的 Web 工具可以幫助你透過瀏覽器瀏覽所有容器的日誌,這對於偵錯特定服務非常方便。
對於指標和持續監控,經典的組合是cAdvisor + Prometheus + Grafana。 cAdvisor會顯示每個容器的 CPU、記憶體、磁碟和網路使用情況統計資料;Prometheus 會定期收集這些資訊;Grafana 則會在美觀的儀表板中展示這些訊息,並在出現任何異常峰值時發出警報。
一個配置完善的家庭實驗室通常會配備Uptime Kuma 用於可用性檢查(HTTP、ICMP、TCP 等),以及像 Duplicati 這樣的自動化備份系統,用於將關鍵資料複製到其他磁碟或雲端。這樣,您就能隨時掌握系統運作狀況,即使出現問題,也不會遺失重要資料。
家庭實驗室的安全性和遠端訪問
然而,無論採用何種 DIY 設定方式,安全性都不可或缺。許多人選擇不將 NAS 或其服務直接暴露給外部世界,而是透過 VPN 限制遠端存取(WireGuard 因其效能和易用性而成為非常受歡迎的選擇)。
在這種模式下,路由器充當網關:僅對 VPN 伺服器開放一個隨機端口,連接成功後,所有對內部服務的請求都會透過加密隧道傳輸。未經此預先過濾,Traefik 和應用程式都不會暴露在網路上。
有些用戶不願意自行管理 VPN,因此會選擇Cloudflare Tunnel 或 Tailscale 等服務來存取他們的家庭實驗室,而無需開放連接埠。這些都是便捷的替代方案,但如果隱私是您的首要考慮因素,則需要考慮這些第三方服務可能會收集哪些元資料。
另一個好做法是對伺服器和NAS磁碟進行加密,定期安裝補丁,並限制自動更新(許多人避免使用Watchtower,而選擇手動控制更新)。與其因為未檢查的更新而導致家庭實驗室一半的系統崩潰,不如稍微落後一些,但要有控制權。
如您所見,您無需達到「企業級」水平,但建議建立最低限度的安全性和紀律性,以免您的家庭實驗室成為漏洞或持續的恐慌源。
最終,使用 Docker Compose 建立一個像樣的家庭實驗室需要組織性、常識和勇於嘗試的精神:如果你將服務分組、正確定義網絡、在 Git 中記錄並進行一些自動化,最終你會得到一個可以用單個命令啟動、輕鬆遷移到另一台機器、並逐步擴展而不會變成難以控制的混亂環境。