完整教學:Docker + Cloudflare Tunnel 部署 n8n
這篇文章記錄 SuperPortia 把 n8n v2.13.2 部署到 MacBook Air M3 的完整過程。最終架構:Docker 容器本機跑、Cloudflare Tunnel 穿透到公網、CF Access 做 Email 認證、MCP Server 對 Claude Code 開放。全程沒有額外費用,資料不出本機。
如果你想先理解為什麼要 self-host 而非用 Make.com / Zapier,先看上一篇:n8n Self-Hosted:為什麼我們選擇自建而非 SaaS。
最終架構圖
graph TB
Browser["🌐 瀏覽器\nn8n.superportia.dev"] -->|HTTPS| CFA["Cloudflare Access\nEmail 認證閘道"]
CFA -->|通過驗證| CFT["Cloudflare Tunnel\nubi-mcp tunnel"]
CFT -->|內部連線| LH["localhost:5678"]
Claude["🤖 Claude Code\n(本機)"] -->|MCP / HTTP| LH
LH --> Docker["Docker Container\nn8nio/n8n:2.13.2"]
Docker --> Data["~/Documents/n8n-data\nSQLite + workflows"]
Docker --> Bind["127.0.0.1:5678\n(不對外)"]
關鍵設計:Docker 只綁 127.0.0.1,5678 port 從不直接對外暴露。外部存取全部走 Cloudflare Tunnel,本機 AI 工具(Claude Code)走 localhost 直連。
前置條件
| 需要 | 用途 |
|---|---|
| Docker Desktop(Mac)或 Docker Engine(Linux) | 跑 n8n 容器 |
| Cloudflare 帳號 + 你管理的域名 | Tunnel + Access + DNS |
cloudflared CLI | 設定和跑 Tunnel |
| 一個 Email 帳號 | CF Access 認證用 |
Linux VPS 的流程幾乎相同,差別在 Docker 安裝方式和 launchd 換成 systemd。Windows 也可行,但路徑格式和 volume 掛載方式不同,本文不另外說明。
Step 1:建立資料目錄
n8n 把所有資料(workflows、credentials、execution log、SQLite DB)都存在 ~/.n8n 或你掛載的路徑。我們用獨立目錄方便備份和管理:
mkdir -p ~/Documents/n8n-data
chmod 700 ~/Documents/n8n-data
chmod 700 確保只有你的用戶可以讀寫這個目錄,防止其他本機用戶或程式意外存取。
Step 2:啟動 Docker 容器
完整的 docker run 指令:
docker run -d \
--name n8n \
--restart unless-stopped \
-p 127.0.0.1:5678:5678 \
-e N8N_HOST=n8n.superportia.dev \
-e N8N_PORT=5678 \
-e N8N_PROTOCOL=https \
-e NODE_ENV=production \
-e WEBHOOK_URL=https://n8n.superportia.dev/ \
-e N8N_ENCRYPTION_KEY=<your-32-char-random-key> \
-e N8N_MCP_ENABLED=true \
-e N8N_RUNNERS_ENABLED=true \
-v ~/Documents/n8n-data:/home/node/.n8n \
n8nio/n8n:2.13.2
環境變數詳解
| 變數 | 值 | 說明 |
|---|---|---|
N8N_HOST | n8n.superportia.dev | n8n 認為自己跑在哪個域名,影響 OAuth redirect 和 webhook URL |
N8N_PORT | 5678 | 容器內部 port |
N8N_PROTOCOL | https | 告訴 n8n 外部是 HTTPS(透過 Tunnel),避免 HTTP/HTTPS 混合問題 |
NODE_ENV | production | 關閉除錯輸出,效能更好 |
WEBHOOK_URL | https://n8n.superportia.dev/ | Webhook 觸發器的完整 base URL,含結尾斜線 |
N8N_ENCRYPTION_KEY | 32 字元亂數字串 | 加密儲存在 DB 裡的 credentials(API keys 等)。絕對不能遺失,否則所有憑證作廢 |
N8N_MCP_ENABLED | true | 啟用 Instance-level MCP Server(v2.13+ 新功能) |
N8N_RUNNERS_ENABLED | true | 啟用 Task Runners(execution 效能優化) |
產生方式:openssl rand -hex 16(32 字元 hex string)
-p 127.0.0.1:5678:5678 是核心安全設定
這行把 port 5678 綁定到 loopback interface(127.0.0.1),不是 0.0.0.0。效果:
- ✅ 本機 Claude Code 可以直連
localhost:5678 - ✅ Cloudflare Tunnel 可以透過 loopback 連到容器
- ❌ 外網無法直接連 5678(即使你的防火牆有漏洞)
如果寫成 -p 5678:5678,n8n 就直接暴露在所有 interface 上,任何人都能存取。永遠不要這樣做。
驗證容器跑起來了
docker ps | grep n8n
# 應該看到:n8n n8nio/n8n:2.13.2 ... 127.0.0.1:5678->5678/tcp ...
curl -s http://localhost:5678/healthz
# 應該回傳:{"status":"ok"}
Step 3:Cloudflare Tunnel 設定
Cloudflare Tunnel(前身 Argo Tunnel)讓你的本機服務可以透過 Cloudflare 網路暴露到公網,不需要開 port 或設 NAT。
SuperPortia 使用 ubi-mcp tunnel,這個 tunnel 已經服務 api.superportia.dev、llm.superportia.dev、note.superportia.dev——n8n 加進同一個 tunnel 就行,不需要建新的。
安裝 cloudflared
# Mac with Homebrew
brew install cloudflared
# Linux (Debian/Ubuntu)
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -o cloudflared.deb
sudo dpkg -i cloudflared.deb
建立新 Tunnel(如果你還沒有)
cloudflared tunnel login # 開瀏覽器授權
cloudflared tunnel create ubi-mcp # 建立 tunnel,名稱自訂
執行後會在 ~/.cloudflared/ 產生 credentials JSON 和 cert.pem。
設定 ingress(加入 n8n)
編輯 ~/.cloudflared/config.yml:
tunnel: <your-tunnel-id>
credentials-file: /Users/<username>/.cloudflared/<tunnel-id>.json
ingress:
# 既有服務(範例)
- hostname: api.superportia.dev
service: http://localhost:8788
# n8n — 新增這段
- hostname: n8n.superportia.dev
service: http://localhost:5678
originRequest:
noTLSVerify: false
# 必須有 catch-all
- service: http_status:404
設定 DNS 記錄
cloudflared tunnel route dns ubi-mcp n8n.superportia.dev
這會在 Cloudflare DNS 自動建立一條 CNAME 指向你的 tunnel。
啟動 Tunnel(背景服務)
Mac launchd 方式(推薦,開機自動啟動):
sudo cloudflared service install
sudo launchctl start com.cloudflare.cloudflared
臨時測試方式:
cloudflared tunnel run ubi-mcp
驗證 Tunnel 連通
# 從外部(或用手機)測試
curl -s https://n8n.superportia.dev/healthz
# 如果 CF Access 已開,會收到 403(正常,代表 Tunnel 通了,Access 在守門)
# 沒有 CF Access 時應該回傳 {"status":"ok"}
Step 4:Cloudflare Access 認證閘道
Cloudflare Access 在 n8n 前加一道身份驗證。未登入的請求直接被擋下,n8n 內建的用戶認證系統(email + 密碼)作為第二道防線。
在 CF Dashboard 建立 Access Application
- 前往 Cloudflare Dashboard → Zero Trust → Access → Applications
- 點 Add an application → 選 Self-hosted
- 填入:
- Application name:
n8n Automation - Session Duration:
24 hours(根據需求調整) - Application domain:
n8n.superportia.dev
- Application name:
- 建立 Policy:
- Policy name:
Email Allowlist - Action:
Allow - 在 Include 加入 Emails → 輸入你的 email(如
[email protected])
- Policy name:
- 儲存
Access Policy 支援多個 email 或整個 email domain(例如@superportia.dev)。如果你要讓多人存取,在同一個 policy 裡加入所有授權 email,或用 Emails ending in 規則。
測試 Access
在一個未登入的瀏覽器(無痕模式)開啟 https://n8n.superportia.dev,應該跳到 Cloudflare Access 的 email 驗證頁面,輸入授權 email 後會收到 one-time code。驗證通過後才能看到 n8n。
Step 5:安全加固檢查清單
部署完成後,過一遍這個清單:
☑ Docker 只綁 127.0.0.1(-p 127.0.0.1:5678:5678)
☑ N8N_ENCRYPTION_KEY 已備份到密碼管理器
☑ n8n-data 目錄權限 700(chmod 700 ~/Documents/n8n-data)
☑ Cloudflare Access 設定 Email 白名單
☑ n8n 內建用戶認證系統已設定(第一次登入建立 owner 帳號,email + 密碼)
☑ CF Tunnel 設為開機自動啟動
☑ Docker restart policy = unless-stopped
☑ 測試過從外部(手機/其他網路)存取需要 CF Access 驗證
如果你只有 CF Access 而 n8n 沒有設帳號,未來如果 CF 設定出問題,n8n 等於完全無保護。
實戰:Codex 安全加固筆記
審查時間:2026-03-22 02:00–04:00(Taipei)
範圍:Codex read-only sandbox,filesystem + web search,無 Docker daemon 存取
性質:這不是正式滲透測試,而是 AI 輔助的安全加固過程(hardening review)——找設定漏洞並立即修復,不包含攻擊模擬或漏洞利用
我們在部署完成後,立即派 OpenAI Codex(GPT-5.4, read-only sandbox)做了兩輪安全加固審查。以下是真實的發現和修復過程,不是事後編寫的理想狀態。
第一輪審查(部署後立即,222K tokens)
| 嚴重度 | 發現 | 修復 |
|---|---|---|
| HIGH | JWT token 明文存在 ~/.claude.json,檔案權限 0644(任何人可讀) | chmod 600 ~/.claude.json |
| HIGH | Docker 預設綁定 0.0.0.0:5678,macOS firewall 關閉,LAN 內任何裝置可直接存取 n8n | 重建容器改為 -p 127.0.0.1:5678:5678 |
| HIGH | CF Tunnel 直接轉發,沒有 Cloudflare Access 保護,n8n 登入頁暴露在公網 | 新增 CF Access Application + Email policy |
| MEDIUM | n8n-data/database.sqlite 權限 0644,含加密金鑰和用戶資料 | chmod 700 目錄 + chmod 600 所有 DB/log 檔案 |
| MEDIUM | 只有一個 owner 帳號,沒有 MFA | 已加 CF Access 作為外層保護,MFA 待後續啟用 |
| MEDIUM | Vault note 記載了 owner 密碼明文 | 已從 vault note 移除明文密碼 |
Docker 的-p 5678:5678 預設綁定所有網路介面,這代表你的 n8n 在同一個 Wi-Fi 網路下,任何人只要知道你的區網 IP 就能存取。務必明確寫 127.0.0.1:。
第二輪審查(安全修復後,133K tokens)
第一輪修復完成後,瀏覽器地址欄仍顯示「不安全」。再派 Codex 排查:
| 發現 | 原因 | 修復 |
|---|---|---|
n8n 生成 http:// 內部 URL | 缺少 N8N_HOST、N8N_PROTOCOL 環境變數 | 加入 N8N_HOST=n8n.superportia.dev + N8N_PROTOCOL=https |
| Cookie 不帶 Secure flag | N8N_SECURE_COOKIE=false | 改為 true(因為走 CF Tunnel 全程 HTTPS) |
| 缺少 SameSite cookie 設定 | 未設定 N8N_SAMESITE_COOKIE | 加入 N8N_SAMESITE_COOKIE=lax |
| 執行記錄無限增長 | 未設定 prune | 加入 EXECUTIONS_DATA_PRUNE=true + EXECUTIONS_DATA_MAX_AGE=168 |
修復後的完整環境變數
docker run -d \
--name n8n \
--restart unless-stopped \
-p 127.0.0.1:5678:5678 \
-v ~/Documents/n8n-data:/home/node/.n8n \
-e GENERIC_TIMEZONE=Asia/Taipei \
-e N8N_HOST=n8n.superportia.dev \
-e N8N_PROTOCOL=https \
-e N8N_EDITOR_BASE_URL=https://n8n.superportia.dev \
-e WEBHOOK_URL=https://n8n.superportia.dev/ \
-e N8N_PROXY_HOPS=1 \
-e N8N_SECURE_COOKIE=true \
-e N8N_SAMESITE_COOKIE=lax \
-e EXECUTIONS_DATA_PRUNE=true \
-e EXECUTIONS_DATA_MAX_AGE=168 \
docker.n8n.io/n8nio/n8n:2.13.2
我們的做法是:部署 → 立即派 AI 加固審查 → 修復 → 再審查。兩輪共消耗 355K tokens(222K + 133K,Codex),找出 6 個 HIGH/MEDIUM 設定問題。這不是滲透測試,而是系統化地把預設配置轉換成安全配置。成本:幾美金。價值:避免一次因為「不知道」導致的安全事件。
Step 6:MCP Server 設定(選用)
如果你要讓 Claude Code 透過 MCP 控制 n8n(這是本系列第三篇的主題),先在 n8n UI 啟用 Instance-level MCP:Settings → Instance-level MCP → Enable。啟用後會生成一組 JWT Access Token。
接著在 Claude Code 端加入 MCP server:
claude mcp add --transport http --scope user n8n \
http://localhost:5678/mcp-server/http \
--header "Authorization: Bearer <你的 JWT token>"
或手動編輯 ~/.claude.json:
{
"mcpServers": {
"n8n": {
"type": "http",
"url": "http://localhost:5678/mcp-server/http",
"headers": {
"Authorization": "Bearer <JWT token from n8n Settings>"
}
}
}
}
Claude Code 和 n8n 在同一台機器上,MCP 直接走localhost:5678,不需要過 CF Tunnel。外部 URL (https://n8n.superportia.dev) 留給人類用瀏覽器登入 — 有 CF Access 保護。
完整的 MCP 整合說明在下一篇:n8n MCP Server × Claude Code:AI 直接操控工作流。
常見問題排除
n8n 起不來,docker logs 顯示 permission denied
# 問題:n8n 容器的 user 是 node(uid 1000),無法寫入 n8n-data
# 解法:確認 volume 掛載路徑存在且 n8n 可以寫入
ls -la ~/Documents/n8n-data
# 如果是空目錄但 Docker volume 有問題,嘗試:
docker run --rm -v ~/Documents/n8n-data:/home/node/.n8n n8nio/n8n:2.13.2 n8n --version
Tunnel 連得到但 n8n 回傳 502
# 確認 n8n 容器真的在跑
docker ps | grep n8n
# 確認 healthz 在本機可以回應
curl localhost:5678/healthz
# 確認 tunnel config 的 service URL 正確
cat ~/.cloudflared/config.yml | grep -A 2 "n8n.superportia"
CF Access 一直跳出認證,無法通過
原因通常是 session cookie 設定問題。確認:
- Access Application 的 domain 設定是
n8n.superportia.dev(不含萬用字元) - 如果你的域名有 WAF 規則,確認沒有阻擋 CF Access 的 token cookie
重新開機後 n8n 沒有自動啟動
# 確認 Docker Desktop 的「Start at login」有開
# 確認 container restart policy
docker inspect n8n | grep -A 2 RestartPolicy
# 應該看到 "Name": "unless-stopped"
# cloudflared 的啟動確認
sudo launchctl list | grep cloudflared
遷移:把 n8n 從舊機器搬到新機器
# 舊機器:備份整個 n8n-data 目錄
tar -czf n8n-backup-$(date +%Y%m%d).tar.gz ~/Documents/n8n-data
# 新機器:解壓縮到同樣路徑
tar -xzf n8n-backup-20260322.tar.gz -C ~/Documents/
# 用完全相同的 docker run 指令啟動(尤其是 N8N_ENCRYPTION_KEY 必須一致)
n8n 的所有狀態都在這個目錄:workflows、credentials、execution history、SQLite DB。建議設定 cron 定期備份到 R2 或本地 NAS。一個簡單的做法:0 2 * * * tar -czf /backup/n8n-$(date +\%Y\%m\%d).tar.gz ~/Documents/n8n-data
部署後的下一步
基礎設施跑起來之後,有兩個方向可以繼續:
-
接上 Claude Code MCP:讓 AI 直接建立和管理工作流 → 詳見 n8n MCP Server × Claude Code:AI 直接操控工作流
-
開始規劃實際工作流:SuperPortia 規劃中的 5 個自動化場景 → 詳見 n8n 實戰應用:5 個我們正在規劃的自動化場景
...麼變化。 本系列的前三篇: - 為什麼選 self-host:[[n8n Self-Hosted:為什麼我們選擇自建而非 SaaS]] - 部署方法:[[完整教學:Docker + Cloudflare Tunnel 部署 n8n]] - MCP...
...啟用方式 如果你按照[[上一篇]]部署, 已經設好了。接下來是 Claude Code 端的設定。 Step 1:在 n8n 啟用 Instance-level MCP 並取得 JWT...
...lare Tunnel 作為唯一入口、Cloudflare Access 的 Email 認證閘道、檔案權限 700。這套設定比大多數人的 SaaS 帳號安全設定還嚴格。完整安全設定詳見本系列第二篇:[[完整教學:Docker + Cloudflare Tunnel 部署 n8n]]。 4. AI-native...