記事の一覧

GitHub Actionsのcron遅延|落ちたと決める前に

GitHub Actionsのcronで組んだ定時実行が、予約した時刻に動かん。遅れとるだけか、もう来んのかは見た目で分けられません。自分の着弾記録の数え直し、公式の説明に書いてある遅れと止まる条件、前倒しと多重化の組み方、それでも8月末に足りんかった話、日本時間で書くtimezoneの項目までまとめました。

GitHub Actions の cron で組んだ定時実行が、予約した時刻に動きませんでした。設定は同じままで、遅れる幅は日によってばらつきます。8月前半は遅い日で予約から100分を超え、8月末には夜の便が翌朝まで来ん晩も出ました。

先に結論だけ書いときます。公式の説明に、混むと遅れるし実行が捨てられることもあると書いてあって、8月前半の遅れはその範囲でした(8月末の何時間もの遅れの原因は確かめてへん)。手当ては、早めに予約を置く、時間差で何回か発火させる、二重に出んガードを付ける、の3つ。ただ8月末の何時間もの遅れには足りず、9月からは手元の PC を主にして GitHub Actions は保険に回しました。

厄介なのは、遅れる幅に決まりが無いことです。前は朝の cron を毎時ちょうどに置いとって、自分から混む時間に寄っとった所もあります。GitHub 側で障害があった日の朝は、初便の予定から3時間半ほど遅れて来ました。この記事は、遅れとるのか落ちたのかをどう見分けて、どう組み直して、どこで足りんくなったかの記録です。数字は、8月1日〜17日の記録と8月27日〜9月2日の実行の記録を、2026年10月に Claude と数え直した物です。公式の説明も Claude と原文で読み直しました。

予約は20時45分、届いたのは21時31分

やっとることは単純です。SNS の投稿を、決めた時刻に自動で出す。出したい中身をファイルにして置いておくと、定時に走るワークフローがそれを拾って投稿する、という形にしてました。この記事の数字は、GitHub Actions の定時実行が主やった8月の物です(9月からの形は後ろに書きます)。

2026年8月前半(1日〜17日)の投稿の記録(29行・うち2行は届いてへん行)から、予約した時刻と届いた時刻を6行抜き出すとこうでした。

予約

届いた時刻

予約からの分

20時45分

21時31分

46分

20時45分

21時35分

50分

20時45分

22時27分

103分

20時45分

21時17分

32分

6時50分

7時33分

43分

6時50分

7時55分

65分

夜はだいたい30分から60分、遅い日は100分前後。朝は30分から75分くらい。いちばん早い日は15分で届いとりました。

ひとつ断っとくと、この分数は「予約した時刻から」数えた物です。あとで書くとおり、予約は cron が発火する時刻より前に置いとるので、cron そのものの遅れはこれより少し短い。cron の遅れだけを実行の記録で測った日は、46分49秒、45分11秒、そして8月3日の夜が129分(2時間9分)でした。この夜は着弾の記録やと予約から99分やけど、それは22時23分に手で撃った分です。遅れに待ちきれんかったわけやなく、前の晩に X の API のクレジットが切れて投稿が片方失敗しとったので、チャージした後に効いとるか確かめるために撃った便で、それで夜の投稿も出ました。定時の便はその40分ほどあとの23時05分に来て、もう出たあとやったので空振りしとります。

前は「遅れた幅は43分から103分」「自然に遅れた分の最大は103分」と書いてましたが、まちがいでした。記録を数え直すと、43分より早く届いた日がいくつもあったし、cron の発火だけを見ても、障害の無い8月3日と4日の夜に2時間を超えて遅れとった(4日は4本のうちどれかが少なくとも2時間18分遅れた計算)。拾った日が偏っとったんです。

ここで押さえておきたいのは、どれも成功しとることです。失敗の通知は来ません。実行のログも緑。ただ時刻がズレとるだけ。だから「動いてへん」と気づくのは、たいてい人間が見に行った時です。

「遅れとる」と「落ちた」は、見た目が同じ

21時に見に行って、まだ出てへん。この時点で分かることは何も無いんです。遅れとるだけかもしれんし、もう来んのかもしれん。

ここで慌てて手で撃ち直すと、二重に出んガードが無い作りやったら、あとから遅れて届いた分と合わせて二重に出ます(うちはガードを入れとったので、8月3日と7日に手で撃った時も、あとから来た定時の便は空振りで、二重にはならんかった)。かというて待っとっても、時刻どおりには来ん。障害の日の朝は3時間半ほど遅れて来ました。

8月7日の朝は、便が3つとも時刻どおりには来んかった。朝の点検をしとった AI(Claude)が8時36分に気づいた時は、いつもの1時間前後の遅れの内かもしれんので、落ちたとは決めずに待った。9時04分に見直しても3本とも来てへん。前の2日の出方から外れとったので、私が承認して、AI が9時19分に手で撃って朝の投稿を出しました。

定時の3本は10時32分から42分にかけて遅れて来て、もう出たあとやったのでガードで空振りしとります。原因は、あとで私が理由を聞いて、AI に調べさせて分かりました。その日は GitHub 側で Actions の障害が起きとった。GitHub Status の履歴では、日本時間の0時22分に障害の知らせが出て、9時5分に「緩和した」という更新が出とる。あとで出た事後の報告では、影響が続いたのは日本時間の0時5分ごろから9時14分ごろまでで、一番ひどい時は実行の7割ほどが失敗しとったそうです。

落ちたと決める前に見る2つ

1つめ、落ちた判定の基準を先に決めておく。基準が無いと、その場の気分で撃ち直してまうからです。

ここは一回間違えました。最初は「予約時刻から2時間」で区切っとった。けど上に書いたとおり、障害の無い日でも cron の発火が2時間を超えて遅れた夜がある。障害の日は、2時間待っても来んかった。8月末には、夜の便が9時間あまり1本も来ん晩もありました。時間で区切っても、遅れとるだけなのか、もう来んのかは分けられません。実測の最大値をそのまま目安にすると、更新されるたびに毎回ズレます。

次に「夜の最終便が走り終わっても届いてへんかったら落ちた」と見ることにして、前はそう書いてましたが、これも使えませんでした。実行の履歴には、どの cron の行で起きた便かが出ん。それに、後ろの対策1で書く賞味期限(予約から6時間)より遅れて最終便が来た晩は、走り終わった時にはもう投稿を捨てとる。8月末の3晩がこの形でした。

今は実行の履歴やなく、投稿の記録のほうを見てます。出たら「投稿OK」、期限切れで捨てたら「退避」の行が残る作りなので、その行を読む検査を回して、退避の行があったら知らせる。どっちの行もまだ無ければ、遅れとる途中です。

2つめ、自分を疑う前に障害情報を見る。GitHub は稼働状況を公開しとって、進行中の障害と、いつ始まっていつ収まったかが出ます。見に行く先は「GitHub Status」という公式のページです。8月7日のうちは、障害やと分かったのが手で撃ったあとでした。先に見とったら、待つか撃つかをもっと早く決められとった。ワークフローの中身を読み直すのは、そこを見てからで遅ないです。

何日待ってもcronが動かない時

ここまでは、遅れはしても便そのものはあとから来る話でした(障害の日も、3時間半遅れで来とった)。止まり方はもう1種類あって、何日たっても一度も動かん形です(動いとるのに気づいてへんだけ、も含む)。これは遅れとは別物で、原因は GitHub の公式の説明にはっきり書いてあります。

  • ファイルが既定のブランチに入っとるか。定時実行は、既定のブランチ(たいてい main)にあるワークフローでしか動きません。作業用のブランチに置いたままやと、何日待っても来ん
  • 自動で止められてへんか。公開リポジトリで60日間なにも動きが無いと、定時実行は自動で無効になります。Actions タブでそのワークフローを選んで「Enable workflow」を押せば戻る。公開リポジトリをフォークした物も、定時実行は最初から止まった状態です
  • 日本時間のつもりで書いてへんか。cron の時刻は、何も指定せんと UTC で読まれます。動いとるのに9時間ずれた時刻に走っとるだけ、ということがある

どれに当たるかは、実行の履歴で分かります。遅れとる時は、遅れた時刻で実行が残っとる。知らん時刻に並んどったら、たぶん3つめ。定時実行で起きた実行が1本も無いなら、1つめか2つめです。

出所は、GitHub Docs の「ワークフローをトリガーするイベント」の schedule の段と、「ワークフローの無効化と有効化」のページです。毎時ちょうどは混んで遅れやすいこと、負荷が十分に高いと並んだジョブの一部が捨てられることがある、というのも schedule の段に書いてあります。2026年10月に Claude と英語の原文で読み直して、中身は変わってへんかったです。

対策1|出したい時刻より前に置く

21時に出したいなら、21時に予約せんことにしました。定時実行そのものが遅れてくるので、ぴったりに出すのを諦めて、前倒しで置くほうが結果として近い時刻に着きます。

これが成り立つのは、まだ来てへん時刻の分は処理せずに待つようにしてあるからです。早い便が拾っても、予約時刻が未来ならそのまま置いて帰る。だから前倒しで置いても早く出てまうことはありません。

具体的には、夜の初便が20時53分に発火する設定なので、予約はその8分前の20時45分に置いてます。ここを何分にするかは、自分の cron の発火時刻から逆算するだけです。

ひとつ注意点があって、前倒しで置いた分には賞味期限があるようにしてます。自分の場合は予約時刻から6時間で期限切れ扱いにして、処理済みへ退避させる。これが無いと、何日も前の予約が残っとって忘れた頃に出てまいます。

あわせて、毎時ちょうどの時刻は避けました。公式の説明に、毎時の頭は混む時間帯やと名指しで書いてある。混む時間に自分から並びに行く理由が無いからです。

対策2|発火を多重化する

もうひとつは、1本の定時実行に賭けるのをやめたことです。夜は4回、朝は3回、時間差で発火させてます。それぞれ独立に並ぶので、1便がハマっても別の便が先に着地する。

書き方はこうです。cron の行を足していくだけの話です。

on:
  schedule:
    - cron: '53 11 * * *'   # UTC 11:53 = JST 20:53
    - cron: '23 12 * * *'   # UTC 12:23 = JST 21:23
    - cron: '53 12 * * *'   # UTC 12:53 = JST 21:53
    - cron: '53 13 * * *'   # UTC 13:53 = JST 22:53

時刻は UTC で書いとります。日本時間から9時間引いた値です(日本時間のまま書く方法は下に書きました)。分に「00・15・30・45」を使ってへんのは、世間の cron がそこに集まりやすいと考えたからで、うちは07・23・37・53に寄せてます。

ただしこれは単体でやったらあかん改造です。同じ仕事を4回拾いに行く形なので、素でやると同じものを4回出します。

要るのは、何回走っても結果が変わらん作りにしておくことです。自分の場合は、出したものを処理済みの置き場へ移してから終わる形にしました。次の便が来た時にはもう対象が残ってへんので、何もせずに帰る。多重化と、この二重実行のガードはセットです。

ガードが効いとるかは、2本目以降の便のログを見れば分かります。1本目が仕事をした日に、後続の便が「対象なし・0件で終了」になっとったら成功です。ここが毎回何か処理しとるようやったら、ガードが素通りしとります。同じものが2回出てから気づくのは遅いので、入れた日に1回だけ見に行っといたほうがええです。

8月末、前倒しと多重化では足りんかった

この2つで8月前半はしのげとったんですが、8月27日から様子が変わりました。設定は同じままで、9月2日までの7晩は、夜の初便の予定(20時53分)から最初の1本が来るまでが3時間半から9時間半あまり。朝も、前の晩の便と混ざらん日で数えると2時間前後から5時間ほど遅れとります。27日と28日の夜は翌朝6時台まで、31日の夜は翌3時11分まで1本も来んかったので、予約から6時間の賞味期限を過ぎて、その3晩の投稿は出さずに退避しとります。ガードは働いたので古い投稿が朝に出ることは無かったけど、出したい投稿も出んかった。前倒しも多重化も、便がいずれ来る前提の手当てなので、何時間も来ん時には効きません。9月1日からは、手元の PC で定時に動かす形を主にして、GitHub Actions の定時実行は保険に回しました。PC は24時間つけっぱなしにしてあるので、それで足ります。9月1日から10月21日まで、投稿した晩は記録に46回あって、45回は20時45分か46分に出とります(残る1回は、出す前に AI に読ませる審査の印が無くて2時間止まり、22時45分に出た晩)。

日本時間(timezone)でcronを書く時

9時間引くだけなら暗算で済む。そう思っとったんですが、1か所だけ落とし穴があります。日本時間の朝9時より前は、UTC では前の日になることです。

うちの朝の初便は日本時間の7時07分で、書いとる値は 7 22 * * *。UTC の22時07分、つまり前の日の夜です。毎日動かすだけなら日付がずれても困りません。困るのは曜日や日にちを指定した時で、日本時間の「月曜7時07分」は UTC やと「日曜22時07分」。曜日の欄まで1つ前にずらさなあかん。

# 日本時間の月曜 7:07 を UTC で書く(曜日は 0=日曜)
- cron: '7 22 * * 0'

この変換は、Claude に Node で計算させて確かめました。7 22 * * 0 は日本時間の月曜7時07分になります。

この変換が面倒なら、時間帯を指定する書き方があります。GitHub Docs の「ワークフロー構文」のページの on.schedule の段に timezone という項目が載っとって、IANA の時間帯の名前(日本なら Asia/Tokyo)を書くと、cron の時刻をその時間帯で読んでくれる。

on:
  schedule:
    - cron: '7 7 * * 1'   # 月曜 7:07(日本時間)
      timezone: "Asia/Tokyo"

先に白状しとくと、うちのワークフローはまだ UTC で書いたままで、この書き方は自分では動かしてません。2026年10月に Claude と英語の原文で、項目があることと書き方を確かめただけです。夏時間のある国を指定した時の注意書き(時計が進む日に飛ばされた時刻の予約は、次の有効な時刻に回る)も載っとるけど、日本は夏時間が無いのでそこは関係ない。

それと、時間帯を指定しても遅れる話は別です。日本時間で書いたから7時07分ぴったりに動く、とはならん。前倒しと多重化はそのまま要ります。

よくある質問

どれくらい遅れるものですか?

自分の記録では、8月前半は予約から届くまでが早い日で15分、遅い日で100分を少し超えました。cron の発火だけ見ても、2時間を超えて遅れた夜があります。8月27日から9月2日は、夜の初便の予定から最初の1本が来るまで3時間半から9時間半あまりで、3晩は投稿を出せんかった。設定は変えてへんのに日によっても時期によっても違うので、上限があるものとして組まんほうがええです。

遅延を無くす方法はありますか?

定時実行に任せず、外から叩いて起こす形もあります(手で動かす用の workflow_dispatch を、別の場所の定時の仕組みから呼ぶ形)。ただし叩く側を別に用意することになるので、そこを何が動かすのかという話に戻ります。自分は前倒しと多重化で8月前半はしのげたけど、8月末には足りんかった。9月からは手元の PC で動かすのを主にして、GitHub Actions は保険にしとります。外から叩く形は試してへんので、精度がどこまで上がるかは分かりません。

失敗したのか遅れとるのか、どう見ますか?

実行の履歴に何も出てへんなら、まだ発火してへんということです。緑で終わっとるのに結果が出てへんなら、中の処理のほうを見ます。この2つは原因が別なので、先に切り分けたほうが早いです。ただ、実行の履歴を時計と見比べても、遅れとる途中か落ちたかは決まりません。うちは投稿の記録に「出た」か「期限切れで捨てた」かの行が残る作りにして、その行で見てます。捨てた行が出たら落ちた、どっちの行もまだ無ければ遅れとる途中で、その間に GitHub Status を見に行きます。

定時に動く仕組みで困っとったら

決めた時刻に動かん、二重に出る、止まっても気づけん。今のワークフローと困っとる症状を教えてもらえたら、組み直し方をこちらで書き起こします。

相談する(メッセージのみ)