はじめに
個人開発しているSiftoのGo APIとPython Workerを、Fly.ioからOracle Cloud Infrastructure(OCI)上のCoolifyへ移行した。
理由は、CPUやメモリではなくネットワークのegress料金だった。
2026年6月のFly.io請求額は$60.09。そのうち東京リージョンのegressが$45.29で、全体の約75%を占めていた。転送量は約1.13 TBだった。
Machineを小さくしても、scale-to-zeroを調整しても、請求の大半は残る。そこで、公共インターネット向けegressが月10 TBまで無料となるOCIへ移行することにした。
移行前後の構成
移行対象は次の2サービス。
| サービス | 実装 | Fly.ioでの構成 |
|---|---|---|
| API | Go / chi | shared-cpu-1x、1 GB |
| Worker | Python / FastAPI | shared-cpu-1x、1 GB |
フロントエンドはVercelを継続利用し、PostgreSQL、Meilisearch、Redis、Cloudflare R2なども既存構成を維持した。
移行後の構成は次のとおり。
OCI VM.Standard.A1.Flex
2 OCPU / 12 GB RAM
Ubuntu 24.04 LTS ARM64
Boot Volume 150 GB
└─ Coolify
├─ Go API
└─ Python Worker
APIとWorkerは、Coolify上で別々のApplicationとして管理している。
これにより、デプロイ、ログ、ヘルスチェック、再起動を個別に扱える。
なぜOCI + Coolifyにしたのか
Fly.ioは、アプリを短時間で公開するには非常に便利だった。
ホストOSやDockerの管理は不要で、TLSやデプロイ、Machineの管理も任せられる。
ただしSiftoの場合、請求の中心はコンピュートではなくegressだった。通信量を大きく減らせない限り、CPUやメモリを最適化しても効果は小さい。
OCIであれば現在の約1.13 TB/月は無料枠内に収まる見込みで、2 OCPU / 12 GBのリソースも確保できる。
その代わり、次の運用は自分で持つことになる。
- UbuntuとDockerの更新
- Coolifyの管理
- ディスクとログの監視
- バックアップと復旧
- 単一VM障害への対応
Coolifyを入れても、Fly.ioと同じマネージド環境になるわけではない。デプロイは楽になるが、ホストの責任は残る。
GitHub Actionsから順番にデプロイする
CoolifyにはGitHubへのpushを検知して自動デプロイする機能がある。
しかしSiftoでは、アプリを更新する前にDB migrationを実行する必要がある。自動デプロイを有効にすると、migrationより先に新しいAPIが起動する可能性がある。
そこでProductionではAuto Deployを無効にし、GitHub Actionsからデプロイ順序を制御した。
設定チェック
↓
DB migration
↓
Workerをデプロイ
↓
Workerのhealth確認
↓
APIをデプロイ
↓
APIのcommit SHA確認
↓
Inngestを同期
Coolify APIへデプロイ要求を送るだけでなく、デプロイ完了までstatusを監視する。
APIの/healthではcommit SHAを返し、GitHub Actionsがデプロイ対象のSHAと一致するか確認するようにした。
これで「デプロイ要求は成功したが、本番では古いコンテナが動いている」といった状態も検出できる。
移行で詰まったポイント
ヘルスチェック用のcurlが入っていなかった
Python Workerは正常に起動していたが、Coolifyのヘルスチェックに失敗した。
/bin/sh: 1: curl: not found
/bin/sh: 1: wget: not found
使用していたpython:3.12-slimには、curlもwgetも含まれていなかった。
Dockerfileへcurlを追加して解決した。
アプリが起動していることと、プラットフォームのヘルスチェックが通ることは別問題だった。
環境変数のJSONで失敗した
Google Service AccountのJSONを、複数行のまま環境変数へ登録したところ、Docker Composeに別々の変数として解釈された。
unexpected character "\"" in variable name
JSONは1行にするか、Multiline設定やbase64を利用する必要がある。
環境変数はキー名だけでなく、JSON、URL、秘密鍵、tokenといった値の型まで管理しておくべきだった。
Vercel側にFly.ioのURLが残っていた
DNSをOCIへ切り替えたあとFly.ioを停止すると、Vercelのフロントエンドも動かなくなった。
Next.jsのrewriteやserver-side routeに、Fly.ioのURLがfallbackとして残っていたためだった。
インフラ側のDNSだけを変更しても、次のような設定は自動では切り替わらない。
- アプリ内の固定URL
- Webhook
- Scheduler
- OAuth redirect URL
- Callback URL
- CORS設定
Inngestが旧URLを参照していた
APIとWorkerは動いていたが、定時jobが実行されなくなった。
Inngestに登録されていた接続先が、Fly.ioのURLのままだったためだ。
対策として、APIデプロイ成功後にGitHub ActionsからInngestを同期するようにした。
DNS以外に接続先を保持している外部サービスも、移行対象として洗い出す必要がある。
移行後に変わったこと
良くなった点は次のとおり。
- 約1.13 TB/月のegressを無料枠内に収められる見込み
- APIとWorkerに2 OCPU / 12 GBを使える
- Coolifyからログや環境変数を管理できる
- GitHub Actionsのデプロイ順序が明確になった
- Fly.io固有のMachine管理処理を削除できた
一方で、運用負荷は増えた。
- OSやDockerの更新
- Docker image、build cache、ログの削除
- ディスク容量の監視
- Coolifyのバックアップ
- 障害時の復旧
料金だけを見れば安くなる見込みだが、運用時間まで含めると完全な無料ではない。
今回の移行で得た教訓
今回、特に重要だと感じたのは次の点だった。
- クラウド料金は合計ではなく、compute、memory、storage、egressに分けて見る。
- デプロイ要求の成功ではなく、healthとcommit SHAまで確認する。
- 環境変数はキー名だけでなく、値の型も管理する。
- DNS以外に旧URLを保持するサービスを洗い出す。
- バックアップは取得するだけでなく、restoreまで試す。
- セルフホストでは、料金と引き換えに運用責任が増える。
まとめ
今回の移行は、「Fly.ioよりOCIのVMが安いから」という単純な話ではない。
Fly.ioの請求の約75%を占めていたegressを、OCIの無料枠へ移すことが目的だった。
実際に難しかったのは、OCIのVMを作ることではなく、環境変数、デプロイ順序、Vercelのfallback URL、Inngestの接続先といった、インフラの周辺に残る依存関係だった。
Coolifyによって、Fly.ioに近いデプロイ体験の一部は維持できた。
一方で、OS更新、ディスク管理、バックアップ、障害復旧は自分の責任になる。
egress料金が大きく、ある程度の運用を引き受けられるサービスであれば、OCI + Coolifyは十分現実的な選択肢だと思う。


コメント