640 字
3 分鐘

GitHub Actions concurrency 怎麼設:部署工作流避免互相取消的 queue 選擇

部署 workflow 同時被多次 push 觸發時,問題往往不是 runner 不夠,而是兩個 job 在同一個環境做不同版本的操作。GitHub Actions 的 concurrency 能把它們放進同一個群組,但若沒有讀懂 pending run 的預設行為,新的提交可能會取代尚未開始的部署。

實務上先回答一題:**這個環境要「只保留最新版本」,還是「每個版本都必須依序部署」?**前者用預設 single queue;後者才選 queue: max。不要把這兩種意圖混在同一份 workflow。

只保留最新提交:預覽環境的設定#

同一 concurrency group 預設最多保留一個 pending run;再來一個 run 時,原本等待中的 run 會被取消並由新的取代。對每次 push 都重建的 preview,這正好避免舊 commit 排隊後才發布。

name: Preview
on:
push:
branches: [main]
concurrency:
group: preview-${{ github.ref }}
cancel-in-progress: true
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- run: ./scripts/deploy-preview.sh

cancel-in-progress: true 不只替換等待中的 run,也會取消同組正在執行的 run。這適合可安全重跑、且部署程序能處理中斷的 preview;若腳本會做不可逆的 migration,就不要照抄這個設定。

每個版本都要跑:production 用 queue: max#

需要保留每次 release 的順序時,使用 queue: max 讓同組最多排隊 100 個 run。它不能與 cancel-in-progress: true 同時使用,GitHub 會把這個組合視為 workflow 驗證錯誤。

concurrency:
group: production-deploy
queue: max

這不會替你的部署做版本鎖定或 rollback。它只是保證同組不會同時執行;資料庫 migration、人工核准與部署健康檢查仍要在 job 裡處理。

group 名稱要包含工作流與環境#

group 名稱不區分大小寫,而且不同 workflow 若用了相同名稱,也會互相影響。建議將 workflow 與目標環境都放進 key:

concurrency:
group: ${{ github.workflow }}-production
queue: max

若 workflow 同時處理 pull_request 與 push,像 github.head_ref 這種只在 pull request 存在的欄位必須提供 fallback,否則 group expression 可能無法組成有效名稱。

提交前的檢查清單#

  1. 寫下被取消的 run 是否可以安全重跑或中斷。
  2. 確認 group 不會跨 workflow、跨 staging/production 意外共用。
  3. production 若不能遺失發布,改用 queue: max,並移除 cancel-in-progress
  4. 以連續兩次 push 在非正式環境測試 Actions 頁面的 pending 與 cancel 結果。

如果 workflow 還涉及不可信的 fork PR,先完成 GitHub Actions pull_request_target 的 checkout 保護;concurrency 只能序列化執行,不能降低權限風險。

參考資料:

GitHub Docs:Control workflow concurrency

GitHub Docs:Concurrency 概念

GitHub Actions concurrency 怎麼設:部署工作流避免互相取消的 queue 選擇
https://laplusda.com/posts/github-actions-concurrency-deployment-queue/
作者
Zero
發佈於
2026-08-02
許可協議
CC BY-NC-SA 4.0
這篇文章有幫助嗎?

回報錯字、失效連結,或告訴我你想看的延伸主題。