Docker Compose 實際伺服器常用指令速查
整理 Docker Compose V2 伺服器日常約 12 個指令,涵蓋啟停、套用變更、logs、shell、network、volume 與安全清理,並提醒常見陷阱。
實際上會使用的 Compose 指令
Docker Compose 提供數十個子指令。伺服器上的日常工作通常只會用到約 12 個。本頁依工作目的整理這些指令,為每個指令說明一個明確用途,並在指令可能隱藏問題時提供深入說明的連結。
本頁所有內容都使用 Compose V2 或 V5:docker compose 中間有一個空格,而不是舊版 docker-compose script。兩者都是 Go plugin;在 Linux 上,Compose plugin 必須與 Docker Engine 分開安裝。Ubuntu 24.04 LTS 的 universe repository 仍提供舊版 Python V1 工具,套件名稱為 docker-compose,版本為 1.29.2-6ubuntu1。使用 docker compose version 檢查。如果找不到該指令,請設定 Docker 的 repository,並安裝 docker-compose-plugin 套件。
以下每個指令都會從存放 compose.yaml 的目錄執行;Compose 會將該目錄名稱作為預設 project name,除非另行覆寫。若未指定 -f 或 COMPOSE_FILE,Compose 會在目前工作目錄及其父目錄中搜尋設定檔。因此,從上一層目錄執行時,只有在設定檔仍位於這個搜尋路徑中,才能找到你的 stack。如果你不熟悉該檔案格式,請先閱讀 VPS 上的第一個 Compose 檔案,再回到本頁查看相關指令。
生命週期:你會輸入的 4 個命令,以及會移除容器的命令
docker compose up -d
docker compose up -d --wait
docker compose stop
docker compose start
docker compose restart web
docker compose downup -d 會建立 network、建立 containers、啟動 containers,然後返回。它會在啟動 containers 後立即返回,不會等待應用程式就緒。因此,部署指令碼若接著執行 curl 探測,第一次通常會失敗。up -d --wait 會等待具有 healthcheck 的 services 變為 healthy,並等待沒有 healthcheck 的 services 開始執行;若有 service 不健康或 --wait-timeout 逾時,命令會以非零狀態碼結束。這個 flag 的效果取決於背後的檢查,因此在自動化流程中依賴它之前,請先撰寫 Compose 可信任的 healthcheck。
stop 會停止 containers,但保留它們,因此 start 能以相同的可寫入層重新啟動相同的 containers。down 會停止 containers,接著移除 containers 與 project network。寫入 container 內部且不在 volume 中的任何內容,都會隨之消失。這是 Compose 中代價最高的誤解;down 與 stop 的完整差異說明了這項差異會在哪些情況造成影響。
restart 不是重新載入。它會停止並啟動同一個 container,使用的仍是該 container 目前已有的設定,因此變更後的環境變數、新的 image tag 或修改後的 port mapping 完全不會生效。若要套用檔案變更,請再次執行 up -d。Compose 會將每個 service 與其正在執行的 container 進行比較,並重新建立設定或 image 已變更的 containers。
套用變更:重新建立、拉取或重建
docker compose up -d --force-recreate
docker compose pull && docker compose up -d
docker compose build --no-cache web
docker compose up -d --build webup -d 會在容器設定與映像檔都未變更時重用執行中的容器;映像檔的拉取與建置則依照服務的 pull policy 執行。--force-recreate會略過這項比對,即使設定完全相同,也會取代每個容器,因此是清除容器內異常狀態最快的方法。
明確更新映像檔的流程會使用兩個命令,因為兩者用途不同。pull會下載檔案中每個標籤目前對應的映像檔。接著,up -d會發現服務的映像檔 ID 已不再符合執行中容器的 ID,並重新建立容器。使用預設的 missing pull policy 時,系統會重用快取中的標籤,但 latest除外,該標籤一定會重新拉取。相反的風險會出現在多服務 stack:一次為每個服務拉取 latest,可能讓原本 10 秒前仍正常運作的應用程式失效。因此,自架 AFFiNE 工作區建議改用釘選方式,讓應用程式映像檔與 migration 映像檔使用相同的 digest。釘選也會讓升級變成有意識地修改標籤,接著執行相同的拉取與重新建立流程。若 stack 在啟動時會遷移資料庫,則應在執行任一命令前先準備好 dump;自架 Chatwoot 支援服務台每次升級版本時都採用這項流程。
build適用於宣告 build:區段的服務,包括同時宣告 image:的服務。up -d --build會在單一步驟中完成建置並啟動,這是修改程式碼時的標準迴圈。只有在明確確認快取層已過時時,才使用 --no-cache,因為它會從頭重建每一層。有些專案會從同一個複製的儲存庫提供這兩種路徑,此時兩個迴圈就是可替代的更新方式:自架 openGym 運動追蹤器使用 docker compose up -d --build建置釘選的 checkout,因為拉取其 latest映像檔可能會執行比該 checkout 更新的版本。
查看目前執行中的項目
docker compose ps
docker compose ps -a
docker compose logs -f --tail=100
docker compose logs --since 15m --timestamps db
docker compose top
docker compose lsps 只會列出目前執行中的容器。若服務在啟動期間當機,除非加入 -a,否則不會顯示在其中。因此,容器未出現在 ps 中,但 ps -a 將其顯示為 Exited (1),正是啟動失敗的常見情況。先查看結束代碼,再查看日誌。
logs -f 會同時追蹤所有服務,並在每行前加上服務名稱。當服務彼此通訊且事件順序很重要時,這是適合使用的檢視方式。指定服務名稱即可縮小範圍。對已執行一個月的容器而言,--tail=100 很重要,因為預設會輸出完整歷史紀錄,讓終端機充滿內容。--since 15m 可回答通常最需要確認的問題,也就是剛才執行的重新啟動期間發生了什麼事。
top 會列出每個容器內的程序,用來區分「容器正在執行」與「容器內的程序正在執行」。ls 會離開目前目錄,並列出主機上正在執行的 Compose 專案及其狀態;加入 -a 可一併列出已停止的專案。
進入服務內的 shell
docker compose exec web sh
docker compose exec -u root web sh
docker compose run --rm web env
docker compose run --rm --no-deps web shexec 會在已經執行中的容器內執行命令。run 會根據相同的服務定義啟動新的容器;當服務維持執行的時間不足以讓您使用 exec 進入時,就需要使用這個命令。請務必將 run 與 --rm 搭配使用,否則每次執行都會留下已停止的容器。這些容器會不斷累積,直到 docker compose ps -a 難以讀取。
請先嘗試 sh,再使用 bash。部分以 Alpine 為基礎的映像檔包含 bash,官方 PostgreSQL Alpine 映像檔就是其中之一。如果映像檔未包含該命令,錯誤訊息會顯示 exec: "bash": executable file not found in $PATH。將 --no-deps 加入 run 可略過服務的相依項目,避免快速檢查設定時啟動整個資料庫。
run --rm web env 是查看服務實際取得之環境的最快方式。此時所有 .env 檔案、environment: 區塊與 shell 變數都已合併。如果值不正確,通常是因為合併順序所致;Compose 如何解析 env 檔案與 secrets 說明了哪個來源具有優先權。
網路、連接埠與名稱解析
docker compose port web 80
docker compose exec web getent hosts db
docker compose config --networks根據預設,Compose 會將每個服務放在同一個專案網路上,而且每個服務名稱都是該網路上的 DNS 名稱。在 web 內執行 getent hosts db 時,如果映像檔包含 getent,即可檢查名稱解析;這不會測試 db 是否接受連線。如果名稱可解析,但連線遭拒,請確認伺服器正在執行,且正在要求的位址與連接埠上監聽;僅繫結至 127.0.0.1 可能是原因之一。同樣的網路邊界也說明了,從專案外部啟動的容器,不論是透過 docker run 啟動,或作為獨立堆疊啟動,都需要共用網路,才能解析 jellyfin 這類服務名稱。當Jellyfin 媒體庫的 Halcyon 前端無法連線到其設定的伺服器時,這是第一個要檢查的項目。其餘模型請參閱Compose 網路與服務 DNS 的運作方式。
port web 80 會列印容器連接埠發布到的主機位址與連接埠。當對映來自變數時,這可免去猜測。發布連接埠也會寫入由 Docker 自行管理的防火牆規則,而且該規則優先於你的規則,因此你以為是私有的服務可能會對網際網路開放。為何已發布的 Docker 連接埠會繞過 ufw說明了這種情況。更安全的做法是不要發布這些連接埠,而是在專案網路上將一個負責驗證的代理放在服務前方;將 Authentik 作為單一登入層執行就是採用這種架構。
磁碟區與資料
docker compose config --volumes
docker compose cp db:/etc/postgresql/pg_hba.conf ./pg_hba.conf
docker compose down -vconfig --volumes 會逐行列出專案宣告的具名磁碟區。這份清單只是備份清冊的一部分,還需要檢查 bind mount 與設定檔。若磁碟區內含無法取代的資料,確切的備份指令與清單同樣重要。因此,PhotoPrism 與 Immich 的比較會明確列出各個相片伺服器所需的 dump 與複製指令。cp 可在不開啟 shell 的情況下,將檔案複製到容器或從容器複製出來;容器位於 service:path 語法的任一側。
down -v 會移除已宣告的具名磁碟區,但外部磁碟區除外;也會連同容器一起移除附加至容器的匿名磁碟區。這適合用來拆除測試 stack,但不適合用於含有重要資料的環境,因為執行時不會要求確認,也無法復原。Bind mount 不會受其影響,因為資料位於主機檔案系統中。這種影響範圍的差異,也是應審慎選擇 bind mount 與具名磁碟區的原因之一。
清理磁碟空間而不遺失資料
docker compose down --remove-orphans
docker system df
docker image prune -a
docker builder prune--remove-orphans 會刪除屬於該專案、但不再出現在檔案中的容器。服務重新命名後,通常就會產生這類容器。如果不執行此命令,這些容器會持續執行;而 docker compose ps 預設會包含正在執行的孤兒容器。
docker system df 會在刪除任何內容前,顯示磁碟空間的使用情況,並分別列出映像、容器、本機磁碟區與建置快取,以及各項可回收的空間。image prune -a 會移除所有沒有任何容器使用的映像,無論是否有標籤。若主機曾拉取大型映像的多個版本,這通常能釋放最多空間。builder prune 預設會移除懸置的建置快取;加入 -a 後,則會移除所有未使用的建置快取。
上述命令都不會處理具名磁碟區。docker volume prune --all、docker compose down -v 與 docker volume rm 會移除具名磁碟區。單獨執行 docker volume prune 時,只會移除匿名磁碟區。
在設定造成問題前先檢查檔案
docker compose config --quiet
docker compose config --services
docker compose --dry-run up -dconfig --quiet會驗證檔案,成功時不輸出任何內容,因此適合放在部署前步驟或 git hook 中。單獨執行 config 會輸出完整合併並插值後的檔案。這可用來確認變數是否已解析,以及 override 檔案是否依預期套用。未設定的變數會顯示為空值,旁邊會出現警告 The "X" variable is not set. Defaulting to a blank string.
--dry-run 是全域旗標,不是子命令旗標,因此要放在 up 前面。它會列出 Compose 將執行的每個動作,但不會修改任何內容。在重要的 stack 上執行 down 前,花費這 30 秒相當值得。
跨檔案、設定檔與專案協作
docker compose -f compose.yaml -f compose.prod.yaml up -d
docker compose --profile debug up -d
docker compose -p staging up -d多個 -f 旗標會依序合併,後面的檔案會逐項覆寫前面的設定。這是保留一個基礎檔案,再搭配小型正式環境覆寫檔的標準方式。不過,清單與對映的規則不同,因此在排查意外結果前,請先閱讀Compose 如何合併多個檔案。
--profile 會啟動標記有該 profile 的服務,以及未標記的服務,讓除錯工具不會出現在一般的 up 中。-p 會設定專案名稱,因此同一個 stack 的兩份副本可以並行執行,並使用分開的網路與 volume 名稱。重新開機後要恢復 stack,可使用 restart policy,或使用會代為執行 Compose 的 unit;詳見在開機時啟動 Compose stack。
FAQ
什麼取代了帶連字號的 docker-compose?
Compose V2 與 V5,使用帶空格的 docker compose 呼叫。在 Linux 上,Compose 是個別安裝的 plugin;Ubuntu 24.04 LTS 仍在其 universe repository 提供 V1 Python 工具。如果無法使用帶空格的寫法,請設定 Docker 的 repository,並安裝 docker-compose-plugin package。請將舊有 script 更新為帶空格的寫法,不要建立 alias,因為 V2 支援 V1 沒有的 flags。
為什麼 docker compose restart 沒有套用我的設定變更?
restart 會使用建立現有 container 時採用的設定停止並啟動該 container,不會重新讀取 compose.yaml。環境變數、埠、volume 或 image tag 有任何變更,都需要執行 docker compose up -d;此命令會將各 service 與目前執行中的 container 比對,並重新建立有差異的 service。如果希望即使檔案內容沒有變更也執行替換,請加入 --force-recreate。
如何將 service 更新至較新的 image?
先執行 docker compose pull,再執行 docker compose up -d。pull 會為檔案中的每個 tag 取得目前的 image,而 up -d 會重新建立 image ID 與其 container 不一致的 service。單獨執行 up -d 時會遵循 pull policy:預設的 missing policy 會重用快取的 image,但 latest 一律會執行 pull。
哪些清理命令可安全地在執行中的伺服器上使用?
docker system df 只會回報磁碟使用量,不會刪除任何內容;docker image prune -a 與 docker builder prune 會移除未使用的 image 與 build cache,因此執行中的 service 仍可正常運作,named volume 也不會受到影響。較危險的是 docker compose down -v,它會刪除專案宣告的非 external named volume,以及已附加的 anonymous volume,且不會提示;docker volume prune --all 則會刪除未使用的 local volume,包括 named volume,執行時只會出現一次 y/N 提示,或在搭配 -f 時完全不提示。先執行 docker compose config --volumes,確認可能受影響的項目。
可以只執行一個命令,而不啟動整個 stack 嗎?
可以。docker compose run --rm --no-deps web sh 會根據 web service definition 啟動單一 container、略過其相依服務,並在你離開時移除該 container。若 container 已在執行,請改用 exec,因為 exec 會在執行中的 container 內執行命令,讓你看到 service 實際所處的狀態。