Dokploy 新手上路 FAQ:從一次實作中整理的 10 個基礎問題
前幾天從零架設了一個 Dokploy + Cloudflare Tunnel + Traefik 的自架服務平台。過程中我問了一堆「看起來很笨但真的卡在那裡」的問題。
這篇不是 step-by-step 教學,是把這些問題的底層原理講清楚——每一題我當下真的停下來問過,看完你下次動手就不用再卡。
排序從淺到深,建議順著讀。
Q1|「這個 API token 已經沒用了」是什麼意思?token 不是會過期嗎?
當時的對話:設定完 Cloudflare Tunnel 後,我被告知「這個 API token 已經沒用了,可以刪掉」。我的反應是「啊?它壞了?」
短答:不是壞了,是任務完成,用不到了。
完整解釋:兩種憑證的區別
做這類設定時,常常會同時用到兩種完全不同的憑證,新手很容易搞混。
| 憑證 | 用途 | 生命週期 |
|---|---|---|
| API Token(短期用) | 讓自動化腳本(或 AI)幫你改設定、建資源 | 做完就該刪 |
| 服務 Token(長期用) | 讓一個常駐服務持續運作,例如 cloudflared 連接 tunnel | 永久留著 |
建 Cloudflare Tunnel 這個流程裡:
你建 API Token
→ AI 用 API Token 建立 Tunnel
→ API 回傳一組「Tunnel 連接 Token」(JWT)
→ cloudflared 服務拿這個 Tunnel Token 連線
→ 設定完成,API Token 已經沒用了,可以刪
之後 cloudflared 常駐運作,靠的是 Tunnel Token(已經寫在 systemd unit 裡),不再需要 API Token。
為什麼要刪
- 減少外洩面(對話記錄、截圖、剪貼簿都可能留下)
- API Token 權限通常很大(能改 DNS、刪 zone settings),留著是風險
口訣:API Token 是鑰匙,用完還給保全;服務 Token 是員工識別證,員工上班要一直帶。
Q2|Dokploy 部署 whoami,會把主機的 port 80 / 3000 占用掉嗎?
當時我超擔心部署了什麼服務會把 Dokploy 自己那個
3000吃掉。
短答:不會。除非你特別設定,容器的 port 跟主機的 port 是完全分開的。
完整解釋:Docker port 有三種,搞混就卡死
這是新手第一大地雷:
| Port 名稱 | 位置 | 誰看得到 |
|---|---|---|
| 主機 published port | 主機的 0.0.0.0:xxx |
整個區域網路、公網(如果你開了防火牆) |
| 容器內部 port | 容器裡 process 監聽的 port | 只有 Docker 網路裡的其他容器 |
| Traefik 路由 port | Traefik 在 Docker 網路裡連去的 port | Traefik 自己,當仲介 |
一般 Dokploy + Traefik 架構,只有 Traefik 自己 publish 主機 port(80、443),其他所有服務容器只在 Docker 內部網路裡活動。
外網 → 主機:80(Traefik)→ Docker 網路 → whoami:80(容器內部)
↑ publish ↑ 完全沒碰主機 port
Code language: CSS (css)實戰確認
# 誰在聽主機 port 3000?
sudo ss -tlnp '( sport = :3000 )'
# 應該只會看到一個 docker-proxy(代表 Dokploy 自己)
# whoami 容器不會出現,因為它沒 publish 到主機
Code language: PHP (php)# 看 Docker 容器有沒有 publish port
docker ps --format "table {{.Names}}\t{{.Ports}}"
# Dokploy/Traefik: 0.0.0.0:80->80/tcp, 0.0.0.0:3000->3000/tcp
# whoami 容器: 80/tcp ← 沒前面那坨「0.0.0.0:xxx->」
Code language: PHP (php)沒 0.0.0.0:→ 這坨,就是沒佔用主機 port。
Q3|可以部署第二個 whoami 嗎?它們會打架嗎?
我問過「如果我要再部署一個 whoami 可以嗎」。
短答:可以,只要給它不同的子網域就好,數量無上限。
完整解釋:Traefik 按 Host header 路由
Traefik 不管誰在哪個 port、哪個容器——它看的是 HTTP 請求的 Host header:
瀏覽器要求 whoami.itsmygo.uk → Traefik 查規則:Host(`whoami.itsmygo.uk`) → whoami 容器
瀏覽器要求 whoami2.itsmygo.uk → Traefik 查規則:Host(`whoami2.itsmygo.uk`) → whoami2 容器
Code language: CSS (css)兩個容器跑同樣的 image、同樣監聽 80,完全不會打架——因為它們在 Docker 網路裡是兩個獨立 service,Traefik 知道誰是誰。
為什麼 wildcard DNS 讓這變超方便
Cloudflare DNS 設了 *.itsmygo.uk 之後,任何子網域的流量都會送到你的 Dokploy 那台。你只要:
- Dokploy 裡建一個應用、綁 domain
- 不用再碰 Cloudflare,立刻生效
數量限制只在你的硬體資源。
不能做什麼
- 同一個 Host 綁到兩個服務 → Traefik 會任選一個,另一個吃不到流量
- 同一個 Host + 同一個 path 綁多個 → 同上
要讓兩個服務共用同一個 host,要用 path 區分:api.xxx.uk/v1 → service A,api.xxx.uk/v2 → service B。
Q4|Dokploy 裡的 Domain 分頁那個「Container Port」到底要填多少?
這個問題大家一開始都會填錯。
短答:填容器裡 process 聽的那個 port,不是主機的 port。
完整解釋
這個欄位告訴 Traefik:「送到這個網域的請求,要打到容器內部哪個 port?」
每個 image 有它的「慣用 port」:
| 服務 | 它在容器裡聽哪個 port | Container Port 欄位就填 |
|---|---|---|
| whoami | 80 | 80 |
| nginx | 80 | 80 |
| 大多數 node/next/nuxt app | 3000 | 3000 |
| Python FastAPI / Flask(預設 uvicorn) | 8000 | 8000 |
| Go app 慣例 | 8080 | 8080 |
| Spring Boot | 8080 | 8080 |
怎麼查? 三種方法:
- 查 image 的 Dockerfile 有沒有
EXPOSE xxx - 看官方 docs
- 先 deploy、去看 logs 的 “Listening on port xxx”
實戰確認
Traefik 內建 dashboard API,可以直接看它的 runtime config:
docker exec dokploy-traefik wget -qO- http://localhost:8080/api/http/services | python3 -m json.tool
Code language: JavaScript (javascript)找到你那個服務,會看到:
{
"name": "app-xxx-service",
"loadBalancer": {
"servers": [{"url": "http://app-xxx:80"}] ← 這個 80 就是你填的 Container Port
}
}
Code language: JSON / JSON with Comments (json)Q5|每台 Dokploy 遠端伺服器都要裝 cloudflared 嗎?
我有兩台機器(.217 跟 .10),一開始想著是不是兩台都要裝一套 tunnel。
短答:不是每台都要。先想清楚「這台機器上的服務要不要公網直接訪問」。
完整解釋:兩種機器的定位
| 機器 | 角色 | 需要 cloudflared? |
|---|---|---|
| 前台機(.217) | 跑有公網 URL 的東西(前端、給瀏覽器打的 API) | ✅ 要 |
| 後台機(.10) | 跑後端服務(DB、Redis、Queue、worker、不對外的內部 API) | ❌ 不用 |
理由:後端服務本來就不該被瀏覽器直接打到。它們只跟「前台機上的容器」溝通。既然沒人從外面打,就不需要 tunnel、不需要 domain、不需要 SSL。
後端放後台機的好處
- 資源隔離:DB 掛了不會拖累前台網站
- 安全:DB 從頭到尾只在 LAN 內,外面摸不到
- 簡單:不用為它操心 domain、SSL、tunnel 這些
Q6|那前台(.217)怎麼打到後台(.10)的 DB?
短答:用 LAN IP + published port。
完整解釋:Dokploy 遠端伺服器的本質
Dokploy 的「遠端伺服器」不是把 .10 加入 .217 的 Docker Swarm 當 worker。它們是兩台獨立的 Docker 主機,Dokploy 透過 SSH 分別管理。
這代表:
- .217 上的容器不能用
service name直接叫 .10 上的容器(兩邊不在同一個 overlay network) - 要溝通,得透過.10 的 LAN IP + 一個它主動公開的 port
實戰做法
假設 Postgres 部署在 .10:
- 在 Dokploy .10 的 Postgres 應用,Advanced → Ports 分頁加:
- Published Port:
5432 - Target Port:
5432 - Protocol:
tcp - Publish Mode:
host(重要!這個選項才會綁到 .10 的 LAN IP,而不是只在 Swarm ingress 網路內)
- Published Port:
- 在 .217 上的 API 容器,設環境變數:
就這樣。API 從 .217 直接連 192.168.50.10:5432,走 LAN,速度超快。
安全提醒
Published port 等於把 DB 開在 LAN 上,所以:
- 家用路由器別把 5432 打洞到公網
- 密碼還是要夠強(內網不是安全保證,萬一有設備被打穿就裸奔)
進階選項(想用 service name 而不是 IP)
- Tailscale / WireGuard:在兩台機器上架個 overlay 網路,服務可以互相用主機名呼叫
- Dokploy Cluster Mode:把兩台真的加入同一個 Swarm,共用 overlay
家用 / 個人 side project,LAN IP + published port 就夠用,清楚又好懂。
Q7|登入 Dokploy 面板出現「Invalid origin」是什麼意思?
我把 Dokploy 面板對外開成
dokploy.itsmygo.uk之後,登入時跳這個錯。
短答:網站的「可信任來源白名單」沒包含你現在用的網址。
完整解釋:瀏覽器跨站保護
現代 web 框架(Dokploy 用 Better Auth、Next.js 也有類似機制)都會在登入、改密碼、任何敏感操作時檢查:
這個請求是從我信任的網址發出來的嗎?
這是為了擋 CSRF 攻擊——壞人做一個假網站,偷偷叫你的瀏覽器對真實網站發請求(你之前登入過,還有 cookie)。如果不檢查來源,這種攻擊就成立。
所以 Dokploy 有一個 trustedOrigins 白名單。一開始只有 http://192.168.50.217:3000 這種被預設,新加的公網網域不在名單上,就被擋。
怎麼修
Dokploy 的白名單存在 Postgres 的 user.trustedOrigins 欄位。用 SQL 加進去:
-- 連進 Dokploy 的 postgres 容器
UPDATE "user"
SET "trustedOrigins" = array_append(
COALESCE("trustedOrigins", ARRAY[]::text[]),
'https://dokploy.itsmygo.uk'
)
WHERE NOT ('https://dokploy.itsmygo.uk' = ANY(COALESCE("trustedOrigins", ARRAY[]::text[])));
Code language: PHP (php)然後有個大坑:這個白名單有 30 分鐘記憶體快取,改完要重啟 Dokploy 才會立刻生效。見下一題。
Q8|Dokploy 容器要重啟,直接 docker restart 不行嗎?
短答:不行,會搞壞 Swarm 的 DNS,導致 502。正確做法是 docker service update --force。
完整解釋:Docker Swarm 跟單機 Docker 的差別
Dokploy 用的是 Docker Swarm mode(一種內建的容器叢集技術),不是單機 docker run。在 Swarm 裡:
- service 是「我要跑幾個 X container」的聲明(抽象層)
- task / container 是實際跑起來的那幾個(具體實例)
- Swarm 會給每個 service 一個穩定的 DNS 名字(例:
dokploy),其他容器用這個名字找它
當你 docker restart <container_id>:
- 直接戳了那個具體 container
- Swarm 不知道你幹嘛,overlay 網路的 DNS 來不及更新
- 結果:Traefik 想找
dokploy這個 DNS 名,直接 NXDOMAIN
正確做法:
docker service update --force dokploy
這會讓 Swarm 正常地停掉舊 container、起一個新的,DNS 自動更新,外部無感。
怎麼看哪些是 Swarm service
docker service ls
有列出來的就是 Swarm service,重啟用 service update。沒列出來、只在 docker ps 看得到的,用 docker restart 才可以。
容器名字的差別
- 單機 Docker:你給什麼就叫什麼(例:
whoami-test) - Swarm service:會自動加後綴,像
dokploy.1.21wg341ibgejo2788zohbjuzu(service名.副本編號.task ID)
看到 .1.xxx 這種格式就是 Swarm,永遠用 docker service 操作。
Q9|Cloudflare Tunnel 為什麼不用開 port、沒有公網 IP 也行?
整個架構最神奇的地方。
短答:因為連線方向是從你家出去,不是從外面打進來。
完整解釋:反向 Tunnel 的原理
傳統部署:
外網 → 你家防火牆(打洞) → 你的伺服器
↑ 這一步需要公網 IP、需要開 port、需要動路由器
Cloudflare Tunnel:
你的伺服器 → 主動連線到 Cloudflare Edge → 保持 websocket-like 的長連線
↑ 這是「出去」的流量,家用路由器預設放行
用戶 → Cloudflare Edge → 從剛才那條長連線丟下來 → 你的伺服器
也就是說,cloudflared 啟動時主動拜託 Cloudflare 建立連線。之後所有外面來的流量,Cloudflare 從這條連線「倒著丟」給你。
為什麼這樣超安全
- 你家路由器完全不用開任何進站 port
- 你的伺服器完全沒暴露在公網(攻擊者連 IP 都掃不到)
- 所有流量先經過 Cloudflare → DDoS 保護、WAF、Bot 管理全自動開啟
- 內網 IP 變了也沒差(動態 DSL、cellular)
實戰確認
# cloudflared 在幹什麼
sudo systemctl status cloudflared
# 會看到類似:
# Registered tunnel connection ... ip=198.41.200.113 location=tpe01 protocol=quic
# Registered tunnel connection ... ip=198.41.192.37 location=khh01 protocol=quic
# ↑ 你的機器連去 Cloudflare 的 edge 節點,不是反過來
Code language: PHP (php)# 確認 HTTP 流量真的走 CF
curl -I https://whoami.itsmygo.uk
# 看 response header:
# server: cloudflare
# cf-ray: xxxxx-SIN ← CF edge 節點代號
# cf-cache-status: DYNAMIC
Code language: PHP (php)Q10|Dokploy 2FA 跟 Cloudflare Access,差在哪?要選哪個?
短答:兩個一起用。角色不一樣。
完整解釋:兩道不同位置的鎖
想像你的管理面板像一間房:
| 保護機制 | 鎖在哪 | 擋什麼 |
|---|---|---|
| Dokploy 2FA (TOTP) | 房間裡的保險箱 | 密碼外洩,但沒你手機的人進不來 |
| Cloudflare Access | 社區大門 | 根本不讓人走進你家巷子 |
單獨用各自的侷限
只有 Dokploy 2FA:
- 全世界的機器人都看得到你的 Dokploy 登入頁
- 他們會持續掃、刷密碼、測 0-day 漏洞
- 2FA 能擋住登入,但擋不住漏洞爆發那天
只有 Cloudflare Access:
- 大門守得很好,但進門之後 Dokploy 本身如果只有密碼沒有 2FA,萬一密碼外洩就完蛋
疊起來的登入流程
- 打開
dokploy.itsmygo.uk - Cloudflare Access 擋第一關:要求 Google SSO 或 email PIN
- 通過 → 看到 Dokploy 登入頁
- Dokploy:填帳號密碼
- Dokploy 2FA:填手機 app 的 6 位數 TOTP
- 進入
日常登入體感:Google 已登入 → 1 秒通過 CF → 填 Dokploy 密碼 → 填 TOTP → 進去。多做的就是最後那個 TOTP 6 位數,對日常幾乎無感,安全大升。
建議
個人站、小團隊、家用:
- 一定要:Dokploy 2FA(5 分鐘設定)
- 強烈推薦:Cloudflare Access + Google SSO(10 分鐘設定,免費 ≤50 人)
結語
這十個問題我全部當下都問過。不是因為文件沒寫,是因為現場的直覺跟文件的抽象說法對不上——你得真的 sudo ss -tlnp 看到那個 port、真的 docker ps 看到容器,才會把文件的內容對到現實。
所以這類「自己架一套」的任務,最有效的學法不是先讀完所有文件。是:
- 丟一個具體的目標(我要讓
whoami.xxx.uk可以從外面打) - 動手,卡住就問「為什麼」
- 每個為什麼都挖到底
看完這篇,下次你卡在同類問題,至少知道該看哪裡、該怎麼測。