# サーバー代は『設計』で半分にできる：スタートアップのAWS月額を30〜50%削減する、Terraform × FinOps実践ガイド

> 「また今月もAWSの請求額が上がっている」。MRR成長より先にインフラ費が膨らむのは、技術ではなく設計の問題です。スタートアップCEO/COO向けに、オートスケーリング・Fargate Spot・S3階層化・非本番の自動停止・予算アラートを組み合わせ、月額を30〜50%削減しつつ事業成長に耐える基盤を作るTerraform実装を、非エンジニアにも分かる比喩と数値シミュレーションで解説します。

- 公開日: 2026-04-18
- 著者: 友田 陽大
- タグ: AWS, Terraform, インフラ, コスト最適化, FinOps, スタートアップ, IaC, 経営
- URL: https://tomodahinata.com/blog/aws-terraform-startup-cost-optimization-finops
- カテゴリ: インフラ・IaC・CI/CD
- 総合ガイド: https://tomodahinata.com/blog/aws-ecs-vs-eks-startup-decision-framework

## 要点

- AWS月額の30〜50%は「寝ているお金」で、原因は技術ではなく設計にある
- オートスケーリングで閑散時間のリソース費を50〜70%下げ、突発アクセスにも自動で耐える
- Fargate Spotはベースラインを通常料金にしつつ増席分だけスポット化し、止めずに席代を最大70%割引にする
- S3 Intelligent-Tieringで古いデータを安い階層に自動降格し、CloudWatch Logsは保持期間を必ず設定する
- Budgetsの予算アラートと非本番環境の夜間・週末自動停止で、消し忘れによる突発課金を構造的に止める

---

## 序論：「また今月もAWSの請求額が高い」——その感覚、正しいです

スタートアップのCEO・COOの方から、毎年必ずと言っていいほど同じ相談を受けます。

> 「エンジニアに『仕方ない』と言われるけど、売上がそれほど伸びていない月もAWSの費用は同じか、むしろ上がっている。これって普通ですか？」

結論から申し上げます。**普通ではありません**。そして、ほとんどのケースで**月額の 30〜50% は"寝ているお金"**です。本記事は、この"寝ているお金"を起こして事業に回す、極めて現実的な方法論をまとめたものです。技術用語には必ず「レストラン経営」「倉庫運用」「家計簿」の比喩を添えますので、技術の詳細がわからなくても最後まで読めるように書きました。

### スタートアップに共通する "インフラの5つのムダ"

多くのSaaS・Webサービスで、次のような"ムダ"が静かに、毎日、お金を溶かしています。

1. **深夜も満員体制で営業している**：お客さんがいない深夜もフルスタッフでサーバーを稼働させている
2. **ピークに合わせて大箱の店舗を借りている**：金曜夜の混雑に合わせた席数を、平日昼も維持している
3. **開かずのクローゼット**：一度も取り出していない古いデータ・ログが高価な棚を占拠している
4. **ステージング環境が本番と同じ規模**：社内テスト用の"試着室"を本店舗と同じ広さで借りている
5. **解約したはずの回線がまだ課金**：使っていないIPアドレスやロードバランサが静かに毎月課金されている

これらの総和は、月額 **8万円〜40万円** 規模のスタートアップでよく見ます。年額に直すと、**エンジニア採用の月給1.5〜2ヶ月分**、あるいは **広告予算半期分** に相当します。本来、事業を伸ばすために使えたはずのお金です。

### 本稿が提示する解

以下の4本柱を **Terraform（インフラを設計図として記述する仕組み）** で宣言的に実装し、「**設定ミスで課金が増える事故**」と「**人が忘れることで増えるムダ**」の両方を構造的に止めます。

- **柱①**：オートスケーリング ＝ 客数に応じて席数を自動調整
- **柱②**：Fargate Spot ＝ 空席チケットを使って席代を最大70%割引
- **柱③**：S3 Intelligent-Tiering ＝ 使用頻度で倉庫を自動で入れ替え
- **柱④**：Budgets + 非本番の自動停止 ＝ 家計簿の自動化と消し忘れ防止

---

## 本論①：柱① —— オートスケーリング（客数に応じて席数を動かす）

レストランで例えましょう。ランチタイムには40席、深夜の閉店前には4席。これが人間の店長なら当たり前にやる「席数調整」ですが、従来のサーバー運用では**24時間、40席を借り続ける**のが常識でした。深夜の36席分は、**誰も座っていないのに毎日電気代と家賃が発生している**のと同じです。

AWSのオートスケーリングは、この「席数の自動調整」を実現します。**お客さん（アクセス数・CPU負荷）が増えると席数を増やし、減ると減らす**。寝ている深夜は最小席数まで縮退し、イベント告知でアクセスが10倍になったら自動で座席を拡張します。

### ビジネス視点で嬉しい3つの効果

- **ムダな常時コストの削減**：閑散時間のリソース費が 50〜70% 下がる
- **突発アクセスへの耐性**：テレビ露出・SNSバズで落ちない → 機会損失ゼロ
- **人間が夜起きない**：深夜に「スケールアップ対応」でエンジニアを叩き起こさない（**採用した人が辞めにくい基盤**）

### Terraformでの実装（後述のコードセクションに詳細）

宣言文はほぼ「CPU使用率が平均60%を超えたら席を増やし、下回ったら減らす」という1文です。**人間の判断を介さず、24時間この判断が自動的に回り続ける**ことが価値です。

---

## 本論②：柱② —— Fargate Spot（空席チケットで席代を最大70%割引）

飛行機の「キャンセル待ち席」を想像してください。直前に余った席を**定価の30%で売る**仕組みがあり、時間に融通が利く客はお得に乗れます。AWS Fargate Spot はこれと同じ発想で、**AWS側で余っている計算リソースを最大70%割引で貸し出す**サービスです。

「え、途中で追い出されることもあるの？」——はい、稀にあります。だからこそ**重要な設計**があります。

**全座席をスポットにしない**こと。これが鉄則です。安定して必要な席（ベースライン）は通常料金、ピーク対応の増席分だけをスポットにする **ハイブリッド配席** にします。例えば「最低2席は通常、増えた分はスポット」とすれば、追い出しが起きてもサービスは止まらず、コストは大きく下がります。

### 期待できる削減効果

- 常時稼働の50%をスポットに切り替えた場合：**全体インフラ費のうち約25〜35%が削減**
- バッチ処理・夜間の機械学習ジョブは**100%スポット**にできることも多い（止まっても朝までに再実行すればよい）

---

## 本論③：柱③ —— S3 Intelligent-Tiering（使用頻度で倉庫を入れ替え）

「ダンボールに詰めて倉庫に入れたまま、3年開けていないもの」が家のどこかにありませんか。企業のデータも同じです。**作成直後は毎日読むが、3ヶ月後には誰も見ない**。しかし、AWS の標準設定では、**最新も古いも同じ高級倉庫に置きっぱなし**です。

S3 Intelligent-Tiering は、**データの使用頻度を自動観察し、アクセスが減ったら安い倉庫に勝手に移す**仕組みです。人間が判断する必要はなく、**オプションを1つONにするだけ**です。

### 段階別の保管コスト（目安）

| 保管階層 | 月額単価（USD/GB/月） | 特徴 |
|---|---|---|
| 頻繁アクセス層（通常S3と同等） | 約 $0.023 | いつでも即時取り出し |
| 低頻度アクセス層（30日アクセスなし） | 約 $0.0125 | 即時取り出し、保管費は半額 |
| アーカイブ層（90日アクセスなし） | 約 $0.004 | 取り出しに数時間、**保管費1/5以下** |
| ディープアーカイブ層（180日） | 約 $0.00099 | 取り出しに12時間、**保管費1/20以下** |

**古い画像・ログ・バックアップの多くは、自動で安い倉庫に降りていき、必要な時だけ取り出す**。1TBのログであれば、**年間で約 $250 → $50 前後まで下がる**試算になります。

---

## 本論④：柱④ —— Budgets + 非本番の自動停止（家計簿と消し忘れ防止）

どれだけ最適化しても、**設定ミスや人為的な"消し忘れ"**で月末に5万円の突発課金が出ることがあります。これを防ぐのが以下の2つの仕組みです。

### AWS Budgets：予算オーバー前に知らせる家計簿

「今月、**予算の80%に達したらSlackに通知**。90%で CEO にメール。100%で緊急対応」。これを Terraform で宣言しておけば、**人間が見張る必要がなくなります**。

### 非本番環境の自動停止：夜と休日は電気を消す

開発環境・ステージング環境は、**誰もログインしていない深夜・週末もほぼ稼働中**です。これを「平日9-19時のみ稼働、夜と週末は停止」にするだけで、**非本番のインフラ費が約2/3カット**されます。週168時間のうち稼働は50時間程度になる計算です。

これもLambda + EventBridge で Terraform で宣言すれば、**誰かが忘れても確実に電気が消えます**。

---

## 技術的裏付け：これらを宣言するTerraformコード

以下は実際の Terraform コードです。エンジニア向けに堅牢な品質を担保しつつ、**各ブロックが「どのビジネスリスクを防いでいるか」をコメントに明記**しています。決裁者の方は、**コメントだけでも流し読み**してください。

### ECS Fargate + オートスケーリング + Spot ハイブリッド

```hcl
# ============================================================
# ビジネス目的：
#   ①ピーク時のアクセス急増で落ちない（機会損失ゼロ）
#   ②閑散時には席数を絞ってコスト最小化
#   ③最低2席は通常料金で確保しサービス停止リスクを排除
#   ④増席分はSpotで最大70%割引 → 成長してもコスト曲線を寝かせる
# ============================================================

resource "aws_ecs_cluster" "app" {
  name = "app-prod"

  # ビジネス目的：障害時の原因究明を最短化（顧客への事故報告の準備時間を1日→1時間に）
  setting {
    name  = "containerInsights"
    value = "enabled"
  }
}

# Capacity Providerの配分。ここが経済性の核。
resource "aws_ecs_cluster_capacity_providers" "app" {
  cluster_name       = aws_ecs_cluster.app.name
  capacity_providers = ["FARGATE", "FARGATE_SPOT"]

  # 通常 : Spot = 1 : 1 で配置（つまり増席分の半分がSpot）
  # ビジネス目的：席代の平均単価を下げつつ、Spot追い出しでも常時運転可能
  default_capacity_provider_strategy {
    capacity_provider = "FARGATE"
    base              = 2 # 最低2席は必ず通常Fargate（サービス停止リスクを封じる）
    weight            = 1
  }
  default_capacity_provider_strategy {
    capacity_provider = "FARGATE_SPOT"
    base              = 0
    weight            = 1
  }
}

# ECS Service（実際に動く店舗）
resource "aws_ecs_service" "app" {
  name            = "app"
  cluster         = aws_ecs_cluster.app.id
  task_definition = aws_ecs_task_definition.app.arn
  desired_count   = 2

  # ビジネス目的：デプロイ失敗を自動ロールバック
  #   → リリース事故で顧客に迷惑をかけたあと、
  #     深夜にエンジニアが手で戻す運用をゼロにする。
  deployment_circuit_breaker {
    enable   = true
    rollback = true
  }

  # 新バージョンの席を立ち上げてから旧席を畳む → ダウンタイムゼロ
  deployment_configuration {
    minimum_healthy_percent = 100
    maximum_percent         = 200
  }

  network_configuration {
    subnets          = var.private_subnet_ids # Multi-AZで単一障害点を排除
    security_groups  = [aws_security_group.app.id]
    assign_public_ip = false # 外部からの直接攻撃面をゼロに
  }

  load_balancer {
    target_group_arn = aws_lb_target_group.app.arn
    container_name   = "app"
    container_port   = 8080
  }

  # Capacity Providerを明示（クラスタのデフォルトを上書きする場合）
  capacity_provider_strategy {
    capacity_provider = "FARGATE"
    base              = 2
    weight            = 1
  }
  capacity_provider_strategy {
    capacity_provider = "FARGATE_SPOT"
    base              = 0
    weight            = 1
  }

  # ビジネス目的：Terraformによる増席数の上書きを避け、オートスケーリングの決定を尊重する
  lifecycle {
    ignore_changes = [desired_count]
  }
}

# ----------- オートスケーリングポリシー（席数の自動調整） -----------
resource "aws_appautoscaling_target" "app" {
  service_namespace  = "ecs"
  resource_id        = "service/${aws_ecs_cluster.app.name}/${aws_ecs_service.app.name}"
  scalable_dimension = "ecs:service:DesiredCount"
  min_capacity       = 2  # 最小席数：深夜・早朝でも2席
  max_capacity       = 20 # 最大席数：10倍アクセスにも耐える
}

# CPU使用率60%をターゲットにスケール
# ビジネス目的：
#   ・CPUが常に60%を超えないよう席数を増やす（客を待たせない）
#   ・60%を大きく下回ったら席を減らす（家賃を払いすぎない）
resource "aws_appautoscaling_policy" "cpu" {
  name               = "app-cpu-target"
  service_namespace  = aws_appautoscaling_target.app.service_namespace
  resource_id        = aws_appautoscaling_target.app.resource_id
  scalable_dimension = aws_appautoscaling_target.app.scalable_dimension
  policy_type        = "TargetTrackingScaling"

  target_tracking_scaling_policy_configuration {
    predefined_metric_specification {
      predefined_metric_type = "ECSServiceAverageCPUUtilization"
    }
    target_value = 60.0

    # スケールアウト（増席）は素早く、スケールイン（減席）は慎重に
    # ビジネス目的：急なピークに出遅れないが、一時的な波で席を減らして後悔しない
    scale_out_cooldown = 60
    scale_in_cooldown  = 300
  }
}
```

### S3 Intelligent-Tiering（倉庫の自動入れ替え）

```hcl
# ============================================================
# ビジネス目的：
#   古いログ・画像・バックアップを人間の判断なしで安い倉庫へ
#   1TB規模で年間 $200〜$300 程度の削減が現実的に見込める
# ============================================================
resource "aws_s3_bucket" "assets" {
  bucket = "my-startup-assets"
}

# バージョニング：誤削除事故時の復旧手段（データ消失＝信用失墜を防ぐ）
resource "aws_s3_bucket_versioning" "assets" {
  bucket = aws_s3_bucket.assets.id
  versioning_configuration { status = "Enabled" }
}

# Intelligent-Tieringの設定：アクセス頻度で自動降格
resource "aws_s3_bucket_intelligent_tiering_configuration" "assets" {
  bucket = aws_s3_bucket.assets.id
  name   = "assets-intelligent-tiering"
  status = "Enabled"

  # 90日アクセスなしならアーカイブ層（保管費が約1/5）
  tiering {
    access_tier = "ARCHIVE_ACCESS"
    days        = 90
  }

  # 180日アクセスなしならディープアーカイブ層（保管費が約1/20）
  tiering {
    access_tier = "DEEP_ARCHIVE_ACCESS"
    days        = 180
  }
}

# 古いバージョン・削除マーカーの定期整理
# ビジネス目的：バージョニングで溜まる"幽霊データ"の保管費を定期的に清算
resource "aws_s3_bucket_lifecycle_configuration" "assets" {
  bucket = aws_s3_bucket.assets.id

  rule {
    id     = "expire-noncurrent-versions"
    status = "Enabled"
    filter {} # 全オブジェクトに適用

    noncurrent_version_expiration { noncurrent_days = 60 }
    abort_incomplete_multipart_upload { days_after_initiation = 7 }
  }
}
```

### 非本番環境の自動停止（夜と休日は電気を消す）

開発環境のECSサービスを **平日9-19時だけ稼働** させ、それ以外は`desired_count = 0` に落とすEventBridgeスケジュールです。

```hcl
# ============================================================
# ビジネス目的：
#   非本番環境（dev/stg）のインフラ費を約2/3カット
#   人の「消し忘れ」による月5万円規模の事故をゼロに
# ============================================================

# 停止スケジュール：平日19時（UTC 10時）に desired_count = 0
resource "aws_scheduler_schedule" "stop_dev" {
  name       = "stop-dev-service"
  group_name = "default"
  flexible_time_window { mode = "OFF" }

  # cron式：月-金の19:00 JST
  schedule_expression          = "cron(0 10 ? * MON-FRI *)"
  schedule_expression_timezone = "Asia/Tokyo"

  target {
    arn      = "arn:aws:scheduler:::aws-sdk:ecs:updateService"
    role_arn = aws_iam_role.scheduler.arn

    input = jsonencode({
      Cluster      = "app-dev"
      Service      = "app"
      DesiredCount = 0
    })
  }
}

# 起動スケジュール：平日9時に desired_count = 2 に戻す
resource "aws_scheduler_schedule" "start_dev" {
  name       = "start-dev-service"
  group_name = "default"
  flexible_time_window { mode = "OFF" }

  schedule_expression          = "cron(0 9 ? * MON-FRI *)"
  schedule_expression_timezone = "Asia/Tokyo"

  target {
    arn      = "arn:aws:scheduler:::aws-sdk:ecs:updateService"
    role_arn = aws_iam_role.scheduler.arn

    input = jsonencode({
      Cluster      = "app-dev"
      Service      = "app"
      DesiredCount = 2
    })
  }
}
```

### 予算アラート（家計簿の自動化）

```hcl
# ============================================================
# ビジネス目的：
#   月末まで気づかず青くなる"驚きの請求書"を撲滅
#   事故発生時のマッハ対応で、被害を「月末」ではなく「同日」で止める
# ============================================================

resource "aws_budgets_budget" "monthly" {
  name         = "monthly-cost"
  budget_type  = "COST"
  limit_amount = "2000" # USD/月
  limit_unit   = "USD"
  time_unit    = "MONTHLY"

  # 予算の80%でSlack通知（余裕がある段階で気づく）
  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                  = 80
    threshold_type             = "PERCENTAGE"
    notification_type          = "ACTUAL"
    subscriber_sns_topic_arns  = [aws_sns_topic.cost_alert.arn]
  }

  # 90%で CEO にメール（経営判断が必要な段階）
  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                  = 90
    threshold_type             = "PERCENTAGE"
    notification_type          = "ACTUAL"
    subscriber_email_addresses = [var.ceo_email]
  }

  # 予測ベースのアラート：月末に超過見込みなら、中旬でも警告
  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                  = 100
    threshold_type             = "PERCENTAGE"
    notification_type          = "FORECASTED"
    subscriber_email_addresses = [var.ceo_email]
  }
}
```

### コードに込めた"設計思想"（決裁者の方向けサマリ）

上記コードで技術的に重要なのは、**すべての設定がコード（Terraform）で宣言されている**という点です。これはビジネスに直結する効果があります。

- **属人化しない**：特定のエンジニアが退職しても、**なぜこの構成なのかが設計書（コード）として残る**
- **再現可能**：同じ構成を本番・ステージング・別事業で**5分で複製**できる
- **変更レビュー可能**：インフラ変更がプルリクエストになるため、**危険な設定変更を事前に経営側が把握可能**（監査対応にも有効）
- **災害復旧**：万一リージョン全体が落ちても、**コードから別リージョンへ復元**できる

これが **IaC（Infrastructure as Code）** の真価です。飲食店に例えるなら、「店長の頭の中にしかないオペレーションマニュアル」から「全員が同じ基準で動ける公式マニュアル」への移行です。

---

## ビジネスインパクト：数字で見る「寝ているお金」

### シミュレーション：MRR 500万円のSaaSスタートアップの場合

以下は、私が実際に似た構成のB2B SaaSで担当した規模感です。数字は事例に基づく保守的な見積です。

| 項目 | Before（素の構成） | After（本記事の構成） | 月額削減 |
|---|---|---|---|
| ECS（本番） 24h×大きめ2台 | ¥72,000 | ¥45,000（Autoscale + Spot） | -¥27,000 |
| ECS（dev/stg） 24h稼働 | ¥40,000 | ¥13,000（夜/週末停止） | -¥27,000 |
| RDS（予約なし） | ¥55,000 | ¥38,000（1年RI + サイズ見直し） | -¥17,000 |
| S3（全件Standard） | ¥18,000 | ¥7,000（Intelligent-Tiering） | -¥11,000 |
| CloudWatch Logs（保持期間なし） | ¥12,000 | ¥4,000（30日自動削除） | -¥8,000 |
| 未使用LB・NATゲートウェイ・EIP | ¥15,000 | ¥3,000（棚卸し + 削除） | -¥12,000 |
| **合計** | **¥212,000** | **¥110,000** | **-¥102,000 / 月** |

**年換算：約 ¥122万円の削減**。これは以下のいずれかに回せる金額です。

- **エンジニア業務委託の1〜1.5ヶ月分**
- **Web広告の半期予算の上積み**
- **カンファレンス出展費 + ブース設営費**
- **オンボーディング動画の外注制作費**

### より重要なのは「10倍にスケールしたとき」の曲線

しかし、本当の価値は"今月の請求書"ではありません。**MRRが 10倍になったとき、インフラ費が 10倍になるか、3倍で済むか**——これが事業の粗利構造を決めます。

| 成長シナリオ | 素の構成（線形増加） | 本記事の構成（亜線形増加） | 差分 |
|---|---|---|---|
| MRR 500万円 | ¥212,000 | ¥110,000 | -¥102,000/月 |
| MRR 1,500万円（3倍） | ¥636,000 | ¥260,000 | -¥376,000/月 |
| MRR 5,000万円（10倍） | ¥2,120,000 | ¥700,000 | -¥1,420,000/月 |

10倍成長したときの月額差は**約142万円**。**年1,700万円**の差額です。これは、**シニアエンジニア1人を年間で追加雇用できる規模**であり、**資金調達の評価額（バリュエーション）にも直結する粗利率改善**です。

**設計の差が、将来のバリュエーションを決めます**。これは精神論ではなく、SaaSの Rule of 40（成長率 + 利益率 ≧ 40% が健全な成長の目安）を改善する直接的な一手です。

### 信頼性の定性効果：落ちない基盤が生む"見えない売上"

コスト削減と同時に、**可用性も上がっている**点を強調させてください。

- **深夜障害対応の激減**：オートスケーリングと自動ロールバックで、**エンジニアが夜起きる頻度が 1/5 以下** に。採用した人材が定着しやすい。
- **突発アクセスへの耐性**：メディア露出時に落ちない基盤は、**一生に数回の事業ジャンプ機会**を逃さないための保険。
- **監査・セキュリティ対応**：インフラ変更がGitに記録されるため、SOC2・ISMS・Pマーク対応で求められる**変更証跡**が自動で残る。

---

## 結論：「作って終わり」ではなく、「下がり続ける仕組み」を残す

私が仕事で最も大切にしているのは、**「来月もそのあとも、自分がいなくても回る基盤を残すこと」**です。

単発の最適化で今月の請求書を5万円下げるのは、比較的簡単です。しかし、それだけでは半年後、元の木阿弥に戻ることが多いのも事実です。**チームにTerraformという共通言語を残し、コスト削減の仕組みそのものを文化として定着させる**——これが、私が提供したい本当の価値です。

### 私が開発パートナーとしてお約束すること

- **決裁者の言葉で話します**：技術用語は必ず「ビジネス上のリスクと機会」に翻訳してご説明します
- **数字で責任を持ちます**：施策の前後で何円下がったか、何%改善したかを月次レポートで可視化します
- **長期目線を優先します**：短期で5万円下がっても、3ヶ月後に15万円上がる設計は採りません
- **引き継ぎ可能な形で残します**：属人化せず、後任のエンジニアが同じ水準で運用できるドキュメントとコードを残します
- **ユーザー体験から逆算します**：コスト削減が画面速度や可用性を犠牲にしないよう、**UXから見た安全圏**でのみ最適化を進めます

### 次の一歩：まずは「棚卸し」から

本格着手の前に、まずは**現状のAWS利用の棚卸し**（AWS Cost Explorer・Trusted Advisor・Compute Optimizer の分析）だけでも、**1〜2週間で"見つかる削減余地"が見えます**。ここから始めれば、リスクゼロで初月から成果が出る進め方が可能です。

「ウチの構成は最適なんだろうか？」「来年のスケールに今のまま耐えるだろうか？」——そう感じていらっしゃるなら、初回の棚卸しから、お気軽にご相談ください。**"寝ているお金"を起こし、事業に回す最初の数字**を、一緒に作りましょう。
