概要
Next.js を standalone ビルドし、Docker イメージを極限まで軽量化した際に、
アプリ自体は正常に動作しているのに healthcheck が unhealthy になる という罠にハマった。
本記事では、
- 発生した症状
- 原因の切り分け
- なぜこの問題が起きるのか
- 正しい解決方法
- standalone + Docker 運用時の注意点
を、後から見返してすぐ再現・回避できる形で整理する。
環境
- GCP e2-micro
- Docker / docker-compose
- Next.js(standalone)
- Nginx(別コンテナでリバースプロキシ)
- curl 等のデバッグツールは **あえて入れない** 最小構成
発生した症状
- ブラウザからは `https://example.com/healthz` が **200 OK**
- Nginx 経由の通信も問題なし
- しかし `docker ps` では frontend コンテナが
になる。
初期調査
1. curl が入っていない
👉 standalone + 軽量化の結果、curl は存在しない(これは想定通り)
2. node で healthz を直接叩く
❌ 接続失敗
しかし、
👉 ここで決定打
原因(結論)
Next.js standalone は localhost で listen していない
standalone ビルドされた Next.js は、
- `127.0.0.1` / `localhost` では listen しない
- Docker ネットワーク上の **eth0(172.18.x.x)でのみ listen**
している。
そのため:
| アクセス元 | 結果 |
|---|---|
| ブラウザ(Nginx 経由) | OK |
| Nginx コンテナ | OK |
| docker exec localhost | ❌ |
| healthcheck(localhost 前提) | ❌ |
👉 アプリは正常、healthcheck の宛先だけが間違っていた
よくある誤解
- 「Next.js が落ちている」 → ❌
- 「standalone ビルドが壊れている」 → ❌
- 「Docker の不具合」 → ❌
実際は:
**healthcheck の設計が standalone と噛み合っていないだけ**
解決方法(正解ルート)
localhost を使わない
healthcheck では eth0 の IPv4 アドレスを取得して叩く。
docker-compose.yml(完成形)
なぜ curl を入れないのか
- イメージサイズ増加
- RSS 増加
- 攻撃面の増加
👉 node があるなら node で十分
standalone 最小構成では curl 依存は避ける。
最終確認
これで解決。
注意点まとめ
- standalone + Docker では localhost 前提を疑う
- healthcheck は「アプリが listen しているアドレス」を叩く
- 軽量化するとデバッグツールは消える(想定内)
- unhealthy = アプリ異常とは限らない
総括
この問題は、
- standalone
- 極限までの軽量化
- 正しい Docker ネットワーク理解
まで踏み込んだ人だけが遭遇する 最適化の副作用。
逆に言えば、ここまで来ていれば設計は正しい。
同じ構成を再現する際は、
最初から eth0 ベースの healthcheck を書くことで回避できる。
以上、Next.js standalone + Docker healthcheck の罠と解決の備忘録。




