前情提要 上一篇在 GCP 免費 VM 上把 n8n 跑起來了,但還沒辦法從外部存取。
這篇用 cloudflared tunnel 解決這個問題。
好處是:
不用改 GCP 防火牆,不暴露任何 port
自動 HTTPS
可以加 Cloudflare Access(Google 帳號驗證)擋在前面
架構概觀 這篇要做的事情分兩層:
第一層:cloudflared tunnel 在 VM 裡跑一個 cloudflared process,它會主動連到 Cloudflare 的網路,建立一條加密的隧道。 外部流量進來的路徑是:
1 使用者瀏覽器 → Cloudflare → cloudflared tunnel(VM 內)→ n8n(127.0.0.1:5678)
VM 不需要開任何防火牆 port,因為連線是由 VM 內部主動發起的。
第二層:Cloudflare Access(Zero Trust) cloudflared tunnel 只負責轉流量,本身不做身份驗證,任何人都能連進來。 Cloudflare Access 是加在 tunnel 前面的門禁:
1 使用者瀏覽器 → Cloudflare Access(驗證身份)→ cloudflared tunnel → n8n
只有通過驗證(這裡用 email OTP)的人才能到達 n8n。 這就是「Zero Trust」的概念:不信任任何人,每次都要驗證身份。
這篇的步驟順序
安裝 cloudflared
登入 Cloudflare、建立 tunnel、設定 config、新增 DNS
設定 Cloudflare Access(先做,tunnel 上線前就有門禁 )
設定 systemd 並啟動 tunnel
步驟零:安裝 cloudflared 在 VM 上執行:
1 2 3 curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared bookworm main' | sudo tee /etc/apt/sources.list.d/cloudflared.listsudo apt update && sudo apt install cloudflared
看到 Setting up cloudflared 就安裝成功了。
命名決策 這台 VM 不只跑 n8n,之後可能還有其他自動化服務。 所以 tunnel 和 subdomain 都以機器用途命名,而不是以某個服務命名。
以這篇為例,選 butler(管家),因為這台機器的定位是「幫你自動處理事情的後台」。
<TUNNEL_NAME> = butler
<YOUR_DOMAIN> = marsen.me
對外網址:butler.marsen.me
之後如果加新服務,在 config 裡多加一條 ingress 規則就好。
步驟一:登入 Cloudflare 在 VM 上跑:
1 cloudflared tunnel login
它會印出一個 URL,複製到瀏覽器,選你要授權的 domain(這裡是 <YOUR_DOMAIN>)。
授權完成後 cert 會自動存到:
步驟二:建立 Tunnel 1 cloudflared tunnel create <TUNNEL_NAME>
成功後會顯示 tunnel ID(UUID 格式),同時在 ~/.cloudflared/ 產生一個 credentials JSON 檔。
1 2 Tunnel credentials written to /home/USER/.cloudflared/<tunnel-id>.json Created tunnel <TUNNEL_NAME> with id <tunnel-id>
把 tunnel ID 記下來,後面要用。
步驟三:建立 config 檔 1 2 3 4 5 6 7 8 9 cat > ~/.cloudflared/config.yml << 'EOF' tunnel: <your-tunnel-id> credentials-file: /home/<USER>/.cloudflared/<your-tunnel-id>.json ingress: - hostname: <TUNNEL_NAME>.<YOUR_DOMAIN> service: http://127.0.0.1:5678 - service: http_status:404 EOF
注意最後的 EOF 必須從行首開始,前面不能有任何空格。
為什麼用 127.0.0.1 而不是 localhost? localhost 在某些環境會被解析成 IPv6 的 [::1],但 n8n 預設只監聽 IPv4。 直接寫 127.0.0.1 避免這個問題。
其中 5678 是 n8n 在 Docker 容器裡對外暴露的 port。
可以用以下指令驗證 config 是否正確:
1 cloudflared tunnel ingress validate
看到 OK 就是正確。
步驟四:新增 DNS 記錄 1 cloudflared tunnel route dns <TUNNEL_NAME> <TUNNEL_NAME>.<YOUR_DOMAIN>
這會在 Cloudflare DNS 自動加一筆 CNAME,指向你的 tunnel。
步驟五:設定 Cloudflare Access 在啟動 tunnel 之前先設好 Access ,這樣 n8n 上線的那一刻就已經有門禁,不會有公開暴露的空窗期。
注意:不要用 Onboarding 精靈。 Zero Trust 首次進入會有引導精靈,精靈的「指派 Tunnel」步驟需要 tunnel 已在執行中才能選取。我們要先設 Access 再啟動 tunnel,所以改用手動建立 Application。
進 Cloudflare 主控台 → Zero Trust → Access → Applications → + Add an application → 選 Self-hosted 。
第一次進入 Zero Trust 需要選擇團隊名稱(之後可以改),Free 方案 50 人以下免費。
填入應用程式資訊:
欄位
填什麼
Application name
<APP_NAME>
Subdomain
<TUNNEL_NAME>
Domain
<YOUR_DOMAIN>
下一步設定 Policy:
欄位
填什麼
Policy name
allow
Action
Allow
Selector
Emails
值
<YOUR_EMAIL>
存檔。Access 現在保護 <TUNNEL_NAME>.<YOUR_DOMAIN>,只有通過驗證的 email 才能進去。
1 2 3 4 5 6 7 使用者瀏覽器 ↓ HTTPS Cloudflare Access(驗證身份) ↓ 通過後 cloudflared tunnel(在 VM 裡跑) ↓ 本機連線 127.0.0.1:5678(n8n)
為什麼要在 tunnel 啟動前先做? 你一開 n8n 就會看到「Set up owner account」頁面,這個頁面是公開的。 如果沒有 Access 保護,任何人都能搶先填、搶走 owner 帳號。
步驟六:設定開機自動啟動並啟動 Tunnel 讓 cloudflared 在 VM 重開機後自動啟動,不用每次手動跑。
cloudflared service install 需要知道 config 在哪,但用 sudo 跑時 home 會變成 root 的, 所以要明確指定路徑。安裝後 systemd 會把 config 複製到 /etc/cloudflared/config.yml:
1 2 3 sudo cloudflared --config /home/<USER>/.cloudflared/config.yml service install sudo systemctl start cloudflared sudo systemctl status cloudflared
把 <USER> 換成你的 OS Login 用戶名稱(Gmail 帳號把 . 和 @ 換成 _,例如 yourname_gmail_com)。
看到 active (running) 就完成了。
這時開 https://<TUNNEL_NAME>.<YOUR_DOMAIN>,應該會先看到 Cloudflare Access 的驗證頁面。
之後 VM 重開機,cloudflared 和 n8n 都會自動恢復。
踩坑紀錄 踩坑一:瀏覽器顯示連線錯誤 症狀 :tunnel 連上了(看到 Registered tunnel connection),但開瀏覽器只看到錯誤頁面。
瀏覽器的連線錯誤原因很多,不要猜。先查 n8n log:
1 sudo docker logs n8n --tail 20
根據 log 的錯誤訊息再對症下藥。
踩坑二:n8n 啟動失敗(permission denied) 出現時機 :docker logs 看到 EACCES: permission denied。
原因 :docker run 之前沒有預先建立 ~/.n8n,Docker 自動用 root 身份建立這個目錄。 n8n 容器裡的 node 用戶(UID 1000)不是 root,沒有寫入權限,n8n 啟動就 crash。
解法 :把目錄 owner 改成 UID 1000,再重建容器:
1 2 3 4 5 6 7 8 9 sudo chown -R 1000:1000 ~/.n8n sudo docker rm -f n8n sudo docker run -d \ --name n8n \ --restart unless-stopped \ -p 5678:5678 \ -v ~/.n8n:/home/node/.n8n \ -e N8N_SECURE_COOKIE=false \ docker.n8n.io/n8nio/n8n
預防 :照第一篇步驟五,docker run 前先執行 mkdir -p ~/.n8n && sudo chown 1000:1000 ~/.n8n,就不會踩到這個坑。
n8n 映像檔本來就用非 root 的 node 用戶執行,不需要加 --user。
參考
小結
cloudflared tunnel 讓 n8n 對外,不用開 GCP 防火牆、不暴露任何 port
Cloudflare Access 擋在前面,只有通過驗證的 email 才能進到 n8n
cloudflared 設成 systemd service,VM 重開機自動恢復,不需要手動啟動
整條鏈路:瀏覽器 → Cloudflare Access(驗證)→ cloudflared tunnel → n8n
(fin)