用 GitHub Actions 自動部署到自架 Mac Mini Server 完整指南

內網 Mac Mini 不開 port、不需 VPN,用 self-hosted runner 讓 GitHub Actions 自動觸發 docker compose 部署,含 launchd service 設定與 Docker PATH 修正。

分享
用 GitHub Actions 自動部署到自架 Mac Mini Server 完整指南
Photo by Growtika on Unsplash

你有一台放在家裡或公司的 Mac Mini,想讓它跑 Docker 服務,又希望每次 push 到 GitHub 就自動部署——但問題來了:Mac Mini 在內網,沒有公開 IP,GitHub 的雲端機器根本連不進來。

解法是反過來讓 Mac Mini 主動出擊。這篇文章說明如何透過 GitHub Actions self-hosted runner,讓內網 Mac Mini 自己去 GitHub 拉 job 來執行,不需要開 port、不需要 VPN。

為什麼不用雲端 Runner

GitHub 提供的雲端 runner(ubuntu-latest 等)每次都是乾淨的全新環境,適合無狀態的測試或打包。但對內網機器的部署場景有根本性的限制:

  • GitHub 無法主動 SSH 進內網:雲端 runner 要連到你的機器,內網機器必須有公開 IP 或 tunnel,設定複雜
  • 每次重新 clone 很慢:雲端環境沒有快取,大型 repo 或 Docker image 每次都要重新下載
  • 沒有本機 .env 存取權:機密設定無法直接讀取本機檔案

Self-hosted runner 解決這三個問題:它是 Mac Mini 上的常駐 service,主動 poll GitHub,有 job 就自己拉下來跑,保有本機快取與檔案存取權。

整體架構

觸發流程:

開發者 merge PR → main 有新 push
  → GitHub 發出 push event
  → Mac Mini 上的 runner 收到 job
  → runner 在本機執行 git pull + docker compose rebuild
元件位置說明
GitHub repogithub.com/<owner>/<repo>原始碼來源
Workflow 檔.github/workflows/deploy-mac-mini.yml定義部署步驟
Self-hosted runnerMac Mini ~/actions-runner/執行 workflow job 的 agent
部署目標Mac Mini <project-path>/repo 在 Mac Mini 的 clone
Docker ComposeMac Mini <project-path>/管理容器生命週期

Workflow 設定

在 repo 建立 .github/workflows/deploy-mac-mini.yml

name: Deploy to Mac Mini

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: mac-mini

    steps:
      - name: Pull latest code
        working-directory: <project-path>/
        run: git pull origin main

      - name: Rebuild and restart services
        working-directory: <project-path>/
        run: |
          docker compose down
          docker compose up -d --build

幾個關鍵設定:

  • runs-on: mac-mini:指定只跑在 label 為 mac-mini 的 runner,不會跑到雲端機器
  • 兩段 working-directorygit pull 在 git repo 根目錄,docker compose 在 docker-compose.yml 所在位置
  • docker compose downup:先關掉舊容器再重建,確保每次都用最新程式碼

Self-hosted Runner 安裝步驟

在 Mac Mini 上執行以下指令,完成 runner 安裝並設為開機自動啟動。

1. 取得 Runner 註冊 Token

到 GitHub repo 頁面取得一次性 token(有效期約 1 小時):

https://github.com/<owner>/<repo>/settings/actions/runners/new?arch=arm64

2. 安裝 Runner

# 建立工作目錄
mkdir -p ~/actions-runner && cd ~/actions-runner

# 取得最新版本號
curl -s https://api.github.com/repos/actions/runner/releases/latest   | grep -o '"tag_name": "v[^"]*"' | head -1

# 下載 ARM64 macOS 版(Intel 改用 x64)
RUNNER_VERSION=<版本號,例如 2.334.0>
curl -o actions-runner-osx-arm64-${RUNNER_VERSION}.tar.gz -L   https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-osx-arm64-${RUNNER_VERSION}.tar.gz
tar xzf ./actions-runner-osx-arm64-${RUNNER_VERSION}.tar.gz

# 設定(--unattended 避免互動提示)
./config.sh   --url https://github.com/<owner>/<repo>   --token <TOKEN>   --labels mac-mini   --name mac-mini   --unattended

# 安裝為 launchd service(開機自動啟動)
./svc.sh install
./svc.sh start

./svc.sh install 而非直接 ./run.sh 的原因:terminal 關掉後 run.sh 進程就死了;launchd service 會在背景常駐、開機自動啟動。

Service 管理指令

cd ~/actions-runner
./svc.sh status    # 確認狀態
./svc.sh stop      # 停止
./svc.sh start     # 啟動
./svc.sh uninstall # 移除(重設時先執行)

Docker PATH 問題修正

launchd service 啟動時不會讀取 ~/.zshrc,預設 PATH 裡沒有 /usr/local/bin,導致 runner 找不到 docker 指令。

~/actions-runner/.env 設定 runner 的執行環境 PATH:

echo 'PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin' > ~/actions-runner/.env

設定後重啟 service:

cd ~/actions-runner
./svc.sh stop && ./svc.sh start

日常維運

確認 runner 狀態有兩個方式:

GitHub 頁面https://github.com/<owner>/<repo>/settings/actions/runners):

  • Idle:等待 job
  • Active:正在執行
  • Offline:service 沒跑

Mac Mini 本機

cd ~/actions-runner && ./svc.sh status
tail -f ~/Library/Logs/actions.runner.<owner>-<repo>.mac-mini/Runner_*.log

確認部署是否成功:

ssh <mac-mini-ssh-alias>
docker ps                          # 確認容器狀態
cd <project-path>/node/app
git log --oneline -3               # 確認最新 commit

常見問題

Q:Runner 顯示 Offline 怎麼辦?到 Mac Mini 執行 cd ~/actions-runner && ./svc.sh status,確認 service 是否在跑。若 token 已過期需重新設定:先 ./svc.sh uninstall,再用新 token 執行 ./config.sh remove 和重新安裝流程。Q:部署後容器沒更新?確認 Workflow 的 working-directory 路徑正確,以及 git pull 有抓到最新 commit(git log --oneline -3)。Q:docker: command not found 錯誤?這是 PATH 問題,依照上方「Docker PATH 問題修正」步驟設定 ~/actions-runner/.env 即可解決。Q:可以用在 public repo 嗎?不建議。Self-hosted runner 上任何能開 PR 的人都能觸發執行任意程式碼,public repo 有安全風險。僅限 private repo 使用。

結語

這套架構的核心思路是「讓內網機器主動出去拉工作,而不是等外部連進來」。一旦 runner service 跑起來,整個部署流程就是全自動的:push 到 main → GitHub 通知 runner → runner 在本機執行 git pull + docker compose rebuild,完全不需要人工介入。

對於個人或小團隊的內網服務來說,這是成本最低、維護最簡單的 CI/CD 方案之一。