個人開発アプリ 5 つを Google Cloud から Oracle Cloud へ移行した話

エンジニアにとって、個人アプリケーション開発はとても楽しいものですよね。ただ、利用者がほとんどいない個人開発のアプリであっても、クラウドの請求は毎月来ます。私が以前利用していた Google Cloud は、非常に使いやすく、サービスや機能も豊富です。それでも料金は、個人で払い続けるには重いと感じていました。アクセスがなくてもデータベースの料金はかかり続け、Web ゲームのように通信の多いアプリでは、遊ばれた分だけ下りの通信料(サーバから利用者へ送るデータの料金)もかさみます。
そこで目を付けたのが、Oracle Cloud Infrastructure(OCI)の無料枠(Always Free)です。Arm の VM、200GB のディスク、月 10TB の下りの通信が、期限なしで無料で使えます。Google Cloud の無料枠と比べると、個人開発にはかなり気前のよい枠です。2026 年 10 月、個人開発のアプリ 5 つを Google Cloud(Cloud Run + Cloud SQL)から OCI の Arm の VM 2 台へ移しました。準備をしておいたので、切り替えそのものは 1 日で終わりました。それでも、詰まったところは少なくありませんでした。
この記事では、Google Cloud の料金と OCI の無料枠を比べたうえで、構成図、移行の手順のサンプル、詰まりやすいところの直し方をまとめます。
この記事でわかること
- Google Cloud の料金が、個人開発で重くなりやすいところ
- OCI の無料枠の中身と注意点
- Cloud Run + Cloud SQL から OCI の VM へ移す手順(構成図・コマンド例つき)
- 移行で詰まりやすいところと、その直し方
予備知識:この記事に出てくる用語
クラウドでアプリを動かす方法は、大きく 2 つあります。サーバの準備や台数の調整をクラウド側に任せるマネージドサービスと、自分専用のサーバを借りる VM(仮想マシン)です。移す前は前者、移した後は後者が中心です。
| 用語 | どんなものか |
|---|---|
| Cloud Run | Google Cloud のマネージドサービス。アプリを入れたコンテナを預けると、リクエストに応じて台数を自動で増減する(0 台まで減らせる) |
| Cloud SQL | Google Cloud のマネージドなデータベース。PostgreSQL などを、バックアップや更新込みで貸してくれる |
| Always Free | OCI の無料枠。期限なしで、決まった量まで無料で使える |
| Ampere A1(A1) | OCI の Arm の CPU の VM。無料枠の中心。CPU の数は OCPU で数え、A1 では 1 OCPU が 1 コア |
| ホームリージョン | OCI のアカウントを作ったときに選ぶ拠点。A1 の無料枠はここでしか使えない |
| Bastion | VM へ安全に SSH するための、OCI の中継(踏み台)のサービス |
| 下り通信料 | サーバから利用者へ送ったデータの量でかかる料金。OCI の資料では「外向きの通信」 |
| Cloudflare | サイトの手前で通信の中継・キャッシュ・攻撃の防御をするサービス |
| Docker・コンテナ | アプリと動作に必要なものをひとまとめにして、どこでも同じように動かす仕組み |
Google Cloud の料金は、個人開発には重い
Cloud Run は、リクエストがないときは 0 台まで減らせるので、アクセスの少ないアプリならほぼ無料で動きます。重かったのは、次の 2 つです。
- データベースは、使っていなくても料金がかかる。 Cloud SQL には、ずっと使える無料枠がありません(30 日の試用のみ)。インスタンスが動いている時間に課金されるので、アクセスがなくても請求が来ます。アプリを 1 つ作るたびに、毎月の固定費が 1 つ増えていきます。
- 下り通信料に上限がない。 Cloud Run からインターネットへの下りの通信は量に比例して課金され、無料分は北米からの月 1GB だけです。Web ゲームのように WebSocket で状態を送り続けるアプリでは、遊ばれるほど請求が伸びます。しかも WebSocket はつながっている間ずっと「処理中のリクエスト」として扱われるので、誰か 1 人が画面を開いているだけで Cloud Run の課金も続きます。
夜間だけ止める、通信量が急に増えたら止める、といった節約もできます。ただ、どれも機能を削る方向の工夫で、「人気が出るほど費用が心配になる」形は変わりませんでした。
OCI の無料枠は、個人開発にはとても優秀
両社の無料枠を並べると、差がよくわかります(2026 年 10 月時点の公式ドキュメントから)。
| 項目 | Google Cloud の Free Tier | OCI の Always Free |
|---|---|---|
| VM | e2-micro 1 台(米国のリージョンのみ。メモリ 1GB) | A1 で合計 2 OCPU・メモリ 12GB(東京・大阪でも可) |
| ディスク | 30GB | 200GB |
| マネージドのデータベース | なし(30 日の試用のみ) | Autonomous Database が 2 つ |
| 下りの通信 | 製品ごとに北米から月 1GB など | 月 10TB |
2 コア・12GB あれば、利用者の少ないアプリを Docker でいくつも動かせます。データベースも VM の中に置けば、アプリを増やしても費用は増えません。そして下りの通信が月 10TB まで無料なので、通信の多いゲームでも請求を気にせずに済みます。
実際の月額(移す前の Google Cloud と、移した後の OCI)は、請求が締まったあとでこの記事に追記します。
ただし、注意点もあります。
- A1 の枠は 2026 年 6 月に半分になった。 以前は 4 OCPU / 24GB でした(InfoQ の記事)。今も古い数字の記事が多いので、公式のドキュメントで確かめてください。この記事の構成は、今の枠を前提にしています。
- A1 は作れないことがある。 空きがないと「Out of host capacity」になります。ホームリージョンでしか無料にならないので、時間をおいて作り直すしかありません。
- 使われていない VM は回収されることがある。 Always Free のインスタンスは、7 日間の CPU・ネットワーク・メモリの使用率がどれも 20% 未満だと回収される場合があります。一般に、従量課金(Pay As You Go)のアカウントで作った VM は対象外とされています。枠の中の分は、従量課金にしても無料のままです。
移す前と後の構成
移したのは、私が個人開発している棋譜蔵・minotake・Meooow・LUDORA・このブログの 5 つです。どれも Go のサーバと PostgreSQL の組み合わせです。

移す前は、アプリごとに Cloud Run と Cloud SQL を持つ、個人開発ではよくある構成でした。

移した後は、A1 の無料枠(合計 2 OCPU / 12GB)を、1 OCPU / 6GB ずつの VM 2 台に分けています。
- Web アプリ用の VM
通信の少ない Web アプリを、それぞれ別のコンテナで動かします。入口の Caddy(リバースプロキシ)が、ドメインごとに振り分けます。データベースは PostgreSQL で、アプリごとに分けています。 - ゲーム用の VM
通信と CPU を使うゲームは、ほかのアプリに影響しないよう別の VM にしました。
VM の 22 番(SSH)はインターネットに開けず、管理は Bastion 越しに行います。Web アプリ用の VM の 443 番は Cloudflare の IP 範囲だけに開けています。データベースとファイルは、毎日 Cloudflare R2 へバックアップしています。
今まで Google Cloud のマネージドサービスに任せていた自動スケール・バックアップ・OS やデータベースの更新は、自前の仕組みで引き受けました。
移行の手順
大事な方針は、本番の動きを変えない準備を先に全部済ませ、最後に短い停止で切り替えることです。アプリ名は app-a のような仮のものです。
1. Google Cloud への依存を洗い出す
| Google Cloud | 移した後の例 |
|---|---|
| Cloud Run | VM の中の Docker(arm64 のイメージが要る) |
| Cloud SQL | VM の中の PostgreSQL(バックアップは自分で) |
| Cloud Storage | OCI Object Storage(S3 互換の API で読み書き) |
| Cloud Scheduler | systemd タイマー |
Google Cloud 専用のライブラリを使っている所は、設定で切り替えられるようにしておきます。既定を今までのままにしておけば、Google Cloud の本番の動きは変わりません。
2. OCI 側を用意する
ホームリージョン(東京か大阪)でアカウントを作り、VCN(仮想ネットワーク)・A1 の VM・Bastion を作って、VM に Docker を入れます。443 番は Cloudflare の IP 範囲だけに開けます。
3. arm64 のイメージを作る
A1 は Arm の CPU なので、amd64 向けのイメージは動きません。docker buildx で両方を作れば、同じタグを Cloud Run でも A1 でも使えます。Go のようにクロスコンパイルできる言語なら、ビルドの段を手元のアーキテクチャで動かすと、エミュレータ(QEMU)なしで速く作れます。
FROM --platform=$BUILDPLATFORM golang:1.27.3 AS build
ARG TARGETOS TARGETARCH
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /server ./cmd/server
# 実行の段には RUN を置かない(置くと QEMU が必要になる)
FROM gcr.io/distroless/static-debian12
COPY --from=build /server /server
ENTRYPOINT ["/server"]
# 複数のアーキテクチャを作れるビルダー
docker buildx create --use
docker buildx build --platform linux/amd64,linux/arm64 -t <レジストリ>/app-a:<タグ> --push .
4. データの移行を練習する
Cloud SQL Auth Proxy でつなぎ、pg_dump で書き出して、VM の PostgreSQL(Bastion 越しの SSH トンネル)へ pg_restore で入れます。
./cloud-sql-proxy --port 15432 <プロジェクト>:<リージョン>:<インスタンス> &
pg_dump -h 127.0.0.1 -p 15432 -U app_a -d app_a -Fc --no-acl -f app_a.dump
pg_restore -h 127.0.0.1 -p 5432 -U app_a -d app_a --no-owner --no-acl app_a.dump
入れたら、両側の全テーブルの行数を比べて一致を確かめます。psql の \gexec で、テーブルごとの count(*) をまとめて流せます。
SELECT format('SELECT %L AS table_name, count(*) FROM %I.%I', table_name, table_schema, table_name)
FROM information_schema.tables
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
ORDER BY table_name
\gexec
ファイルは rclone sync で Cloud Storage から Object Storage へ写せます(Object Storage は「顧客秘密キー」を作ると S3 互換で使えます)。逆向きの移行と、バックアップから戻す練習もしておくと、何かあったときに戻せます。
5. 定期処理を systemd タイマーへ移す
今まで Cloud Scheduler が担ってくれていたメール送信タスクなどの定期実行処理を、 systemd タイマーに記述する形に変更します。cron を使用する方法もあります。
# /etc/systemd/system/app-a-cleanup.timer(同名の .service で処理を書く)
[Timer]
# VM の時計は UTC が既定なので、タイムゾーンを書く
OnCalendar=*-*-* 04:30:00 Asia/Tokyo
Persistent=true
[Install]
WantedBy=timers.target
6. 切り替えて、片付ける
- Google Cloud への書き込みを止める(Cloud Run の公開を外し、Cloud Scheduler を止める)
- データを移し、行数の一致を確かめる
- Cloudflare の DNS を VM へ向ける(プロキシ越しなら数十秒〜数分で切り替わる)
- OCI 側のタイマーを動かし、ログイン・書き込みなどを目で確かめる
- しばらく様子を見てから、Google Cloud の資源を消す
停止は、アプリ 1 つあたり数十分を見込んでおけば十分でした。
トラブルシューティング
| 症状 | よくある原因 | 対処 |
|---|---|---|
| A1 の VM が「Out of host capacity」で作れない | ホームリージョンに空きがない | 時間をおいて作り直す。別の可用性ドメインを試す |
コンテナが exec format error で起動しない | amd64 のイメージを arm64 で動かした | arm64 を含めて作り直す |
| Cloudflare のエラー 521・522 | 443 番が閉じている | セキュリティリストと VM の中の iptables の両方を確かめる |
| Cloudflare のエラー 526 | 証明書が Full (strict) で通らない | Cloudflare の Origin CA などの証明書を置く |
| PostgreSQL 18 のコンテナが起動直後に止まる | ボリュームを /var/lib/postgresql/data に付けた | 18 からは /var/lib/postgresql に付ける |
| 定期処理が 9 時間ずれる | VM の時計が UTC | OnCalendar にタイムゾーンを書く |
| 更新したのに古い JS・CSS が返る | Cloudflare のキャッシュ | URL に版(?v=...)を付ける |
| Google Cloud の資源が消せない | 削除保護・VPC のピアリングが残っている | Terraform の deletion_protection を外す。ピアリングを先に消す |
ファイアウォールは 2 段ある
OCI では、VCN のセキュリティリストと、VM の中の iptables の 2 段で通信を絞っています。OCI の Ubuntu のイメージには、INPUT の最後で通信を断る規則が最初から入っています。Docker で公開したポートは Docker の規則で通りますが、VM で直接待ち受けるソフトは届きません。その場合は、/etc/iptables/rules.v4 の REJECT の行の前に 443 番を通す規則を書き足します。
利用者の IP が、すべて Cloudflare のものになる
Caddy は、信頼するプロキシを設定していないと、届いた X-Forwarded-For を直前の接続相手(Cloudflare)の IP で作り直します。すると、レート制限やアクセス解析が Cloudflare の拠点ごとにまとめて数えられてしまいます。Cloudflare が付ける CF-Connecting-IP で置き換えます。
https://app-a.example.com {
reverse_proxy app-a:8080 {
header_up X-Forwarded-For {http.request.header.CF-Connecting-IP}
}
}
このヘッダは偽装できるので、443 番を Cloudflare の IP 範囲だけに開けておくことが前提です。
新旧の環境から、同じメールが 2 通届く
練習で本番のデータを OCI 側へ写すと、OCI 側の定期処理も本番のデータを見て動き出し、Google Cloud と合わせて同じメールが 2 通届いてしまいます。切り替えまでは OCI 側のタイマーを止めておきます。目印のファイルがあるときだけ動くようにしておくと確実です。
# タイマーから起動する .service の [Unit] に足す
ConditionPathExists=/srv/app/live
まとめ
- Google Cloud は使いやすいが、Cloud SQL の固定費と上限のない下り通信料が、個人開発には重い
- OCI の無料枠は、Arm の VM(合計 2 OCPU / 12GB)・200GB のディスク・月 10TB の下りの通信が期限なしで無料。個人開発にはとても優秀
- 移行は、本番を変えない準備を先に済ませ、データの移行と復元を練習してから、短い停止で切り替える
- 詰まりやすいのは、arm64 のイメージ、2 段のファイアウォール、Cloudflare 越しの IP、新旧の環境の二重送信
自動スケールや止まらない運用が要るなら、マネージドサービスの料金は払う価値があります。一方で「月々の請求をできるだけ 0 に近づけたい」「通信量を気にせずに作りたい」個人開発なら、OCI の無料枠は第一の候補になると思います。実際の費用や運用してわかったことは、あとで追記します。
参考
- Always Free Resources(Oracle Cloud Infrastructure Documentation)
- Oracle Quietly Halves Free Tier Ampere A1 Compute Limits with No Public Announcement(InfoQ)
- Free cloud features and trial offer(Google Cloud)
- Cloud Run の料金(Google Cloud)
- Multi-platform builds(Docker Docs)
- postgres(Docker Hub)
- HTTP headers(Cloudflare Docs)