Rails アプリを Fly.io にデプロイして Pull Request ごとのプレビュー環境を手に入れる

重岡 正 ·  Fri, February 6, 2026

Pull request を作成したら、レビュアーがすぐに動作確認できるプレビュー環境が欲しい。Fly.io は東京リージョン(nrt)を持ち、KVM ベースのハードウェア仮想化コンテナでアプリを動かすプラットフォームです。

Fly.io には Pull Request (PR) プレビュー環境の組み込み機能はありませんが、GitHub Actions と superfly/fly-pr-review-apps を組み合わせることで実現できます。

今回、前回の Railway 編と同じ Rails 8.1 + PostgreSQL のアプリを Fly.io にデプロイし、PR プレビュー環境が動くところまでを検証しました。

Fly.io とは

Fly.io は、アプリケーションをグローバルに分散デプロイできるプラットフォームです。

特徴説明
MachinesKVM ベースのハードウェア仮想化コンテナ。必要なときだけ起動
グローバルデプロイ18 リージョンに展開。東京(nrt)対応
自動停止/起動リクエストがなければ Machine を停止し、コストを削減
Fly Postgresマネージド PostgreSQL クラスタをリージョン指定で作成
CLI 中心flyctl CLI でアプリの作成・デプロイ・管理を完結
セキュリティRust/Go スタック。SOC2 Type 2 対応

技術スタック

検証に使用した技術スタックです。Railway 編と同じアプリを使用しています。

技術バージョン
Ruby3.4.8
Rails8.1.2
PostgreSQL17

アプリは Post の CRUD を scaffold した最小構成です。ルーティングやヘルスチェック(GET /up)、Dockerfile の詳細は Railway 編を参照してください。

Fly.io へのデプロイ

1. fly.toml を作成する

リポジトリのルートに fly.toml を追加します。

app = "rails-preview-demo"
primary_region = "nrt"
 
[build]
 
[deploy]
  release_command = "bin/rails db:prepare"
 
[http_service]
  internal_port = 3000
  force_https = true
  auto_stop_machines = "stop"
  auto_start_machines = true
  min_machines_running = 0
 
[checks.status]
  port = 3000
  type = "http"
  interval = "10s"
  timeout = "2s"
  grace_period = "30s"
  method = "GET"
  path = "/up"
 
[[vm]]
  memory = "512mb"
  cpu_kind = "shared"
  cpus = 1
設定説明
primary_regionnrt東京リージョンにデプロイ
release_commandbin/rails db:prepareデプロイ前にマイグレーションを実行
internal_port3000Rails(Puma)のデフォルトポート
auto_stop_machinesstopアイドル時に Machine を停止
auto_start_machinestrueリクエスト時に Machine を自動起動
min_machines_running0最小稼働数 0(完全停止を許可)
path(checks)/upRails のヘルスチェックエンドポイント

Railway との大きな違いは release_command です。Fly.io はデプロイ時に release_command を専用の一時的な Machine で実行してから、アプリ本体の Machine を起動します。db:prepare はここで実行されるため、docker-entrypoint 経由で実行する必要はありません。

2. CLI でアプリを作成する

Fly.io CLI(flyctl)を使ってアプリと PostgreSQL クラスタを作成します。

# CLI のインストール
curl -L https://fly.io/install.sh | sh
 
# ログイン
fly auth login
 
# アプリの作成
fly apps create rails-preview-demo
 
# PostgreSQL クラスタの作成(東京リージョン)
fly postgres create --name rails-preview-demo-db --region nrt
 
# PostgreSQL をアプリにアタッチ(DATABASE_URL が自動設定される)
fly postgres attach rails-preview-demo-db --app rails-preview-demo
 
# RAILS_MASTER_KEY を設定
fly secrets set RAILS_MASTER_KEY=<config/master.key> --app rails-preview-demo
 
# デプロイ
fly deploy

fly postgres attach を実行すると、DATABASE_URL が自動的にアプリの環境変数に設定されます。Railway のように手動で変数参照構文を書く必要はありません。

3. デプロイを確認する

デプロイが完了すると <app-name>.fly.dev のドメインでアプリにアクセスできます。

GitHub Actions による本番デプロイ

Fly.io は Dashboard から GitHub リポジトリを接続して自動デプロイする方法もありますが、PR プレビュー環境と合わせて管理するために GitHub Actions を使います。

.github/workflows/fly-deploy.yml を作成します。

name: Fly Deploy
 
on:
  push:
    branches: [main]
 
env:
  FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
 
jobs:
  deploy:
    runs-on: ubuntu-latest
    concurrency: deploy-group
    steps:
      - uses: actions/checkout@v4
      - uses: superfly/flyctl-actions/setup-flyctl@master
      - run: flyctl deploy --remote-only

main ブランチに push されると、flyctl deploy --remote-only が実行され、Fly.io のリモートビルダーで Docker イメージをビルドしてデプロイします。

必要な GitHub Secrets

Secret説明
FLY_API_TOKENFly.io の org レベルトークン
RAILS_MASTER_KEYconfig/master.key の値

FLY_API_TOKEN は以下のコマンドで取得します。

fly tokens create org

org レベルのトークンが必要な理由は後述します。

PR プレビュー環境

ここからが本題です。Fly.io には Railway のような組み込みの PR プレビュー機能はありません。代わりに、superfly/fly-pr-review-apps という公式の GitHub Action を使います。

ワークフロー

.github/workflows/fly-preview.yml を作成します。

name: Fly Preview
 
on:
  pull_request:
    types: [opened, reopened, synchronize, closed]
 
env:
  FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
 
jobs:
  preview:
    runs-on: ubuntu-latest
    concurrency:
      group: pr-${{ github.event.number }}
    steps:
      - uses: actions/checkout@v4
      - uses: superfly/fly-pr-review-apps@1.5.0
        with:
          region: nrt
          org: personal
          postgres: rails-preview-demo-db
          secrets: RAILS_MASTER_KEY=${{ secrets.RAILS_MASTER_KEY }}

動作の仕組み

PR のイベントに応じて、ワークフローは以下を自動で実行します。

イベント動作
opened / reopenedPR 用の Fly アプリを新規作成し、デプロイ
synchronizePR ブランチに push されるたびに再デプロイ
closedPR 用の Fly アプリを削除

superfly/fly-pr-review-apps は内部で以下の処理を行います。

ステップ内容
1. アプリの作成pr-{番号}-{リポジトリ名} の命名規則で Fly アプリを作成
2. DB のアタッチ既存の PostgreSQL クラスタに新しいデータベースを作成しアタッチ
3. シークレットの設定RAILS_MASTER_KEY などを Fly アプリに設定
4. ビルド・デプロイFly.io のリモートビルダーで Docker イメージをビルドし起動
5. DB マイグレーションrelease_commanddb:prepare)を実行

PR がマージまたはクローズされると、プレビュー用の Fly アプリは自動的に削除されます。

プレビュー URL の通知

superfly/fly-pr-review-apps 自体は PR にコメントを投稿しません。ワークフローにステップを追加して、プレビュー URL を PR にコメントします。

      - name: Set preview URL
        if: github.event.action != 'closed'
        id: preview-url
        run: |
          APP_NAME="pr-${{ github.event.number }}-$(echo '${{ github.repository }}' | tr '/' '-')"
          echo "url=https://${APP_NAME}.fly.dev" >> "$GITHUB_OUTPUT"
      - name: Comment preview URL on PR
        if: github.event.action != 'closed'
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          MARKER="<!-- fly-preview-url -->"
          BODY="${MARKER}
          **Fly.io Preview:** ${{ steps.preview-url.outputs.url }}"
          EXISTING=$(gh api "repos/${{ github.repository }}/issues/${{ github.event.number }}/comments" \
            --jq "[.[] | select(.body | startswith(\"${MARKER}\"))] | first | .id" 2>/dev/null)
          if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then
            gh api "repos/${{ github.repository }}/issues/comments/${EXISTING}" \
              -X PATCH -f body="${BODY}"
          else
            gh pr comment "${{ github.event.number }}" --repo "${{ github.repository }}" --body "${BODY}"
          fi

HTML コメント <!-- fly-preview-url --> をマーカーとして使い、同じ PR への複数回 push で既存コメントを上書きする仕組みです。

GitHub Deployments との連携

さらに、GitHub Deployments API を使ってプレビュー環境のステータスを GitHub 上で管理することもできます。

      - name: Create GitHub deployment
        if: github.event.action != 'closed'
        uses: actions/github-script@v7
        with:
          script: |
            const deployment = await github.rest.repos.createDeployment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              ref: context.payload.pull_request.head.sha,
              environment: `preview-pr-${context.payload.number}`,
              auto_merge: false,
              required_contexts: [],
              transient_environment: true,
            });
            await github.rest.repos.createDeploymentStatus({
              owner: context.repo.owner,
              repo: context.repo.repo,
              deployment_id: deployment.data.id,
              state: 'success',
              environment_url: '${{ steps.preview-url.outputs.url }}',
              log_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
            });

transient_environment: true を指定すると、PR がクローズされたときに GitHub が自動でデプロイメントを非アクティブにします。

ハマりどころ — deploy トークンでは権限が足りない

検証中に遭遇した問題を共有します。

問題

fly tokens create deploy で発行したデプロイトークンを FLY_API_TOKEN に設定したところ、PR プレビュー環境のデプロイで以下のエラーが発生しました。

Not authorized to deploy this app

原因

デプロイトークンは特定のアプリに対する操作のみを許可します。PR プレビューのワークフローは pr-{番号}-{リポジトリ名} という新しい Fly アプリを動的に作成するため、既存アプリへのデプロイ権限だけでは不足します。

解決策

org レベルのトークンを使用します。

fly tokens create org

org レベルのトークンは組織内のすべてのアプリに対する操作を許可するため、新しいアプリの作成も可能になります。

Railway との比較

同じアプリを Railway と Fly.io の両方にデプロイした結果を比較します。

観点RailwayFly.io
PR プレビュー組み込み機能GitHub Actions が必要
設定ファイルrailway.jsonfly.toml + 2 つのワークフロー
DB 管理PR ごとに独立した PostgreSQL インスタンスを自動作成既存クラスタに DB を追加
リージョン自動選択明示的に指定(nrt など)
マイグレーションdocker-entrypoint 経由release_command で実行
Bot PR 対応Dashboard で有効化が必要GitHub Actions なので区別なし
カスタマイズ性低い(組み込みのため)高い(ワークフローを自由に書ける)

Railway は設定の手軽さ、Fly.io はワークフローのカスタマイズ性に優れています。PR にプレビュー URL をコメントする方法や、GitHub Deployments との連携など、Fly.io では細かい制御が可能です。

まとめ

Fly.io と GitHub Actions を組み合わせれば、Rails アプリの PR プレビュー環境を構築できます。

  • fly.toml + 2 つの GitHub Actions ワークフローで構成
  • superfly/fly-pr-review-apps が PR ごとのアプリ作成・削除を自動化
  • 東京リージョン(nrt)にデプロイ可能
  • release_commanddb:prepare を実行するため docker-entrypoint の競合がない
  • org レベルのトークンを使うこと(deploy トークンでは新規アプリを作成できない)
  • プレビュー URL の通知や GitHub Deployments 連携はワークフローで自由にカスタマイズ

Railway の組み込み機能と比べると設定ファイルは増えますが、その分ワークフローを自在に制御できます。

以上、Rails を Fly.io にデプロイして PR ごとのプレビュー環境を構築してみた、現場からお送りしました。

参考情報