---
title: "Inngestとは？使い方とNext.js・Vercel・Supabase連携をSDK v4で解説"
url: "https://www.issoh.co.jp/tech/details/2595/"
published: 2024-06-17
updated: 2026-10-24
categories: ["Next.js"]
publisher: "株式会社一創"
---

# Inngestとは？使い方とNext.js・Vercel・Supabase連携をSDK v4で解説

**Inngest**（読み方は「インジェスト」）は、処理を`step.run()`で区切っておくと、失敗したステップだけを再試行し、成功済みのステップを飛ばして途中から再開できるワークフロー実行基盤です。キューやワーカーを自分で用意せずに、Next.jsのAPIルートからメール送信や外部API呼び出しをバックグラウンドへ逃がせます。

この記事は2026年10月24日時点の**TypeScript SDK v4（npmの最新は2026年10月22日公開の4.26.0）**とCLI 1.48.0を前提に、インストール、Next.jsでの書き方、CLIでのrunの調べ方、Vercelへのデプロイ、Supabaseとの組み合わせ、料金までを扱います。v3以前のコードはv4ではそのまま型検査を通りません。古い解説記事のコードを写す前に、後半の移行の章を確認してください。

## まとめ：Inngestの要点（SDK v4時点）

- **仕組み**：関数を`step.run()`で区切ると各ステップの結果が保存され、失敗時はそのステップだけが既定で最大4回再試行されます（初回と合わせて最大5回）。
- **導入**：`npm install inngest`でSDKを入れ、ローカルは`npx inngest-cli@latest dev`でDev Server（`http://localhost:8288`）を起動します。v4ではアプリ側に`INNGEST_DEV=1`が必要です。
- **Next.js**：`app/api/inngest/route.ts`で`serve()`からGET・POST・PUTをexportし、`inngest.send()`でイベントを送ると関数が動きます。
- **CLI**：`inngest api`でrunの取得・トレース・手動起動・再実行ができます。ローカルは認証不要、Cloudは`inngest login`のあと`--prod`を付けます。
- **Vercel**：公式連携で署名キー・イベントキーの設定とデプロイごとの同期が自動になります。Deployment Protectionは本番にも効く設定があるので要確認です。
- **料金**：Freeは月5万executionsまで無料、Proは月99ドルから、Businessは月499ドルからです。1つのrunの長さはFreeで30日までです。

## Inngestの仕組み：stepで区切るDurable Execution

Inngestの関数は、sleepや待機、再試行を挟みながら複数のHTTPリクエストにまたがって実行されます。Inngest側のサーバーがアプリの`/api/inngest`を呼び出し、SDKがステップを実行して結果を返すと、Inngestはその結果を状態ストアへ保存します。次の呼び出しでは関数を先頭から再実行しますが、完了済みのステップは保存済みの戻り値に置き換えられて中身は実行されません。v4で既定有効のチェックポイントは、続けて実行できる複数のステップを1回のリクエストにまとめて往復を減らす最適化で、この仕組み自体は変わりません。

ここから実装上のルールが1つ導かれます。**DB更新や外部API呼び出しなど、結果が毎回変わる処理は必ず`step.run()`の中に書く**ことです。ステップの外に書いた処理は再実行のたびに走るため、メールが二重に送られるといった事故につながります。ステップ内でも、送信先で処理が成功した後に応答が失われると再試行されます。公式の[リトライのガイド](https://www.inngest.com/docs-markdown/durable-execution/guides-and-advanced/error-handling/retries)も、同じ操作の試行には同じ冪等キーを使うよう求めています。

各ステップのリトライは独立しています。後述のDev Serverでの確認でも、2つ目のステップを2回失敗させる間、1つ目のステップは1回しか実行されませんでした。

### ジョブキューやTemporalとの違い

BullMQでは、RedisまたはPostgreSQLのバックエンドとワーカープロセスを用意し、接続先や実行環境を管理します。Inngestは既存のWebアプリのエンドポイントを外から呼ぶ方式なので、Vercelのようなサーバーレス環境でも常駐ワーカーを置かずに済みます。ワーカーを常駐させられる環境で単純な非同期処理だけを回すなら、[BullMQとは？v6のPostgreSQL対応・リトライとワーカー運用を実装者目線で解説](/tech/details/17289/)で扱っているキュー方式のほうが部品は少なく済みます。

同じDurable Executionの系統には、ワーカーを自前で運用する[Temporal Workflowとは？仕組み・Pythonの実測結果・Cloud料金を解説](/tech/details/9090/)や、AWSの中で完結する[Lambda Durable Functionsとは？東京リージョン対応と15分制限を超える長時間ワークフロー実装](/tech/details/10107/)があります。Inngestが向いているのは、TypeScriptのWebアプリにワークフローを後付けしたい場合です。

## インストールとDev Serverの起動（npm・CLI）

SDKは`inngest`という1つのnpmパッケージです。4.26.0の`engines`は`node >=20`で、TypeScriptは5.8以上をpeer依存に指定しています。

```
npm install inngest
```

ローカル開発では、Inngestのサーバー機能を1プロセスに詰めたDev Serverを起動します。CLIは都度`npx`で呼ぶのが手軽ですが、[公式のCLIページ](https://www.inngest.com/docs-markdown/cli)はインストーラ・Homebrew・`npm install -g inngest-cli`の3通りの常設インストールも案内しています。

```
# ターミナル1：Dev Server（UIは http://localhost:8288）
npx inngest-cli@latest dev

# ターミナル2：Next.js（v4ではINNGEST_DEV=1が必須）
INNGEST_DEV=1 npm run dev
```

`curl -sfL https://cli.inngest.com/install.sh | sh`のインストーラは、スクリプトを読むと実行したディレクトリに`inngest`のバイナリを置くだけで、PATHへは入れません。続けて`inngest dev`と打っても見つからないので、表示される案内どおり`/usr/local/bin`などへ移すか、`./inngest dev`で起動します。`INNGEST_DEV=1`は`.env.local`に書いておけば毎回付ける必要はありません。Dev Serverは関数の一覧、手動実行（Invoke）、各ステップの入出力とタイムラインをブラウザのUIで確認できます。

## Next.js App Routerでの使い方：client・serve・関数の3ファイル

最小構成は、クライアント、serveハンドラ、関数定義の3ファイルです。この例は既存のNext.jsプロジェクトを前提とし、追加で`npm install @supabase/supabase-js`を実行します。サーバー用の環境変数`SUPABASE_URL`と`SUPABASE_SECRET_KEY`を設定し、ユーザーIDを格納する`id`・`email`・`activated_at`列を持つ`profiles`テーブルを用意してください。以下のコードはinngest 4.26.0とTypeScript 7.0.2の組み合わせで型検査を通しています。

### クライアントと/api/inngestのserveハンドラ

```
// src/inngest/client.ts
import { Inngest } from "inngest";

export const inngest = new Inngest({
  id: "my-app",
  checkpointing: { maxRuntime: "240s" },
});
```

```
// src/app/api/inngest/route.ts
import { serve } from "inngest/next";
import { inngest } from "../../../inngest/client";
import { onboarding } from "../../../inngest/functions";

export const maxDuration = 300;

export const { GET, POST, PUT } = serve({
  client: inngest,
  functions: [onboarding],
});
```

POSTは関数の実行、PUTはInngestへの関数登録（同期）に使われ、GETは開発時に限って関数のメタデータを返します。Pages Routerの場合は`pages/api/inngest.ts`で`export default serve(...)`とします。`maxDuration`と`maxRuntime`の関係はVercelの章で説明します。

### 関数定義：triggersとstep.run・step.sleep・step.waitForEvent

サインアップのイベントを受けてプロフィールを読み、歓迎メールを送り、有効化のイベントを3日待っても届かなければ1時間おいてリマインドを送る関数の骨組みです。メール送信はコメントの位置にSendGridなどのAPI呼び出しを書く想定で、このままでは宛先を返すだけです。

```
// src/inngest/functions.ts
import { NonRetriableError } from "inngest";
import { createClient } from "@supabase/supabase-js";
import { inngest } from "./client";

const supabase = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SECRET_KEY!
);

export const onboarding = inngest.createFunction(
  {
    id: "user-onboarding",
    triggers: { event: "app/user.signed_up" },
    retries: 5,
    concurrency: { limit: 10, key: "event.data.userId" },
  },
  async ({ event, step }) => {
    const profile = await step.run("load-profile", async () => {
      const { data, error } = await supabase
        .from("profiles")
        .select("id, email")
        .eq("id", event.data.userId)
        .maybeSingle();
      if (error) throw error;
      if (!data) throw new NonRetriableError("profile not found");
      return data;
    });

    await step.run("send-welcome-mail", async () => {
      // ここでメール送信APIを呼ぶ
      return { to: profile.email };
    });

    const activated = await step.waitForEvent("wait-for-activation", {
      event: "app/user.activated",
      timeout: "3d",
      match: "data.userId",
    });

    if (!activated) {
      await step.sleep("grace-period", "1h");
      await step.run("send-reminder", async () => {
        const { data } = await supabase
          .from("profiles")
          .select("activated_at")
          .eq("id", event.data.userId)
          .maybeSingle().throwOnError();
        if (!data || data.activated_at) return { skipped: true };
        // ここでメール送信APIを呼ぶ
        return { to: profile.email };
      });
    }

    return { userId: event.data.userId, activated: activated !== null };
  }
);
```

ステップの第1引数（`"load-profile"`など）はステップのIDで、ステップの状態をメモ化するキーに使われます。sleepや待機中のrunが残っている状態でIDを書き換えると、完了済みのステップを引き当てられず再実行につながるため、一度付けたIDは変えないでください。

`step.waitForEvent()`は、`match`で指定したフィールド（ここでは`data.userId`）が元のイベントと一致するイベントを待ち、タイムアウトすると`null`を返します。3日の起点はサインアップ時刻ではなく待ち始めた時刻です。待ち始める前や猶予の1時間に有効化されたユーザーは`waitForEvent`では拾えないため、リマインドのステップで送信直前に`activated_at`を読み直しています。待機中やsleep中、アプリ側のサーバーは何も実行していません。

### inngest.send()によるイベント送信

```
// src/app/api/signup/route.ts
import { NextResponse } from "next/server";
import { createClient } from "@supabase/supabase-js";
import { inngest } from "../../../inngest/client";

const supabase = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SECRET_KEY!,
  { auth: { persistSession: false, autoRefreshToken: false } }
);

export async function POST(req: Request) {
  const token = req.headers.get("authorization")?.replace(/^Bearer /, "");
  if (!token) {
    return NextResponse.json({ error: "unauthorized" }, { status: 401 });
  }
  const { data, error } = await supabase.auth.getUser(token);
  if (error || !data.user) {
    return NextResponse.json({ error: "unauthorized" }, { status: 401 });
  }
  await inngest.send({
    id: `profile-created-${data.user.id}`,
    name: "app/user.signed_up",
    data: { userId: data.user.id },
  });
  return NextResponse.json({ queued: true }, { status: 202 });
}
```

APIルートはAuthorizationヘッダーのアクセストークンをSupabaseで検証し、検証済みのユーザーIDだけをイベントに入れます。リクエスト本文のIDをそのまま使うと、第三者が他人のIDで関数を起動できてしまいます。`id`には後述のWebhook経路と同じ値を入れ、両方から送られても関数の起動が1回になるようにしています。応答が待つのは`inngest.send()`の完了までで、後続のメール送信の完了は待ちません。

## リトライとフロー制御の設定値

`createFunction`の第1引数には、リトライ回数と流量制御を並べて書けます。似た名前が多いので、超過したイベントをどう扱うかで区別すると選びやすくなります。

| オプション       | 制限する対象       | 超過分の扱い        |
| ----------- | ------------ | ------------- |
| retries     | ステップごとの再試行回数 | 0〜20・既定4      |
| concurrency | 同時に実行中のステップ数 | 待機して順に実行      |
| throttle    | 期間内に開始するrun数 | FIFOで後に回す     |
| rateLimit   | 期間内に開始するrun数 | 破棄（実行しない）     |
| debounce    | 連続イベントの実行    | 最後のイベントで1回    |
| idempotency | 同じキーのrun     | 24時間で1回       |
| priority    | キュー内の順序      | \-600〜600秒で前後 |

取り違えやすいのは`concurrency`です。上限の対象はrunの数ではなく実行中のステップ数で、sleepや`waitForEvent`で止まっているrunは数えません。開始頻度を抑えて超過分を後に回すのは`throttle`、超過分を捨てるのは`rateLimit`で、後者は取りこぼしてよい通知系に限って使ってください。

再試行しても結果が変わらない失敗（削除済みユーザーなど）は、`NonRetriableError`を投げて残りのリトライを止めます。相手APIが待ち時間を返す場合は`RetryAfterError`で次回の再試行時刻を指定できます。

ローカルで再試行を試すときは待ち時間に注意してください。リトライの間隔はジッター付きのバックオフで、CLI 1.48.0のDev Serverでステップを2回失敗させると、再試行は約43秒後と約36秒後に走り、runの完了まで82秒かかりました。`inngest dev --retry-interval 1`で起動すると間隔が1秒の線形になり、同じ関数（2秒のsleepを含む）は約4.3秒で完了しました。本番の間隔はこの設定では変わりません。

## Inngest CLIでrunを調べる：login・api・invoke-function

CLI 1.48.0のサブコマンドは`login`・`logout`・`auth`・`api`・`dev`・`start`・`version`の7つです。`api`はInngestのREST API v2から自動生成されたサブコマンド群（ベータ）で、ダッシュボードを開かずにrunの状態や各ステップの出力をJSONで取れます。既定の接続先はローカルのDev Serverで、こちらは認証なしで使えます。

```
# イベントIDから、起動したrunと出力を取る
inngest api get-event-runs <event_id> --include-output

# runのトレース（ステップごとの試行と時刻）
inngest api get-function-trace <run_id>

# アプリID・関数IDを指定して手動で起動する
inngest api invoke-function my-app user-onboarding --data '{"userId":"<ユーザーID>"}'

# Cloudの本番環境を見るときは、ログインしてから --prod と --env を付ける
inngest login
inngest api --prod --env production get-function-run <run_id>
```

前の章で再試行を確かめた関数に対してDev Serverで実行すると、`get-event-runs`は再試行中のrunを`"status": "RUNNING"`と直前のエラーメッセージ付きで返し、完了後は`"COMPLETED"`と`durationMs`、関数の戻り値を返しました。`get-function-trace`はステップごとに`Attempt 0`・`Attempt 1`の開始と終了の時刻を並べるので、前の章の再試行間隔もここから読み取れます。このほか`rerun`（元のイベントで新しいrunを作る）、`cancel-run`、`get-apps`などがあります。

`inngest login`はブラウザでOAuthの承認を行い、資格情報をOSの資格情報ストアに保存します。承認時に権限の範囲を選べるので、コーディングエージェントに本番のトレースを読ませるなら読み取りだけに絞ります。CIや無人のスクリプトでは、ダッシュボードで発行したAPIキーを`INNGEST_API_KEY`に入れます。APIキーはOAuthのログインより優先されます。v1時代の解説にある`inngest deploy`はもうありません。デプロイはアプリ側で行い、Inngestへは同期（後述のVercel連携か手動）で関数一覧を伝えます。

## Supabaseとの組み合わせ：Webhookで起動しIDだけを渡す

### イベントの設計：ユーザーIDの受け渡しとサーバー側の認証

Inngestの関数はユーザーのブラウザからではなく、Inngestのサーバーから呼ばれます。ログイン中のセッションやCookieは関数の中に届かないため、**イベントの`data`にはユーザーIDだけを入れ、関数の中でサーバー用の鍵を使ってSupabaseを読む**のが基本の形です。

サーバー用の鍵（新形式の`sb_secret_`で始まるsecretキー、または旧来のservice\_roleキー）はRLSを通らずに全行へアクセスできます。前の章のコードで`.eq("id", event.data.userId)`を省くと他人の行まで読めてしまうので、絞り込みは関数の側で必ず書いてください。鍵の世代と移行の注意点は[Supabase APIキーの管理：publishable/secret移行とレガシー鍵を回せない前提の設計](/tech/details/17068/)、ログイン処理そのものは[Supabase Next.js連携の実装｜proxyとgetClaimsで組むApp Router構成](/tech/details/17065/)で扱っています。

### Database Webhooks経由の関数起動と受信認証

テーブルへの行追加をきっかけに関数を動かしたい場合は、Supabaseの[Database Webhooks](https://supabase.com/docs/guides/database/webhooks)から自分のAPIルートを呼び、そこで`inngest.send()`します。Database Webhooksはpg\_net拡張を使ったトリガーのラッパーで、公式docsのSQL例ではタイムアウトが1000ミリ秒です。受け口は検証とイベント送信だけにして、重い処理はInngest側に任せます。

```
// src/app/api/supabase-webhook/route.ts
import { timingSafeEqual } from "node:crypto";
import { NextResponse } from "next/server";
import { inngest } from "../../../inngest/client";

type ProfileRow = { id: string; email: string };
type InsertPayload = {
  type: "INSERT";
  table: string;
  schema: string;
  record: ProfileRow;
  old_record: null;
};

function verify(header: string | null): boolean {
  const secret = process.env.SUPABASE_WEBHOOK_SECRET ?? "";
  if (!header || !secret) return false;
  const a = Buffer.from(header);
  const b = Buffer.from(secret);
  return a.length === b.length && timingSafeEqual(a, b);
}

export async function POST(req: Request) {
  if (!verify(req.headers.get("x-webhook-secret"))) {
    return NextResponse.json({ error: "unauthorized" }, { status: 401 });
  }
  const payload = (await req.json()) as InsertPayload;
  if (payload.type !== "INSERT" || payload.table !== "profiles") {
    return NextResponse.json({ ignored: true });
  }
  await inngest.send({
    id: `profile-created-${payload.record.id}`,
    name: "app/user.signed_up",
    data: { userId: payload.record.id },
  });
  return NextResponse.json({ queued: true }, { status: 202 });
}
```

Webhookの設定画面でHTTPヘッダー`x-webhook-secret`に任意の長い文字列を入れ、同じ値をアプリの環境変数`SUPABASE_WEBHOOK_SECRET`に置きます。Supabaseのdocsは、ヘッダーの値がトリガー定義に保存されてデータベースのダンプにも現れると注意しているので、DBのsecretキーをそのまま流用しないでください。

`inngest.send()`に渡した`id`は重複排除のキーです。Dev Serverで同じ`id`のイベントを3回送ると、`send()`は3回とも別のイベントIDを返しましたが、関数のrunは最初の1回だけでした。同じ行についてWebhookが二重に届いても、24時間以内なら関数の起動は1回に収まります。公式の[冪等性のガイド](https://www.inngest.com/docs/guides/handling-idempotency)によると、24時間を過ぎた同じIDのイベントは新しいイベントとして関数を起動します。期間を超える再送まで防ぐには、処理済みの記録をDBに残して関数の側でも確かめます。

## Vercelへのデプロイ：公式連携・maxDuration・Deployment Protection

### 公式連携が自動で行う3つの設定

本番では2つの環境変数が必要です。`INNGEST_SIGNING_KEY`はInngestとアプリの間のリクエストを署名・検証する鍵、`INNGEST_EVENT_KEY`はアプリがイベントを送るときの認証キーです。[公式のVercelガイド](https://www.inngest.com/docs-markdown/durable-execution/deploying-functions/platforms/vercel)によると、InngestのVercel連携（Vercel Marketplaceからも導入可能）はこの2つを自動で設定し、さらにVercelへデプロイするたびにアプリをInngestへ同期します。連携を使わない場合は、デプロイ後にInngestのダッシュボードかcurlのAPI呼び出しで手動同期します。

連携は同期にVercelがデプロイごとに発行する一意のURL（`https://myapp-xxxx-team.vercel.app`の形）を使います。本番でカスタムドメイン経由に呼ばせたいときは、Production環境の環境変数に`INNGEST_SERVE_ORIGIN=https://example.com`を設定します。そのドメインからアプリに届かないと同期は失敗します。stagingを分けたい場合は、Vercelのカスタム環境とInngestのカスタム環境を作り、その環境用のイベントキーと署名キーをVercel側のカスタム環境にだけ登録します。環境ごとの変数の分け方は[Vercelの環境変数の設定手順：Production・Previewの分け方とvercel env pull・Secret型【2026年10月】](/tech/details/18580/)を参照してください。

### maxDurationとcheckpointing.maxRuntimeの関係

Vercelの関数には実行時間の上限があります。v4ではチェックポイントが既定で有効なので、`/api/inngest`のルートに`maxDuration`を書き、クライアントの`checkpointing.maxRuntime`をそれより短く設定します。公式ガイドは`maxDuration`より20〜40%短くするよう勧めており、前の章のコードは300秒に対して240秒（20%短い値）にしています。`maxDuration`に指定できる上限はVercelのプランで変わるので、[Vercel料金プラン｜Hobby無料枠とPro($20)の中身・超過単価【2026年10月】](/tech/details/8792/)で確認してください。

### Deployment Protectionで同期と実行が止まる条件

VercelのDeployment Protectionが有効だと、Inngestのサーバーが`/api/inngest`に到達できず、同期も実行も失敗します。影響範囲は保護の設定で変わり、「Standard protection」と「All deployments」はInngestの本番環境とブランチ環境の両方、「Only preview deployments」はブランチ環境だけに効きます。**既定のデプロイ固有URLを使い、保護のバイパスを設定していない場合は、Standard protectionで本番の関数も止まる**点が見落とされやすいところです。

対処は、保護を外すか、使えるプランであればVercelの「Protection Bypass for Automation」を有効にし、発行されたシークレットをInngestダッシュボードのVercel連携設定の「Deployment protection key」に登録する方法です。登録後にプレビュー環境を再デプロイすると、アプリがInngestへ同期し直されます。Supabaseの環境変数もVercelへ同期している場合は、[Supabase Vercelデプロイ連携｜環境変数の自動同期とプレビュー環境のDB分離](/tech/details/17066/)とあわせて設定を揃えてください。

## v3・旧版コードからの移行で詰まる3点

日本語で読めるInngestの解説には、`new Inngest({ name })`や`inngest deploy`といったv1時代の書き方が残っているものがあります。v4で実際に止まるのは次の3点です。全項目は公式の[v3からv4への移行ガイド](https://www.inngest.com/docs/reference/typescript/v4/migrations/v3-to-v4)にまとまっています。

### cloudモードの既定化と署名キー未設定時の500応答

v3までの既定はdevモードでしたが、v4ではcloudモードになりました。`INNGEST_DEV`も署名キーも設定せずにinngest 4.26.0の`serve()`へGETを送ると、ログに「In cloud mode but no signing key found. For local dev, set the INNGEST\_DEV=1 env var.」と出て、応答は500でした。`inngest.send()`もイベントキーが見つからないというエラーで失敗します。`INNGEST_DEV=1`を付けると同じハンドラが200と`"mode":"dev"`を返します。ローカルで「関数がDev Serverに出てこない」ときは、まずこの環境変数を疑ってください。

### createFunctionのトリガー指定位置とイベントの形

v3までは`createFunction({ id }, { event: "..." }, handler)`の3引数でした。v4では`triggers`を第1引数のオプションに書く2引数形式になり、3引数のまま型検査するとTypeScriptは「TS2554: Expected 2 arguments, but got 3.」を出します。複数のトリガーは`triggers: [{ event: "..." }, { cron: "0 9 * * *" }]`のように配列で渡します。クライアントに置いていた`EventSchemas`も廃止され、イベントの型は`eventType()`で個別に定義する方式に変わりました。

イベントのトップレベルにあった`user`フィールドもv4で削除されました。注意したいのは、2026年10月24日時点の公式v4リファレンスの`inngest.send()`の例にまだ`user: { external_id, email }`が残っている点です。4.26.0でこの例を型検査すると「TS2353: Object literal may only specify known properties, and ‘user’ does not exist in type ‘SendEventPayload’.」になります。必要な値はすべて`data`へ入れてください。

### serve設定のクライアントへの移管とPromise.raceの変更

`signingKey`、`signingKeyFallback`、`baseUrl`、`fetch`は`serve()`ではなく`new Inngest()`に渡します。環境変数で渡していればコードの変更は不要です。`Promise.race`でステップを競わせていたコードは、v4の並列最適化の既定化ですべてのステップの完了を待つ挙動に変わるため、`group.parallel()`で包む必要があります。

v3を使い続ける場合は、npmの`v3-lts`タグ（2026年10月時点で3.54.0）が用意されています。移行するなら、まず`INNGEST_DEV`と`triggers`の2点から直します。

## 料金プランと上限：Free・Pro・Business・Enterprise

| 項目           | Free   | Pro    | Business |
| ------------ | ------ | ------ | -------- |
| 月額           | 0ドル    | 99ドルから | 499ドルから  |
| executions/月 | 5万     | 100万   | 1,000万   |
| 同時実行ステップ     | 5      | 100    | 500      |
| イベント受信/月     | 50万    | 500万   | 5,000万   |
| イベントサイズ      | 256KiB | 3MiB   | 3MiB     |
| runの最大期間     | 30日    | 90日    | 366日     |
| トレース保持       | 24時間   | 7日     | 14日      |
| ブランチ環境       | 2      | 10     | 10       |
| シート          | 5      | 15     | 30       |

2026年10月24日時点の[公式料金ページ](https://www.inngest.com/pricing)と[上限のドキュメント](https://www.inngest.com/docs-markdown/durable-execution/limits)の値です。Enterpriseは個別見積もりです。見積もりで効いてくるのはexecutionsの数え方で、1回のrunと各ステップの実行がそれぞれ1と数えられます。5ステップの関数は1回のrunで6executionsになり、月1万回動かすと6万でFreeの5万を超えます。Freeは超過分の従量課金が無く、上限に達すると実行が止まりアップグレードするまで再開しません。Proの超過は1executionあたり0.000050ドルから量に応じて0.000015ドルまで下がる段階制です。

「runの最大期間」は、`step.sleep()`や`step.waitForEvent()`で待っている時間を含めたrun全体の長さです。Freeでも1つのrunの中で合計30日まで待てるので、前の章の「3日待って1時間後にリマインド」はFreeの範囲に収まります。プランに関係なく、1つの関数のステップ数は1,000まで、ステップが返すデータは4MiBまで、run全体の状態は32MiBまでです。配列の要素ごとに`step.run()`を回すとステップ上限に届きやすいので、件数が多い場合は1ステップでまとめて処理するか、要素ごとに別のrunへイベントを送り分けます。

## セルフホストとライセンス：inngest startとSSPL

Inngestのサーバーは、CLIの`inngest start`で自前の環境でも動かせます。アプリ側には接続先の`INNGEST_BASE_URL`、`INNGEST_DEV=0`、サーバーと対応するイベントキーと署名キーを設定します。既定では8288番ポートで待ち受け、永続化はSQLite、キューと状態は内蔵のインメモリRedisを使います。本番で使うなら`--postgres-uri`と`--redis-uri`で外部のPostgreSQLとRedisを指定します。

```
inngest start --event-key <イベントキー> --signing-key <署名キー>
```

ライセンスは部品で分かれています。GitHubのREADMEは、サーバーとCLIを「Server Side Public License」と「delayed open source publication (DOSP) under Apache 2.0」で提供し、SDKはすべてApache 2.0と記載しています。[LICENSE.md](https://github.com/inngest/inngest/blob/main/LICENSE.md)には、公開から3年後にApache 2.0で使える権利を与える条項があります。SSPLは複製物の配布にも条件を定めているため、改変したサーバーを委託先や顧客へ配る場合や、**Inngest自体を外部向けのサービスとして提供する事業では、SSPLのソース公開義務の範囲を法務と詰めるまで採用を見送る**のが安全です。

もう1つ採用を見送る条件は、処理がすべて1リクエスト内の数秒で終わり、失敗しても再実行すれば済む場合です。このときは`after()`のようなNext.jsの仕組みや単純なキューで足り、Inngestを入れると外部サービスへの依存と鍵の管理が増えるだけになります。逆に、承認待ちや数日後のリマインドを含む業務フローをNext.jsのアプリに組み込むなら、Inngestの待機とリトライがそのまま効きます。こうした業務アプリの設計から依頼したい場合は[業務用・Webアプリ開発](https://www.issoh.co.jp/service/apps/business/)で相談を受け付けています。

## よくある質問

### Inngestの読み方は？

「インジェスト」と読みます。開発・運営は米国のInngest, Inc.で、GitHubのLICENSE.mdにも同社名で著作権表示があります。

### Inngestは無料で使えますか？

Freeプランは月額0ドル・月5万executionsまでで、クレジットカードの登録も不要です。Dev Serverを使ったローカル開発はプランに関係なく無料です。

### npmのinngestとinngest-cliは何が違いますか？

`inngest`はアプリに組み込むSDKで、依存に追加します。`inngest-cli`はDev Server・セルフホスト用サーバーの起動と`inngest api`でのrun調査に使うコマンドで、`npx`で都度呼べば入れる必要はありません。

### Inngest Cloudのダッシュボード（ログイン）や稼働状況はどこで確認できますか？

ダッシュボードは`app.inngest.com`からログインし、run履歴や署名キー・イベントキーの確認、Vercel連携の設定を行います。ターミナルからは`inngest login`のあと`inngest api --prod`で同じ情報を取れます。障害や遅延の情報は`status.inngest.com`で公開されています。

### TypeScript以外の言語やVercel以外の環境でも使えますか？

SDKはTypeScriptのほかにPython（PyPIの`inngest`、2026年10月時点で0.5.19）とGo（`inngestgo`、同v0.16.5）があります。GitHubのREADMEはKotlin/Java SDKも挙げていますが、リリースは2025年1月の0.2.2が最後です。TypeScript SDKは`serve()`のアダプタとしてExpress、Hono、Fastify、SvelteKit、AWS Lambdaなども用意しており、Vercel以外のサーバーやサーバーレス環境でも動かせます。

## 関連記事

- [Temporal Workflowとは？仕組み・Pythonの実測結果・Cloud料金を解説](/tech/details/9090/)
- [Lambda Durable Functionsとは？東京リージョン対応と15分制限を超える長時間ワークフロー実装](/tech/details/10107/)
- [BullMQとは？v6のPostgreSQL対応・リトライとワーカー運用を実装者目線で解説](/tech/details/17289/)
- [Supabase Next.js連携の実装｜proxyとgetClaimsで組むApp Router構成](/tech/details/17065/)
- [Vercelの環境変数の設定手順：Production・Previewの分け方とvercel env pull・Secret型【2026年10月】](/tech/details/18580/)

---

出典: [Inngestとは？使い方とNext.js・Vercel・Supabase連携をSDK v4で解説](<https://www.issoh.co.jp/tech/details/2595/>)（株式会社一創）
