Heroku から Fly.io へ移行してインフラ費を 73% 削減する
BtoB の業務システム(Rails 8 / PostgreSQL)を Heroku から Fly.io へ移行しました。
この 1 本目では、移行を決めた理由と、何をどう選んだかを書きます。 実際の手順は 2 本目以降で紹介します。
要点
- Heroku から Fly.io へ移行し、月額は約 $360 から約 $98 になった
- 同時に Delayed Job を Solid Queue へ、Heroku Scheduler を Solid Queue Recurring Tasks へ統合した
- コストが下がったにもかかわらず、Web サーバーは 1 台から 2 台構成へ冗長化できた
- プラットフォームの移行とジョブ基盤の移行は、障害の原因を切り分けられるよう別のフェーズにした
移行前の構成とコスト
| 項目 | 内容 | 月額 |
|---|---|---|
| Web | Performance-M ×1(2.5GB RAM / 1 CPU 専有) | $250 |
| Worker | Standard-2X ×1(1GB RAM, Delayed Job) | $50 |
| DB | Heroku Postgres Standard-0(4GB / 120 connections) | $50 |
| Scheduler | Heroku Scheduler(3ジョブ) | $0 |
| ログ | Papertrail | $7〜 |
| 合計 | 約 $360 |
課題
Heroku の新機能開発の停止
Salesforce は Heroku の新機能開発を停止し、開発資源を Agentforce に移しました1。 プラットフォームとしての機能面の進化は、もう期待できません。
インスタンススペックの刻みの粗さ
Web
Performance-M は 1 CPU 専有の 2.5GB です。 これに対して Heroku Metrics の実測値は load AVG 0.01 / MAX 0.51 で、完全にオーバースペックでした。 かといって一段下の Standard-2X に落とすと、メモリが 1GB しかなく足りません。 「CPU は要らないがメモリは 2GB 欲しい」という要求に当てはまる選択肢がありませんでした。
Worker
Excel と CSV のファイル生成ジョブで、R14/R15(メモリクォータ超過)が頻発していました。 ここでも Standard-2X の次は Performance-M の 2.5GB で、必要な量に対して大きすぎます。 メモリ不足そのものはコードを最適化して解消し、Standard-2X に戻しました。 移行の理由として残ったのは、必要な量にちょうど合うサイズがないことだけです。
Web が 1 台だけの構成
Web が 1 台しかなく、単一障害点になっていました。 ミッションクリティカルなシステムではないため、この構成はクライアントと事前に合意しています。 とはいえデプロイのたびに瞬断が起きるので、実行するタイミングに気をつける必要がありました。
Fly.io の選定理由
最初から Fly.io に決めていたわけではありません。 今回の構成と要件を前提に、Heroku の継続利用、Render、AWS も並べて検討しました。
- Heroku:運用は快適だが、コストが高い。前述のとおり機能面の進化も見込めない
- Render:移行は容易だが、開発者ごとの課金とインスタンス料金を合わせるとコストの差が小さい
- AWS:柔軟性は高いが、運用負荷が増える。今回の目的は運用コストを増やさずにインフラ費を下げることなので、釣り合わない
- Fly.io:必要なスペックを細かく選べ、東京リージョンもある。コストと運用負荷の釣り合いが最もよい
Fly.io なら shared-cpu-8x / 2GB という「CPU は多いがメモリは控えめ」な組み合わせが選べます。 しかも 2 台に増やしても、Heroku の Web 1 台より安く収まる計算になりました。
何を選んだか
| トピック | 決定 | 理由 |
|---|---|---|
| DB | Fly Managed Postgres (Basic) | 同一プラットフォーム内で低遅延。PgBouncer 内蔵、接続数 200(Heroku Standard-0 は 120) |
| Region | 東京(nrt)単一 | ユーザーが日本国内のみ |
| ジョブ基盤 | Delayed Job → Solid Queue | Rails 8 公式路線。Recurring Tasks でスケジューラも統合できる |
| スケジューラ | Solid Queue Recurring Tasks | 専用プロセス不要、設定が 1 ファイルに集約 |
| APM | New Relic を継続 | 環境変数だけで動く。移行コストゼロ |
| ログ | fly-log-shipper → S3 | Fly の標準 UI は live tail 中心で長期検索に使えない |
| 戦略 | 段階移行(2 フェーズ) | 同時に動かすと、障害が出たときにプラットフォームとジョブ基盤のどちらが原因か切り分けられない |
名前の似た 2 つの Postgres 製品
Fly.io には名前の似た Postgres が 2 つあります。
- Fly Postgres:自己管理型。HA もバックアップも障害対応も自前で用意する。Fly.io はこの構成についてサポートも案内も行わないと明記しており、本番用途にはマネージド版を勧めています2
- Fly Managed Postgres(MPG):マネージド。自動バックアップ、HA、接続プーリング(PgBouncer)を備える
Heroku Postgres と同等の運用感が欲しいなら後者です。 料金比較の記事で前者の額だけを見て安いと判断すると、自前で負う運用の手間を丸ごと見落とします。
| Fly MPG Basic | Heroku Standard-0 | |
|---|---|---|
| 月額 | $38 + ストレージ $0.28/GB | $50 |
| 最大クライアント接続 | 200 | 120 |
| 接続プーリング | PgBouncer 内蔵 | なし |
| HA / 自動バックアップ | あり | あり |
安いうえに接続数の上限が高く、PgBouncer も最初から入っています。 Heroku では上限を気にして Puma のスレッド数を絞っていましたが、その制約が緩みました。
ステージング用 DB の置き場所
ステージングへのアクセス頻度が極小だったので、1 つのクラスターの中に本番用とステージング用の DB を並べました。
- Cluster(Basic / 200 connections / 1GB RAM)
myapp_staging… staging アプリ用myapp_production… production アプリ用
fly mpg attach を実行すると、アプリごとに DB とユーザーが自動で作られ、DATABASE_URL も secret に入ります。
データは完全に分離されます。
接続数の試算は本番 25〜50 とステージング 5〜10 で、上限 200 に対して十分な余裕があります。 ステージング用に別のクラスターを立てて月 $38 を足す必要はない、という判断です。
移行後の構成
アプリも DB も東京リージョン(nrt)の Fly.io 上にあり、両者の間にインターネットを挟みません。
| 構成要素 | 内容 |
|---|---|
| Web | shared-cpu-8x(2GB)× 2。ローリングデプロイのため冗長化 |
| Worker | shared-cpu-8x(2GB)× 1。Solid Queue の worker / dispatcher / scheduler が動き、scheduler が Recurring Tasks を起動する |
| DB | Fly Managed Postgres(Basic)。HA、自動バックアップ、PgBouncer 内蔵、200 接続 |
Solid Queue が「ジョブ実行」と「定期実行」の両方を担うため、Heroku Scheduler という独立した add-on が 1 つ消えました。
外部サービスは移行の前後で変えていません。
| 外部サービス | 扱い |
|---|---|
| New Relic | 環境変数のみで継続使用 |
| AWS S3 / SNS | 変更なし |
| ログ | fly-log-shipper 経由で S3 へ |
サイジングの根拠
| プロセス | スペック | 台数 | 根拠 |
|---|---|---|---|
| Web | shared-cpu-8x (2GB) | 2 台 | Performance-M(2.5GB / 1 CPU 専有)での実測が load AVG 0.01 / MAX 0.51。冗長化 + ローリングデプロイで無停止 |
| Worker | shared-cpu-8x (2GB) | 1 台 | 普段アイドル + バースト型のワークロード。並列度は環境変数で調整 |
移行前に実測値を持っていたので、思い切ってスペックを落とせました。 実測がなければ「Performance-M 相当だから」と過剰なスペックを選び、削減幅はずっと小さくなっていたはずです。 現行環境の実測値を先に取っておくことをおすすめします。
結果
| Heroku | Fly.io | |
|---|---|---|
| Web | $250(Performance-M ×1) | $31(shared-cpu-8x 2GB ×2) |
| Worker | $50 | ~$16(shared-cpu-8x 2GB ×1) |
| Scheduler | $0 | $0(Solid Queue に統合) |
| DB | $50 | ~$40(Fly MPG Basic + storage) |
| ログ | $7〜 | $7〜10 |
| 合計 | 約 $360 | 約 $98 |
約 73% 減りました。 しかも Web は 1 台から 2 台に増えています。
金額以外で効いた点を挙げておきます。
- 無停止デプロイ:Web 2 台のローリングデプロイで、デプロイ時の瞬断が消えた
- 単一障害点の解消:1 ホストの障害で即ダウンにならない
- ステージングのアイドル停止:使っていない時間は課金されない(
auto_stop_machines。4 本目で扱います) - スケジューラ設定の一元化:設定ファイル 1 枚に集約され、UTC の暗算からも解放された
- ログの長期保管:S3 に置くことで、Papertrail に頼らず過去ログを検索できる
2 本目の内容
2 本目では、プラットフォームの移行より先に手を付けた Delayed Job から Solid Queue への置き換えを書きます。 過去に一度 revert した案件にどう再挑戦したか、移行時に飛ぶジョブをどう洗い出したかが中心です。
参考リンク
Footnotes
-
Salesforceが「Heroku」の新機能開発を停止 Agentforceに資源集中(クラウド Watch、2026年2月16日) ↩
-
Fly Postgres のドキュメント冒頭に “We are not able to provide support or guidance for unmanaged Postgres.” と書かれている。 ↩