Vercel Cron Jobs が実行されない
`vercel.json` がプロジェクトルートに無い、`path` が存在しないルートを指している、Hobby プランの 1 日 1 回上限に当たっている、Production にデプロイしていない、の 4 系統が大半。
Project → Cron Jobs タブで実行履歴を確認する。
#vercel#cron#schedule#deploy
公開:
要約
Vercel Cron が動かないときは、ほぼ 4 つの確認で切り分けできる: vercel.json がルートにあるか、path が実在ルートを指しているか、プラン上限に当たっていないか、Production deployment があるか。
Project → Settings → Cron Jobs の Logs と次回実行時刻を見て、定義そのものが認識されているかから検証する。
よくある原因
vercel.jsonの配置: プロジェクトルート(monorepo なら Settings → General で設定した Root Directory 直下)に無いと Vercel に認識されない。- 存在しないルート:
path: "/api/cron"と書いても、対応する API Route が無ければ 404 で毎回失敗扱いになる。 - Hobby プランの上限: 1 ジョブ、1 日 1 回まで。
*/5 * * * *のような高頻度は Pro 以上が必要。 - Production 未デプロイ: Cron は Production 環境の最新デプロイに対して発火する。
Preview deploy しか無い状態では動かない。 CRON_SECRET検証失敗: 公式パターンのAuthorization: Bearer ${CRON_SECRET}検証が不整合だと、毎回 401 を返して失敗扱いになる。
解決策
1. vercel.json で crons を定義する
{
"crons": [
{
"path": "/api/cron",
"schedule": "0 0 * * *"
}
]
}公式の Cron Jobs ドキュメント のとおり、schedule は 5 フィールドの cron 式(秒は含まない)。
時刻は UTC で評価される。
2. API Route / Route Handler を作る
// app/api/cron/route.ts
import { NextRequest } from "next/server";
export async function GET(req: NextRequest) {
const auth = req.headers.get("authorization");
if (auth !== `Bearer ${process.env.CRON_SECRET}`) {
return new Response("Unauthorized", { status: 401 });
}
await doScheduledJob();
return Response.json({ ok: true });
}GET に 200 を返すのが基本。CRON_SECRET は Project → Environment Variables で Production にだけ登録する。
3. プランを確認する
Usage and Pricing で、Hobby は 1 アカウントあたり 2 cron まで・1 日 1 回まで・最大 60 秒、と決まっている。
それ以上の頻度や本数が必要なら Pro 以上へ。
4. Production にデプロイして Logs を見る
git push origin main # Production deploy をトリガ
npx vercel logs --follow # 実行ログを stream で確認Project → Settings → Cron Jobs の Next run 時刻と Logs を確認する(Manage Cron Jobs)。
Logs に何も出ていなければ定義そのものが認識されていないので、vercel.json の配置を疑う。