用 GitHub Actions 自動部署到自架 Mac Mini Server 完整指南
內網 Mac Mini 不開 port、不需 VPN,用 self-hosted runner 讓 GitHub Actions 自動觸發 docker compose 部署,含 launchd service 設定與 Docker PATH 修正。
你有一台放在家裡或公司的 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 repo | github.com/<owner>/<repo> | 原始碼來源 |
| Workflow 檔 | .github/workflows/deploy-mac-mini.yml | 定義部署步驟 |
| Self-hosted runner | Mac Mini ~/actions-runner/ | 執行 workflow job 的 agent |
| 部署目標 | Mac Mini <project-path>/ | repo 在 Mac Mini 的 clone |
| Docker Compose | Mac 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-directory:git pull在 git repo 根目錄,docker compose 在docker-compose.yml所在位置 docker compose down再up:先關掉舊容器再重建,確保每次都用最新程式碼
Self-hosted Runner 安裝步驟
在 Mac Mini 上執行以下指令,完成 runner 安裝並設為開機自動啟動。
1. 取得 Runner 註冊 Token
到 GitHub repo 頁面取得一次性 token(有效期約 1 小時):
https://github.com/<owner>/<repo>/settings/actions/runners/new?arch=arm642. 安裝 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:等待 jobActive:正在執行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 方案之一。