個人開発のバックエンド、コールドスタートで1分半かかってた。インスタンス増やす前にやるべきだったこと
Cloud Runで動かしてるGoのバックエンドが、コールドスタート時に1分半かかっていた。お金を払ってインスタンスを増やす前に、コードを1行直したら3秒台になった話。
この記事は、株式会社ZELKが運営する、
個人開発者・一人社長を応援するメディアMagnyxaがお届けしています。
個人開発のバックエンド、コールドスタートで1分半かかってた。インスタンス増やす前にやるべきだったこと
開発中のアプリ、初回アクセスだけやたら遅かった。
普段は普通に動く。でも、しばらく誰もアクセスしてない状態から最初の1回目だけ、異常に遅い。
Cloud Run(Googleのサーバーで、アクセスがないと自動で止まって、来たら起きる仕組み)で動かしてるGoのバックエンド。
いわゆるコールドスタートです。
「まあサーバー代をケチってるから仕方ないか」と思っていましたし、
お金を払ってインスタンス(常に起きてるサーバー)を増やそうかと思いもしました。
でもその前に、一応コードを見てみたところ大問題。
1分25秒。ログに残ってた数字
Cloud Runのログを一部お見せすると。
INFO 2026-04-14T14:38:22.556197Z
[httpRequest.requestMethod: GET]
[httpRequest.status: 200]
[httpRequest.responseSize: 3.17 KiB]
[httpRequest.latency: 85.557 s]
[httpRequest.userAgent: Chrome 146.0.0.0]
latency: 85.557 s
1分25秒。
ただのGETリクエスト(ページを開くだけの操作)に、1分25秒。
いや、さすがにおかしい。
コールドスタートが遅いのはわかる。でも1分半は異常です。
ログを時系列で追ったら、犯人が見えた
起動時のログを上から順番に見ていく。
14:38:22 [325.447ms] SELECT c.column_name, constraint_name
FROM information_schema.table_constraints...
14:38:27 [310.414ms] SELECT a.attname as column_name
FROM pg_attribute...
14:38:28 [310.361ms] SELECT description
FROM pg_catalog.pg_description...
14:38:32 [319.037ms] SELECT count(*)
FROM pg_indexes WHERE tablename = 'users'...
14:38:35 [309.107ms] SELECT count(*)
FROM INFORMATION_SCHEMA.table_constraints...
(延々と続く)
14:39:46 Database initialized successfully with GORM AutoMigrate
14:39:47 Starting server on port 8080
14:38:22に起動が始まって、14:39:47にやっとサーバーが立ち上がってる。
85秒間、ずっとデータベースの中身を調べるクエリが走り続けていた。
結論、犯人はGORMのAutoMigrateでした。
コールドスタートが遅いとき、初期実行時のコードを疑え
個人開発のバックエンドがCloud Runで動いていて、
コールドスタートが遅いとき、まず疑うべきはサーバ起動時にまとめて行っている処理に無駄なものが入っていないか。
特に今回私はGoを使っていて
GORM(GoのORM。データベースを操作するためのツール)には、AutoMigrateという機能がありますが、これがコールドスタート時に毎回起動していたことが大きな原因でした。
これは「コードで書いたテーブルの定義と、実際のデータベースの中身を見比べて、差分があったら自動で直してくれる」というものですが、
開発中はめちゃくちゃ便利でテーブルの構造を変えても、起動するだけで勝手にデータベースが更新される。
問題は、この「見比べる」作業がめちゃくちゃ重いこと。
毎回起動するたびに、データベースの裏側にある管理情報(information_schema とか pg_attribute とか)を全部なめにいく。
テーブルが1個ならすぐ終わる。
でもうちは数十個のテーブルある。
1テーブルにつき300ミリ秒(0.3秒)くらいのクエリが何本も走る。
しかも、データベース側もNeon(サーバーレスのPostgreSQL。こっちもアクセスがないと止まる)を使ってるので、データベース自体のコールドスタートと掛け算になる。
サーバーが起きるのに時間がかかる。起きた直後にデータベースも起きる。起きたばかりのデータベースに大量のクエリを投げる。
そりゃ1分半かかるわけです。
初回起動時の処理は削ぎ落とす
今回の例で言えばやったことはシンプル。
本番ではAutoMigrateを動かさないようにした。
それだけです。
// 本番ではスキップ。テーブル構造を変えたときだけ
// RUN_AUTOMIGRATE=true で実行する
runAutoMigrate := env != "production" ||
os.Getenv("RUN_AUTOMIGRATE") == "true"
if runAutoMigrate {
log.Println("Running GORM AutoMigrate...")
if err := db.AutoMigrate(
// ... 数十 テーブル分
); err != nil {
return nil, err
}
log.Println("Database initialized successfully with GORM AutoMigrate")
} else {
log.Println("AutoMigrate skipped (production mode)")
}
- ローカル(手元のPC)で開発してるときは、今まで通りAutoMigrateが動く。テーブルの構造をいじったら勝手に反映される
- 本番では、毎回の起動時にAutoMigrateを動かさない。テーブルの構造なんてそう頻繁に変わらないから
- テーブルの構造を変えたときだけ、一時的に
RUN_AUTOMIGRATE=trueをセットしてデプロイする。反映されたら外す
これだけでした。
AutoMigrateに限った話ではなく
サーバが常駐しているのであれば初回の起動のみで意識することがないオーバーヘッドもコールドスタートの場合は考慮する必要があるということです。
1分半が3秒になった
変更後のログ。
AutoMigrate skipped (production mode, RUN_AUTOMIGRATE not set)
[latency: 3.475 s]
3秒台。
85秒が3.5秒になった。
インスタンスを常駐させる必要がなかったわけです。つまり追加のサーバー代ゼロ。
個人開発でコールドスタートが遅いとき、まずやること
今回の話をまとめると、こうです。
- インスタンスを増やす(=お金を払う)前に、まずコードを疑う。
- ログを時系列で追えば、犯人は見える。 何に何秒かかってるか、しっかり監視しましょう
- コールドスタートのプロジェクトでは初期化時のオーバーヘッドを削ぎ落とせ
- 今回でいえばAutoMigrateは開発中だけ使う。 本番で毎回動かすと、テーブルが増えるほど起動が遅くなる
- 個人開発でも、ちゃんと計測する癖をつける。 「なんか遅いな」で終わらせない。ログを見る。数字を見る。実行時間で料金が発生する場合、それだけで無駄な出費となります
初期実行の処理に気をつけて。
Magnyxaでは、AIや新しい技術の「こんなこともできるの?」を追いかけています。
気になる方は Magnyxa をチェックしてみてください。
自分のサービスを広めたい方は Magnyxaのプラットフォーム もぜひご活用ください。
「AIを使ってみたいけど何から始めればいいかわからない」という方は、運営元の株式会社ZELKまでお気軽にご相談ください。
個人開発のツールといえば、Claude Codeは開発ツールじゃない -- 一人社長の「もう一人の自分」だったという話も書いている。こっちも読んでみてください。
参照ソース:




