🚢

Kamalデプロイ

Rails公式デプロイツール — Dockerベースのゼロダウンタイムデプロイ

Kamal 2はRails 8の公式デプロイツールで、Dockerコンテナベースのゼロダウンタイムデプロイを提供します。HerokuのようなPaaSなしでも、どんなVPS(DigitalOcean、Hetzner、AWS EC2等)にもデプロイできます。

Kamalとは?

Kamal(旧MRSK)は37signalsがBasecampとHEYをデプロイするために作ったツールです。Rails 8からrails newでプロジェクトを作成するとKamal設定がデフォルトで含まれます。

主要な特徴:

  • Dockerコンテナベースのデプロイ

  • ゼロダウンタイム: 新コンテナがヘルスチェックを通過した後にトラフィック切替

  • kamal-proxy: Traefikを置き換える軽量リバースプロキシ(Kamal 2から)

  • SSL自動発行(Let's Encrypt)

  • ロールバック: kamal rollback一行で前バージョン復元

  • マルチサーバーデプロイ対応


deploy.yml設定

# config/deploy.yml
service: my-app
image: my-dockerhub-user/my-app

servers:
  web:
    hosts:

      - 123.456.789.10
    options:
      network: "private"

proxy:
  ssl: true
  host: my-app.com

registry:
  username: my-dockerhub-user
  password:

    - KAMAL_REGISTRY_PASSWORD

builder:
  local:
    arch: amd64
    host: unix:///var/run/docker.sock


デプロイフロー

kamal deploy 実行
  ↓
1. ローカルでDockerイメージビルド
  ↓
2. Docker Hub(またはGHCR)にイメージPush
  ↓
3. サーバーでイメージPull
  ↓
4. 新コンテナ起動 + ヘルスチェック
  ↓
5. kamal-proxyがトラフィックを新コンテナに切替
  ↓
6. 旧コンテナ削除


⚠️ DigitalOcean + Kamal: ローカルビルド推奨

DigitalOceanの小規模Droplet(1〜2GB RAM)でサーバー上で直接Dockerビルドすると、CPU/メモリ負荷が極めて高くなります。RailsアプリのDockerビルドはgemコンパイル、アセットプリコンパイル等で多くのリソースを消費するためです。

推奨パターン: ローカルビルド → レジストリPush → サーバーPull

builder:
  local:
    arch: amd64
    host: unix:///var/run/docker.sock

builder.local設定でローカルマシンでイメージをビルドし、Docker HubやGitHub Container Registryにpush後、サーバーでは完成したイメージをpullするだけです。

なぜサーバービルドが問題か?

  • bundle install: 数十のgemコンパイル → CPU 100% + メモリ1GB以上

  • rails assets:precompile: Tailwind CSS + Viteビルド → 追加メモリ消費

  • 1GB Droplet: OOM Killerがプロセスを終了、稼働中のアプリにも影響

  • 2GB Droplet: ビルド中のレスポンスタイムが10倍以上遅延

💡 実戦経験: Solid Queueのメモリ問題

Solid Queueはデフォルトでワーカーが3つ起動します。各ワーカーは独立プロセスで約178MBずつ占有し、Rails本体よりメモリを多く消費します。

プロセス                         メモリ
─────────────────────────────────────────
ruby(Rails/Puma)               357MB
bundle(SolidQueue worker x3)   178MB x 3 = 534MB
supervisord                      18MB
─────────────────────────────────────────
合計                             約910MB

1GBサーバーでOOMが発生するのは当然です。2GB Dropletでも、OS + Dockerオーバーヘッドを考慮すると通常運用自体がメモリ限界に近い状態です。特にデプロイ時に新コンテナ起動 + 旧コンテナdrain過程でメモリ不足が顕著になり、OOMが多発しました。

production設定でprocesses: 1, threads: 1にしてもbundleプロセスが3つ起動する点に注意が必要です。ワーカー数を減らせばメモリ節約は可能ですが、根本的に小規模VPSでは厳しいです。

メモリ節約方法:
1. YJIT無効化RUBY_YJIT_ENABLE=0)— プロセスあたり30〜50MB節約(性能は若干低下)
2. MALLOC_ARENA_MAX=2 — Rubyのメモリ断片化を削減(プロセスあたり50〜100MB節約、効果大)
3. Sidekiqに切替 — スレッドベースで1プロセスで動作、メモリ大幅節約。ただしRedis必要
4. Solid Queueを別マシンに分離 — SQLite使用時は不可能(ファイル共有問題)

筆者は最もシンプルな環境変数追加を選択しました:

MALLOC_ARENA_MAX = "2"    # Rubyメモリ断片化防止
RUBY_YJIT_ENABLE = "0"   # YJIT無効化でメモリ節約

これだけでプロセスあたり80〜150MB程度の節約が可能ですが、根本的な解決策ではありませんでした。

筆者は結局2GB Dropletでの不安定さを解消できずFly.ioに移行しました。より大きなDroplet(4GB+)にすれば解決する可能性はありますが、コスト対比でFly.ioがより良い選択でした。

ローカルビルドの利点:

  • 開発マシンの豊富なCPU/RAMを活用(M1/M2 Macの高速ビルド)

  • サーバーはpull + コンテナ起動のみ → ほぼ負荷なし

  • 稼働中のアプリに影響なくデプロイ可能


主要コマンド

kamal setup          # 初回サーバー設定(Docker、kamal-proxyインストール)
kamal deploy         # ビルド → Push → デプロイ
kamal redeploy       # 既存イメージで再デプロイ(設定変更時)
kamal rollback       # 前バージョンにロールバック
kamal app logs       # アプリログ確認
kamal app exec 'bin/rails console'  # リモートRailsコンソール
kamal app exec 'bin/rails db:migrate'  # マイグレーション実行


⚠️ Vite autoBuild: プロダクションでは必ずfalse

Viteを使うRailsアプリをデプロイする際、config/vite.jsonプロダクション環境でautoBuild: trueに設定してはいけませんautoBuild: trueだとユーザーアクセスのたびにサーバーでvite buildが実行され、膨大なCPU/メモリ負荷が発生します。

筆者はこの設定を見落として、アクセスのたびにサーバーリソースが枯渇する問題を経験しました。

// config/vite.json
{
  "development": { "autoBuild": true },  // ✅ 開発環境のみtrue
  "test": { "autoBuild": false },
  "production": { "autoBuild": false }    // ⚠️ 必ずfalse!
}

プロダクションではデプロイ時にvite buildを事前実行(DockerfileのRUN bin/rails assets:precompile)し、ランタイムではビルド済みアセットのみ配信すべきです。


Kamal vs 他のデプロイ方式

項目 Kamal + VPS Heroku Fly.io Capistrano
月額費用 $5〜12(VPS) $25+(Eco) $5+ VPS費用のみ
Docker必須 ❌(Buildpack) ❌(サーバー直接)
ゼロダウンタイム ✅(kamal-proxy) ✅(有料) △(設定必要)
SSL自動 ✅(Let's Encrypt) 手動
SQLite対応 ✅(Volume) ✅(Litestream)
サーバー制御 完全制御 制限的 制限的 完全制御
学習コスト

SQLite + Kamal

KamalでSQLiteベースのアプリをデプロイするには、Docker Volumeを使用してデータベースファイルをコンテナ外部に保存する必要があります。

servers:
  web:
    volumes:

      - data:/rails/storage  # SQLite DBファイル永続化

注意: SQLiteは単一サーバーでのみ使用可能です。マルチサーバーデプロイ時はPostgreSQL等のクライアント-サーバーDBを使用してください。

構造ダイアグラム

Kamalデプロイフロー
💻
1. ローカルでDockerイメージビルド
docker build → my-app:latest
📦
2. Docker Hub / GHCRにPush
docker push registry/my-app
🖥️
3. サーバーでイメージPull + 新コンテナ起動
docker pull → docker run
4. ヘルスチェック通過 → kamal-proxyがトラフィック切替
⚡ ゼロダウンタイム — 旧コンテナ削除
❌ サーバーでビルド(小規模VPS)
bundle install → CPU 100%, RAM 1GB+
assets:precompile → 追加メモリ消費
🔥 OOM Killer発動 / サービス中断
1〜2GB Dropletでビルド = 災害
✅ ローカルでビルド(推奨)
開発マシン → 豊富なCPU/RAM
サーバーはPullのみ → 負荷ほぼなし
⚡ サービス無停止デプロイ
builder.local.arch: amd64
核心設定(config/deploy.yml):
# config/deploy.yml
service: my-app
image: user/my-app
servers:
  web:
    hosts: [123.456.789.10]
builder:
  local:
    arch: amd64
ポイント: <strong>Kamal + VPS</strong>で月$5〜12のプロダクション運用 — 小規模サーバーは必ず<strong>ローカルビルド</strong>

キーポイント

1

gem install kamal — Kamal CLIインストール

2

kamal init — config/deploy.yml生成

3

deploy.ymlにサーバーIP、Dockerレジストリ、環境変数を設定

4

builder.local設定 — ローカルビルド + レジストリPush(小規模サーバー必須)

5

kamal setup — 初回サーバー設定(Docker、kamal-proxyインストール)

6

kamal deploy — ビルド → Push → ゼロダウンタイムデプロイ

メリット

  • Rails 8公式 — rails newですぐ使用可能
  • 月$5〜12でプロダクション運用可能(PaaS比で安価)
  • ゼロダウンタイムデプロイが標準搭載
  • SSL自動発行(Let's Encrypt)
  • サーバー完全制御 — ベンダーロックインなし
  • kamal rollbackで即座にロールバック
  • SQLite運用可能 — Render/Herokuは永続ボリューム未対応でSQLite不可
  • DigitalOcean等のVPSはお名前.com VPSより全般的に安価で、サードパーティ連携も充実しUIもシンプル

デメリット

  • サーバー管理を自分で行う必要(セキュリティパッチ、モニタリング)
  • Dockerの基本知識が必要
  • 初期設定がPaaSより複雑
  • 小規模サーバーでサーバービルド時の負荷問題(ローカルビルドで解決)
  • SSHアクセス可能なサーバーが必要(サーバーレス不可)
  • VPS運用負担 — Docker/アプリログが蓄積しディスク100%問題、定期的なログ整理・容量・リソース監視等PaaSでは不要な作業が増える
  • Dockerイメージレジストリ管理が必要 — Render/Fly.ioはコードpushで自動ビルドされるが、KamalはDocker Hub等にイメージを自分で保存する必要あり。Privateリポ使用時は追加費用発生の可能性、古いイメージの蓄積で容量管理も必要
  • SQLite運用時バックアップを自前で実装する必要あり — Kamal自体の問題というよりSQLite + VPSの組み合わせの問題。マネージドDB(RDS等)は自動バックアップ提供だが、SQLiteファイルはcron + Litestream等で自分でバックアップパイプラインを構築する必要あり
  • デプロイ時のトラブルが頻発 — インフラ知識のある筆者でもデプロイのたびに大小の問題が発生。インフラ経験がない場合、問題解決がかなり困難になりうる。Render/Fly.ioはgit pushだけでデプロイ完了だが、KamalはDocker・SSH・ネットワーク・プロキシ等複数レイヤーで問題が起こりうるためデバッグ範囲が広い

ユースケース

DigitalOcean DropletにRailsアプリデプロイ($5〜12/月) Hetzner、Vultr等の低コストVPSにデプロイ SQLite + VolumeでDB運用コストゼロ Heroku/Render比で月額70%以上削減 サイドプロジェクト/MVPの高速デプロイ