Sougen Blog

個人 AWS で毎月 4 万円溶かしていたので大掃除した

awscostcloudformationhomelab

個人アカウントの AWS で、月 ¥38,000 くらい払っていた状態が 2 ヶ月続いていました。
プロモーションクレジットが適用されていたため利用額に気付かず、残高がなくなって実費請求が来てから確認しました。
2 日かけて大掃除した結果、月 ¥2,200 まで下がった (94% 減) ので、何にお金が消えていたのかと、どう削ったのかを記録します。

クレジットの枯渇で気付く

去年の re:Invent でもらった数百ドル分の AWS クレジットが残っていて、実費はほぼゼロで運用できていました。
クレジット残高は AWS Console の Billing → Credits からしか見えません。
CLI にも list-credits のような公式 API はないため、cron で監視することもできず、気付いたら残高がゼロになっていました。

適用状況を後追いで見るには、Cost Explorer の UnblendedCost と NetUnblendedCost の差分を取ります。

aws ce get-cost-and-usage \
  --time-period Start=2026-06-01,End=2026-09-01 \
  --granularity MONTHLY \
  --metrics UnblendedCost NetUnblendedCost

UnblendedCost - NetUnblendedCost が実際に適用されたクレジット額です。
今回の場合、2026 年 6 月まではこの差分が月 ¥30,000 前後あり、7 月に入ってから急にゼロになりました。

大掃除の対象は主に次の 4 つ

1. 休止中の個人サービスの残存リソース (月 ¥25,000 以上)

運用していた「完全受注生産の衣服を扱う LINE ミニアプリ」を一時休止した際に、CloudFormation スタック一式が残ったままでした。
サービスとしては停止していても、内部で動いていた RDS インスタンス 2 系統 (dev / prod)、それぞれに紐付いた NAT Gateway、CloudFront ディストリビューション、Lambda、複数の EIP、Serverless Framework が作った serverlessdeploymentbuck-* の S3 バケット、CloudWatch Logs、周辺のサブシステム用に立てていた EC2 まで含めて課金され続けていて、これが最大の項目でした。
Cloudflare Workers のメンテナンス表示にドメインを差し替えてから、CloudFormation スタックを順に削除しました。

2. 別 VPC の遊休 NAT Gateway ¥6,750/月 (30 日間 0 byte)

上記とは別の VPC に、以前 Lambda を VPC 内で動かしていた頃の名残として NAT Gateway が 1 個残っていました。
Lambda 本体は既に削除していたため通信は 30 日間ゼロで、それでも AZ ごとの固定費で月 ¥6,750 かかっていました。
時間課金 ($0.045/h × 24 × 30) + データ処理量 (0 GB) の固定部分だけで、通信していなくても発生します。

3. EIP とパブリック IPv4 の課金 (2024-02 の仕様変更)

2024 年 2 月から、パブリック IPv4 アドレスは 使用中でも $0.005/h の課金対象 になりました。
それ以前は「関連付けられていない EIP のみ課金」だったのですが、仕様変更以降は EC2 に紐付いた EIP も、EC2 に自動割り当てされたパブリック IP も、どちらも同じ料金がかかります。

項目 1・2 に含まれない、稼働を続けている EC2 側の EIP も複数保持していて、それぞれ月 ¥550 くらい、合計で数千円になっていました。
EIP を解放して自動割り当てのパブリック IP に切り替えても料金は変わらないため、削減するにはパブリック IP アドレスを持たない構成 (プライベートサブネット + NAT Gateway か Cloudflare Tunnel) に変えるしかありません。
しかし NAT Gateway 自体の料金が高く (項目 2)、外向きにアクセスする要件があるサーバーで Tunnel を挟むのも当時は面倒だったので、パブリック IP アドレスを持たない構成には変更しませんでした。

代わりに、保持していた EIP はすべて解放して自動割り当てのパブリック IP を受け入れる形にしています。
1 台あたりの課金額は変わりませんが、複数の EIP と関連付け作業が消えて、固定 IP の管理が不要になりました。
IP アドレスが再起動のたびに変わるので、systemd timer で 5 分ごとにスクリプトを実行し、<redacted>.sougen.dev の Cloudflare DNS レコードを更新しています。

4. RDS スナップショットの重複

RDS を CloudFormation で削除した際に、FinalSnapshotIdentifier による最終スナップショットと、自分で撮っていた手動スナップショットが両方残り、合計 40 GB になっていました。
月 ¥570 で、削除するまで料金がかかります。
手元に pg_dump を保管しておくとスナップショット側は消せるのですが、DB 復元経路の予備として今も念のため保持していて、この項目は 2026-09 時点でも残っています。

リソース削除の手順

CloudFormation の二段階削除でバケットを残す

CloudFormation で作ったスタックを削除すると、その中の S3 バケットも基本的には削除されます (デフォルトの DeletionPolicy=Delete)。
中身が空のバケットは削除できますが、中身があるバケットは削除に失敗してスタックが DELETE_FAILED で止まります。
DeletionPolicy: Retain をテンプレートに追加して更新 → 削除が正攻法ですが、多数のスタックを持っている場合は次の 2 段階削除が早いです。

  1. 空のバケットにダミーのマーカーファイルをアップロードします。

    echo "keep" | aws s3 cp - s3://$BUCKET/_retain_marker.txt
    
  2. aws cloudformation delete-stack --stack-name X を実行するとバケット削除に失敗し、DELETE_FAILED になります。

  3. 再実行時に --retain-resources <BucketLogicalId1> <BucketLogicalId2> を指定します。

    aws cloudformation delete-stack --stack-name X \
      --retain-resources ImageBucket ServerlessDeploymentBucket
    

--retain-resources は DELETE_FAILED 状態のときだけ有効です。
最初の削除で渡しても無視されます。

VPC 内 ENI の切り離し待ち

Lambda を VPC に配置していた頃の ENI は、Lambda 本体を削除してから AWS 側が自動で切り離すまで 20〜45 分 かかります (SLA なし)。
CloudFormation で VPC を削除しようとすると "Exceeded attempts to wait" で NotStabilized になるので、時間を置いて再実行します。
手動で ENI を削除しようとしても、Lambda 管理下の ENI (InterfaceType=lambda, RequesterId=AROA...) は削除権限が AWS 側にあり、関連付けの解除もこちらからはできません。

RDS の削除保護

CloudFormation 経由で作った RDS はデフォルトで DeletionProtection: true になっていることが多く、そのままだと Cannot delete protected DB Instance で削除できません。

aws rds modify-db-instance --db-instance-identifier X \
  --no-deletion-protection --apply-immediately

を先に実行し、削除保護を解除します。

削減後の月額費用と今後の増額

現在は t4g.small の EC2 が 1 台だけ動いています。

t4g.small の無料トライアルは 2026-12-31 まで 有効で、それ以降は月 ¥1,815 が上乗せされます。
リザーブドインスタンスやセービングスプランに切り替えるか、そのままオンデマンドで払い続けるかは 2027 年 1 月までに判断する予定です。

残る削減余地

稼働している 1 台の EC2 も、そのうち Cloudflare Workers など別構成に移行して削除する予定です。
そこまで進めば、RDS スナップショット 40 GB と Secrets Manager 2 個も一緒に片付けて、AWS 側の月額は数十円まで減らせます。
2027 年 1 月に t4g.small の無料枠が終わる前に移行を完了したいと考えています。

教訓