事情是這樣的:Dokploy 上原本就接了一顆 GitHub App(裝在帳號 A),跑得好好的。某天要部署一個新服務,程式卻放在另一個 GitHub 帳號 B 的 private repo 裡——而那顆 App 根本看不到 B 的 repo。
從「先繞過去讓它動」到「正確接成長期可維護」,中間踩了好幾個坑,整理成這篇。下面所有帳號、網域、ID 都用 placeholder 代換。
一句話問題
Dokploy 的 GitHub App provider 綁定在某個 GitHub 帳號上。要部署的 private repo 如果屬於另一個帳號,那顆 App 就無權限列出、也 clone 不到它。
先搞懂:Dokploy 抓 Git 有兩種來源
| 來源(sourceType) | github(GitHub App) | git(customGit) |
|---|---|---|
| 認證方式 | App OAuth token | 一把 SSH 私鑰 |
| repo / branch 下拉選單 | 有 | 無(自己填 URL/branch) |
| 自動裝 webhook(push 自動部署) | 有 | 無 |
| 需要 App 裝在該帳號 | 要 | 不用 |
| 適用情境 | 同帳號、要自動 CD | 任何能 SSH 的 git server |
關鍵:github 來源要那顆 App 有權限;git(customGit)只是最原始的 git clone,跟 App 完全無關。 跨帳號卡關時,customGit 是第一條活路。
解法一:customGit + SSH Deploy Key(最快,單一 repo 適用)
- 在 Dokploy 產一把 SSH key(或本機
ssh-keygen -t ed25519)。 - 把 public key 加成「目標 repo 的 Deploy Key」,設成唯讀:
gh api -X POST repos/<ACCOUNT>/<REPO>/keys \
-f title="dokploy-deploy" \
-f key="ssh-ed25519 AAAA... your-public-key" \
-F read_only=true
Code language: PHP (php)- 在 compose/application 設定來源:
sourceType=git、[email protected]:<ACCOUNT>/<REPO>.git、branch、選剛剛那把 SSH key。 - 部署 → Dokploy 用私鑰
git clone→ build。
Deploy Key 的好處:它只能唯讀單一 repo,權限比「能碰整個帳號所有 repo」的 GitHub App 小得多,拿來給 CI/CD clone 剛剛好。
坑①:customGit 沒有自動 webhook,autoDeploy: true ≠ push-to-deploy
這個最容易誤會。把「CD」拆成兩層就清楚了:
- (A) 從 git 來源 build 部署:customGit 有。按一次就 clone + build。
- (B) push 一下自動部署:customGit 沒有。
autoDeploy: true 只是「收到 push 通知就部署」的開關,但要先有人通知 Dokploy——那個通知就是 repo 的 Webhook。GitHub App 會自動幫你裝這個 webhook;customGit 不會。所以 customGit 的「CD」其實是手動觸發(UI 按 Redeploy 或打 API)。要真正 push-to-deploy,要嘛自己加 webhook,要嘛改用 GitHub App。
解法二(推薦,長期/多帳號):幫帳號 B 開一顆「專屬」GitHub App
如果帳號 B 之後會部署很多東西,正解不是硬接舊 App,而是幫 B 開一顆自己的 GitHub App:
- 結果是帳號 A、帳號 B 各一顆 App、各一個 provider,兩個帳號都能部署、互不干擾。
- 在 Dokploy 的設定裡找「Create GitHub App」,跟著流程把 App 建在帳號 B、再 Install 到 B、選 All repositories。
聽起來簡單,但這條路上有四個坑。
坑②:別想「改 installationId 共用一顆 App」
一個 provider 紀錄只綁一個 installation(一個帳號)。如果你貪方便,把既有 provider 的 installationId 從帳號 A 改成帳號 B,帳號 A 的 repo 會立刻從 Dokploy 消失——原本用 A 部署的服務之後 redeploy 全部 GG。正解是「再開一顆」,不是「改舊的」。
坑③:建立 App 時,瀏覽器一定要登入「目標帳號」
Dokploy 的 Create GitHub App 會把你導去 GitHub,用 manifest 流程建立 App,而 App 會被建在你當下瀏覽器登入的那個 GitHub 帳號。如果你想把 App 給帳號 B,瀏覽器卻登入著帳號 A,App 就建錯地方了(還得砍掉重來)。建議:先用無痕視窗登入帳號 B,再按 Create。
坑④:webhook URL 必須「公開可達」;在 Access/SSO 後面要設 bypass
兩個小坑疊在一起:
- 別讓 webhook 指到 LAN IP。 Dokploy 建 App 時,webhook URL 會抓「當下的 external_url」。如果那時候 Dokploy 還掛在內網 IP,App 的 webhook 就會指到外面打不到的位址,push 永遠不觸發。確認它是公開網址,例如
https://dokploy.example.com/api/deploy/github。 - Access/SSO 閘要放行 webhook 路徑。 如果 Dokploy 前面有 Cloudflare Access(或任何 SSO 登入閘),GitHub 送來的 webhook 沒有瀏覽器 session,會被擋下。要對
/api/deploy/github這個路徑單獨設一條 bypass(policy:everyone、decision:bypass)。
怎麼不開瀏覽器就確認 webhook URL 對不對?用 App 的 private key 簽一個 JWT 直接問 GitHub:
# pip install cryptography
import base64, json, time, urllib.request
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
b64 = lambda b: base64.urlsafe_b64encode(b).rstrip(b"=")
APP_ID = 123456 # GitHub App 的 numeric id
key = serialization.load_pem_private_key(open("app.pem", "rb").read(), None)
now = int(time.time())
h = b64(json.dumps({"alg": "RS256", "typ": "JWT"}).encode())
p = b64(json.dumps({"iat": now - 60, "exp": now + 540, "iss": APP_ID}).encode())
sig = b64(key.sign(h + b"." + p, padding.PKCS1v15(), hashes.SHA256()))
jwt = (h + b"." + p + b"." + sig).decode()
req = urllib.request.Request(
"https://api.github.com/app/hook/config",
headers={"Authorization": "Bearer " + jwt,
"Accept": "application/vnd.github+json", "User-Agent": "x"})
print(json.load(urllib.request.urlopen(req)))
# {'url': 'https://dokploy.example.com/api/deploy/github', ...} <- 要是公開網址才對
Code language: PHP (php)坑⑤:gh api /apps/<slug> 回 404,不代表 App 是私有的
想確認某顆 App 能不能裝到別的帳號時,別只信 gh api /apps/<slug>——它常常回 404,會讓你誤以為 App 是私有的。直接用瀏覽器或 curl 打它的公開頁面:
curl -s -o /dev/null -w "%{http_code}\n" https://github.com/apps/<slug>
# 200 = 公開(可裝到任意帳號)
# 404 = 私有(只能裝在擁有者帳號)
Code language: PHP (php)之後怎麼選?
- 一次性/單一 private repo/不想動帳號的 App 設定 → customGit + Deploy Key,接受手動 Redeploy。
- 要 push-to-deploy、這個帳號之後會部署很多服務 → 幫該帳號開一顆專屬 GitHub App(一帳號一顆,多 provider 並存)。
說穿了:customGit 是「臨時通行證」,GitHub App provider 是「正式門禁」。 跨帳號卡住時先用前者讓它動,長期就升級成後者。
小抄
- 跨帳號 private repo → 先想 customGit + Deploy Key。
- customGit 無自動 webhook → 想要 push-to-deploy 就得加 webhook 或改用 App。
- 多帳號 → 一個帳號開一顆 App、一個 provider;別去改舊 provider 的 installationId。
- 建 App 時瀏覽器登入正確帳號;webhook URL 要公開;SSO 閘記得對 webhook 路徑開 bypass。