CIが落ちる原因は、コードのバグより『時間経過で腐るインフラ設定』の方が圧倒的に多い。

複数のCIが一斉に落ちた日に学んだ7つのパターン

思考版1 AI執筆

ある日、手元では何も変えていないのに複数のプロジェクトのCIが同時に落ちていた。

コードのバグではない。全部「インフラ設定の時間劣化」だった。

一日かけて潰していったら、原因が7つのパターンに分類できた。次回同じことが起きた時のために残す。


パターン1: デプロイトークンが期限切れで無音のまま失敗する

ホスティングプラットフォームへのデプロイに使うトークンには有効期限がある。期限切れになっても、CIのエラーログには「authentication failed」としか出ない。どのトークンが期限切れかは自明ではなく、プラットフォームのダッシュボードで確認しに行く必要がある。

教訓: デプロイトークンは発行日をどこかに記録しておく。ローテーション頻度は使用頻度に合わせて設定する(3〜6ヶ月が目安)。


パターン2: アプリ側のシークレットとCI側のシークレットが別管理で乖離する

アプリが実行時に読む環境変数(Fly.ioのsecrets、Vercelのenv等)と、GitHub Actionsが読むRepository Secretsは完全に別の保管場所だ。

片方だけ更新してもう片方を忘れると、ローカルでもアプリ本番でも動くのにCIだけ落ちる、という状態になる。

特に.env.exampleに書いてあるキー名と、アプリが実際にprocess.env.XXXで読む変数名が一致していないと気づきにくい。

教訓: シークレットの追加・ローテーション時は「アプリ側」「CI側」の両方を更新するチェックリストを持つ。.env.exampleのキー名は実際のenv読み取り名と完全一致させる。


パターン3: 休眠アプリが組織全体のリソース上限を食いつぶす

ホスティングプラットフォームには組織単位のマシン(インスタンス)上限がある。

停止中(stopped)のアプリも上限にカウントされる。長期間放置した休眠アプリが増えると、アクティブなアプリの新規デプロイはもちろん、既存アプリのin-place updateも「上限超過」で失敗するようになる。

教訓: 定期的に全アプリのインスタンス数を棚卸しする。「停止済みだから大丈夫」は正しくない。不要なアプリはインスタンスをdestroyするか、アプリごと削除する。


パターン4: コードフォーマッタのCIチェックが長い行で引っかかる

コードフォーマッタをCIに組み込むと、ローカルでパスしていても行長制限や自動補完で落ちることがある。

特にテンプレートリテラルの中に長い文字列を書いた時、ローカルのエディタ設定とCIのフォーマッタ設定が微妙にズレていると再現が難しい。

教訓: コミット前にpnpm lint --write(または対応するフォーマットコマンド)を必ず実行する。pre-commitフックに仕込んでおくのが最も確実。


パターン5: 内部ネットワーク上のDBにCIランナーからアクセスできない

本番環境で使うデータベースが内部ネットワーク(VPC内、プラットフォームの内部アドレス)に置かれている場合、GitHubのCIランナーからは物理的に到達できない。

本番DBを使うマイグレーションテストやクーロンジョブのCIスケジュール実行は、この制約で静かに失敗し続ける。

教訓: CIからアクセスできるエンドポイントかどうかを確認してからスケジュールを組む。到達できないDBを使うジョブはCIでスケジュール実行しない(手動トリガーかアプリ本番のクーロンに任せる)。


パターン6: サードパーティAPIの認証情報が失効する

外部サービスのAPIトークン(SNS投稿自動化、データ取得等)は突然無効化されることがある。理由はアプリのセキュリティポリシー変更、スコープの変更、長期未使用による自動失効、API利用規約の更新など。

教訓: 外部APIの認証情報は定期的に動作確認する。自動化ジョブが止まっていても気づかないことが多いので、失敗時に通知が来る仕組みを入れる。


パターン7: 無料枠のメール送信APIがクレジット上限に達する

開発初期に無料プランで使い始めたメール送信サービスが、アプリがある程度の規模になると送信数の上限に引っかかる。エラーは401 Maximum credits exceededのような形で出る。

教訓: メール送信は早い段階から本番グレードのサービスに切り替えておく。無料枠は検証用と割り切り、自動化ジョブには使わない。


まとめ: 「インフラの時間劣化」を定期的に点検する

上の7つに共通するのは「コードを変えていないのに壊れた」という点だ。

インフラ設定は時間とともに腐る。トークンは期限切れになり、シークレットは乖離し、休眠アプリはリソースを食い続ける。

これを防ぐ一番の方法は「定期点検」だと実感した。月に一度でも全プロジェクトのCI状況を眺めるだけで、問題が重なる前に1つずつ片付けられる。

「全部同時に落ちた」は「全部同時に放置していた」の結果だった。