[CODE]

用 GitHub Actions 取代 n8n Queue Mode

我把四個 Railway 容器跟自架的 n8n 全部砍掉,換成 GitHub Actions cron,零常駐費用,這篇記錄為什麼這樣做、踩了哪些坑。

2 min read
github-actions n8n automation devops

我服務的民宿客戶沒有 IT 人員。系統壞掉的時候,他們不會看到監控警告——他們是因為客人沒收到報表才發現出了問題。這是一個很糟糕的方式來發現基礎設施的問題。

去年底,我為幾個民宿業主客戶建了一套 Google Ads 自動化系統,跑在 n8n Queue Mode 上。這套系統應該每週一安靜地跑一次:拉廣告成效數據、生一份報表、送出去。沒有人需要去想它。這就是建它的目的。

實際上,一直在想這件事的人是我——而且太頻繁,通常是在週末。

原本的 n8n 架構長什麼樣

Queue Mode 的 n8n 需要四個容器:主應用、worker、webhook receiver,還有 Postgres。我全部跑在 Railway 上,費用大概是每月 $15–20 美金,對一個客戶專案來說還好,但真正的成本不是錢。

真正的成本是維護時間。每次 n8n 出新版本,就有東西壞掉。Credential 要重新設定,某個 node 的 API 行為改了,Postgres 的連線池又不一樣了。我常常在週日下午花兩個小時在 debug 一個「應該自動跑」的系統。

四個容器,跑一個 cron job,一週一次。

真正讓我下定決心的那次

有一次 n8n 版本升級,悄悄改了某個 node 處理 HTTP Basic Auth 的方式。工作流程在 UI 上看起來跑成功了,但實際的 API call 都在失敗。

我是因為客戶問「怎麼沒收到報表」才發現的。

這句話本身就是問題所在。系統沒有捕捉到失敗——是客戶注意到缺席才發現的。我花了三個小時在 visual editor 裡 debug,試圖找出到底哪裡變了。中間有個念頭閃過:如果這是 Python script,我大可以寫個 unit test,在它碰到任何人之前就抓到這個問題。

GitHub Actions 版本

遷移花了大概一天。現在整個系統就是一個 Python script 加一個 workflow YAML。

name: Google Ads Weekly Report

on:
  schedule:
    - cron: '0 1 * * 1'  # 每週一 09:00 台北時間(UTC+8)
  workflow_dispatch:

jobs:
  run-report:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Run Google Ads report
        env:
          GOOGLE_ADS_DEVELOPER_TOKEN: ${{ secrets.GOOGLE_ADS_DEVELOPER_TOKEN }}
          GOOGLE_ADS_CLIENT_ID: ${{ secrets.GOOGLE_ADS_CLIENT_ID }}
          GOOGLE_ADS_CLIENT_SECRET: ${{ secrets.GOOGLE_ADS_CLIENT_SECRET }}
          GOOGLE_ADS_REFRESH_TOKEN: ${{ secrets.GOOGLE_ADS_REFRESH_TOKEN }}
          CUSTOMER_ID: ${{ secrets.CUSTOMER_ID }}
          NOTIFY_EMAIL: ${{ secrets.NOTIFY_EMAIL }}
        run: python scripts/weekly_report.py

workflow_dispatch 這個 trigger 是我覺得最實用的小細節。不用等到週一,直接在 GitHub UI 上手動觸發,客戶週中臨時想確認什麼的時候很方便。

Credential 全部搬進 GitHub repository secrets,不用再管 n8n 的 credential vault,也不用猜 Railway 的環境變數有沒有正確注入。

真的變好的地方

零常駐費用。 GitHub Actions 私有 repo 的免費額度對一個一個月跑四次的工作綽綽有餘。從每月 $20 美金降到 $0。對服務毛利有限的小型民宿客戶來說,這很重要——我不需要把基礎設施費用轉嫁給客戶。

程式碼進了版本控制。 聽起來理所當然,但在 n8n 裡不是這樣。workflow 存在 Postgres 資料庫裡,要回滾只能從備份還原。現在就是 git revert。出了問題,有清楚的歷史記錄可以追蹤什麼時候改了什麼。

可以在本機測試。 Python script 在我電腦上直接跑。塞進測試用的 credential,看一下輸出,沒問題再 push。這件事在 n8n 的 visual workflow 裡根本做不到。前面說的那個 Auth 靜默失敗,如果是 Python script,本機跑一下就會看到 auth error,上線之前就能修掉。

失敗是可見的。 GitHub Actions 的 job 失敗會立刻發 email 通知。失敗模式從「客戶幾天後注意到報表沒來」變成「我立刻收到通知」。對於在跑著一套沒人盯的自動化系統來說,這是一個實質性的差異。

放棄了什麼

Visual editor 對非工程師確實親切很多。如果有一天要把這個系統交給不會寫程式的人維護,n8n 的 UI 會比 Python script 加 YAML 好上手得多。

另外,n8n 的錯誤處理和重試機制是內建的。換成 GitHub Actions 之後,這些邏輯要自己寫:加 try/except、決定什麼算可重試的錯誤、怎麼發失敗通知。不難,但以前不用想這些。

真正的教訓

問題的核心是工具選擇跟需求不匹配。n8n Queue Mode 是一套認真的系統——它是為需要處理複雜事件驅動流程、多個並行工作流程的團隊設計的。而我用它跑的是:一個 Python script,一週一次。

對於「需要穩定、維護成本低」的每週批次工作,cron job 才是對的工具。不是所有東西都需要常駐服務,而常駐服務的維護開銷,在服務沒有技術背景的小型客戶時會被放大——因為他們的容忍度很低,而問題的能見度很差。

遷移花了一天。那之後的週末,我沒有再在 debug n8n。