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

2026-09-14

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

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

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

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

```bash
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. 空のバケットにダミーのマーカーファイルをアップロードします。

   ```bash
   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>` を指定します。

   ```bash
   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` で削除できません。

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

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

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

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

- **月 ¥2,176 の内訳** (2026-09 見込み)
  - データ転送 (送信): ¥750
  - RDS スナップショット 2 個 (40 GB、手元に pg_dump もあるが念のため保持中): ¥570
  - EBS 30 GB: ¥432
  - 税: ¥198
  - Secrets Manager × 2: ¥120
  - S3 リクエスト + CloudWatch: ¥100
  - S3 ストレージ 1.69 GB: ¥6
  - **EC2 t4g.small: ¥0** (無料トライアル適用中)

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

## 残る削減余地

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

## 教訓

- クレジット残高は Console でしか確認できません。実費に戻る時期を予測しづらいため、`UnblendedCost - NetUnblendedCost` を月次で監視します。
- 使わなくなったリソースはスタック単位で早めに削除します。特に NAT Gateway と RDS スナップショットは固定費として蓄積します。
- パブリック IPv4 アドレスは使用中でも課金対象になったため、EIP の解放だけでは節約になりません。
- CloudFormation の二段階削除パターンを覚えておきます。`--retain-resources` は DELETE_FAILED 状態のときだけ有効です。
