Fly.ioからOCI + Coolifyへ移行した記録

技術

はじめに

個人開発している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での構成
APIGo / chishared-cpu-1x、1 GB
WorkerPython / FastAPIshared-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には、curlwgetも含まれていなかった。

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のバックアップ
  • 障害時の復旧

料金だけを見れば安くなる見込みだが、運用時間まで含めると完全な無料ではない。

今回の移行で得た教訓

今回、特に重要だと感じたのは次の点だった。

  1. クラウド料金は合計ではなく、compute、memory、storage、egressに分けて見る。
  2. デプロイ要求の成功ではなく、healthとcommit SHAまで確認する。
  3. 環境変数はキー名だけでなく、値の型も管理する。
  4. DNS以外に旧URLを保持するサービスを洗い出す。
  5. バックアップは取得するだけでなく、restoreまで試す。
  6. セルフホストでは、料金と引き換えに運用責任が増える。

まとめ

今回の移行は、「Fly.ioよりOCIのVMが安いから」という単純な話ではない。

Fly.ioの請求の約75%を占めていたegressを、OCIの無料枠へ移すことが目的だった。

実際に難しかったのは、OCIのVMを作ることではなく、環境変数、デプロイ順序、Vercelのfallback URL、Inngestの接続先といった、インフラの周辺に残る依存関係だった。

Coolifyによって、Fly.ioに近いデプロイ体験の一部は維持できた。

一方で、OS更新、ディスク管理、バックアップ、障害復旧は自分の責任になる。

egress料金が大きく、ある程度の運用を引き受けられるサービスであれば、OCI + Coolifyは十分現実的な選択肢だと思う。

コメント

タイトルとURLをコピーしました