| name | inngest-cli |
| description | Use when installing or running the Inngest CLI and Dev Server for local development, local testing, serve endpoint debugging, Docker or Docker Compose setup, MCP configuration, self-hosted `inngest start`, or deployment workflow checks. Covers `inngest dev`, `inngest start`, auto-discovery, config files, environment variables, `@inngest/test`, local event sending, platform gotchas, and production/self-hosted server flags. |
Inngest CLI
Master the Inngest CLI for local development, testing, and self-hosted production. The CLI provides the Dev Server — a fully-featured, open-source local version of the Inngest Platform.
These skills are focused on TypeScript. For Python or Go, refer to the Inngest documentation for language-specific guidance. Core concepts apply across all languages.
Use this skill for CLI setup, Dev Server workflows, local testing, Docker, MCP, and self-hosted server operations. For run/trace inspection through inngest api, use inngest-api-cli when available.
Installation
npx inngest-cli@latest dev
yarn dlx inngest-cli@latest dev
pnpm dlx inngest-cli@latest dev
npm install -g inngest-cli
docker pull inngest/inngest
If your npm configuration disables lifecycle scripts and the binary is missing, retry with npx --ignore-scripts=false inngest-cli@latest dev. Bun does not support lifecycle scripts by default, so prefer npx for the CLI even in Bun projects.
inngest dev — Local Dev Server
Starts an in-memory local version of Inngest with a browser UI at http://localhost:8288.
npx inngest-cli@latest dev
npx inngest-cli@latest dev -u http://localhost:3000/api/inngest
npx inngest-cli@latest dev -p 9999
npx inngest-cli@latest dev \
-u http://localhost:3000/api/inngest \
-u http://localhost:4000/api/inngest
npx inngest-cli@latest dev --no-discovery -u http://localhost:3000/api/inngest
CLI Flags
| Flag | Short | Default | Description |
|---|
--sdk-url | -u | http://localhost:3000/api/inngest | App serve endpoint URL(s) |
--port | -p | 8288 | Dev Server port |
--host | | | Dev Server host |
--no-discovery | | false | Disable auto-discovery of apps |
--no-poll | | false | Disable polling apps for changes |
--config | | | Path to configuration file |
--connect-gateway-port | | 8289 | Connect gateway endpoint port |
--persist | | false | Persist data between restarts |
--poll-interval | | 5 | Seconds between app polling checks |
--queue-workers | | 100 | Number of executor workers |
--retry-interval | | 0 | Linear retry interval in seconds |
--tick | | 150 | Executor queue polling interval in milliseconds |
Auto-Discovery
Without --no-discovery, the Dev Server scans common ports and endpoints automatically:
Ports scanned: Common development ports including 3000, 3030, and others
Endpoints scanned:
/api/inngest
/x/inngest
/.netlify/functions/inngest
/.redwood/functions/inngest
Configuration File
Create an inngest.json (or .yaml, .toml) in your project root. The CLI walks up directories to find it.
{
"sdk-url": [
"http://localhost:3000/api/inngest",
"http://localhost:3030/api/inngest"
],
"no-discovery": true
}
sdk-url:
- "http://localhost:3000/api/inngest"
- "http://localhost:3030/api/inngest"
no-discovery: true
Environment Variables
| Variable | Description |
|---|
INNGEST_DEV | =1 enables Dev Mode (disables signature verification). =0 forces Cloud mode. Accepts a URL (e.g., http://localhost:8288). Defaults to Cloud mode if unset. |
INNGEST_BASE_URL | Host for SDK-to-Inngest communication (e.g., http://localhost:8288). Leave unset in most cases. |
INNGEST_EVENT_KEY | Authentication key for sending events. Use any dummy value locally — Dev Server does not validate. |
INNGEST_SIGNING_KEY | Secures requests between Inngest and your app. Required in production. Determines which Inngest environment receives syncs. |
INNGEST_SIGNING_KEY_FALLBACK | Fallback key for signing key rotation (v3.18.0+). |
INNGEST_SERVE_ORIGIN | Full origin URL for Inngest to reach your app (e.g., https://my-app.com). Auto-inferred from request headers; set explicitly for AWS Lambda, proxies, or tunnels. |
INNGEST_SERVE_PATH | URL path to your serve endpoint (e.g., /api/inngest). Auto-inferred in most cases. |
INNGEST_STREAMING | Enable response streaming (true/false). Extends timeout limits on Vercel and edge runtimes. |
INNGEST_ENV | Target Inngest Environment. Auto-detected on some platforms. |
Debugging Your Serve Endpoint
Verify your serve() endpoint is configured correctly:
curl -s http://localhost:3000/api/inngest | jq
{
"message": "Inngest endpoint configured correctly.",
"hasEventKey": false,
"hasSigningKey": false,
"functionsFound": 3
}
If functionsFound is 0, check that your functions are passed to the serve() call.
Testing Functions Locally
Send Events via SDK
import { Inngest } from "inngest";
const inngest = new Inngest({ id: "my-app" });
await inngest.send({
name: "user/signup.completed",
data: {
userId: "user_123",
email: "user@example.com",
},
});
Send Events via curl
curl -X POST "http://localhost:8288/e/test" \
-H "Content-Type: application/json" \
-d '{
"name": "user/signup.completed",
"data": {
"userId": "user_123",
"email": "user@example.com"
}
}'
The event key in the URL path (test above) can be any value locally.
Unit Testing with @inngest/test
Requires inngest@>=4.0.0.
npm install -D @inngest/test
import { InngestTestEngine } from "@inngest/test";
import { helloWorld } from "./functions";
const t = new InngestTestEngine({ function: helloWorld });
const { result } = await t.execute();
expect(result).toEqual("Hello World!");
const { result: stepResult } = await t.executeStep("calculate-price");
expect(stepResult).toEqual(123);
const { state } = await t.execute();
await expect(state["my-step"]).resolves.toEqual("output");
await expect(state["risky-step"]).rejects.toThrowError("failed");
const { result: eventResult } = await t.execute({
events: [{ name: "demo/event.sent", data: { message: "Hi!" } }],
});
const { result: mockResult } = await t.execute({
steps: [{ id: "external-api-call", handler() { return { status: "ok" }; } }],
});
await t.execute({
steps: [{ id: "wait-one-day", handler() {} }],
});
Mock external dependencies with your test framework's standard mocking (jest.mock, vi.mock, etc.) — @inngest/test handles only Inngest-specific mocking.
Docker Setup
Standalone
docker run -p 8288:8288 -p 8289:8289 \
inngest/inngest \
inngest dev -u http://host.docker.internal:3000/api/inngest
Use host.docker.internal to reach your app running on the host machine (works out of the box on Docker Desktop). On Linux, add --add-host=host.docker.internal:host-gateway or use an equivalent host-access method.
Docker Compose
services:
app:
build: ./app
environment:
- INNGEST_DEV=1
- INNGEST_BASE_URL=http://inngest:8288
ports:
- "3000:3000"
inngest:
image: inngest/inngest
command: "inngest dev -u http://app:3000/api/inngest"
ports:
- "8288:8288"
- "8289:8289"
Port 8288 is the main server and UI. Port 8289 is the connect() WebSocket gateway.
Critical: Set INNGEST_DEV=1 on your app — the TypeScript SDK defaults to Cloud mode, which will skip the Dev Server.
MCP Server (AI Dev Tools)
The Dev Server exposes an MCP server at http://127.0.0.1:8288/mcp (HTTP transport).
claude mcp add --transport http inngest-dev http://127.0.0.1:8288/mcp
{
"mcpServers": {
"inngest-dev": {
"url": "http://127.0.0.1:8288/mcp"
}
}
}
Available MCP Tools
| Tool | Description |
|---|
send_event | Send events to trigger functions |
list_functions | List all registered functions and triggers |
invoke_function | Execute a function synchronously (default 30s timeout) |
get_run_status | Get detailed status of a function run |
poll_run_status | Poll multiple runs until completion |
grep_docs | Search Inngest documentation by regex pattern |
read_doc | Read a specific documentation page |
list_docs | List available documentation structure |
inngest start — Self-Hosted Production
Runs Inngest as a self-hosted production server. Not the same as inngest dev — this is for production workloads.
inngest start --event-key <key> --signing-key <key>
| Flag | Short | Default | Description |
|---|
--port | -p | 8288 | Server port |
--signing-key | | | Hex key for request signing (even character count) |
--event-key | | | Authentication key for apps (repeatable) |
--sdk-url | -u | | App serve URLs (repeatable) |
--redis-uri | | | External Redis connection |
--postgres-uri | | | External PostgreSQL connection |
--sqlite-dir | | | SQLite database directory |
--poll-interval | | 0 | App sync polling interval (seconds) |
--queue-workers | | 100 | Number of executor workers |
--connect-gateway-port | | 8289 | Connect gateway port |
--retry-interval | | 0 | Linear retry interval in seconds |
--tick | | 150 | Executor queue polling interval in milliseconds |
--no-ui | | | Disable web UI and GraphQL API |
--postgres-conn-max-idle-time | | 5 | PostgreSQL idle connection lifetime in minutes |
--postgres-conn-max-lifetime | | 30 | PostgreSQL maximum connection reuse time in minutes |
--postgres-max-idle-conns | | 10 | PostgreSQL max idle connections |
--postgres-max-open-conns | | 100 | PostgreSQL max open connections |
Global flags such as --log-level, --verbose, and --json are also available. For environment variables, follow the current CLI and deployment docs; do not assume every flag has an INNGEST_ environment variable equivalent.
Default persistence: in-memory Redis + SQLite at ./.inngest/main.db. For production, use external Redis and PostgreSQL.
Deployment Workflow
Local Development → Production
- Develop locally with
inngest dev — no keys needed, no code changes for production
- Deploy your app to your hosting platform
- Sync with Inngest using one of three methods:
curl -X PUT https://your-app.com/api/inngest --fail-with-body
- Set environment variables in production:
INNGEST_EVENT_KEY=<your-event-key>
INNGEST_SIGNING_KEY=<your-signing-key>
No code changes are needed when moving from local dev to production. The SDK automatically detects the environment.
Platform-Specific Gotchas
| Platform | Gotcha |
|---|
| Express | Requires express.json() middleware; default body limit is 100kb — increase to handle Inngest payloads (up to 4MB) |
| AWS Lambda | Set INNGEST_SERVE_ORIGIN and INNGEST_SERVE_PATH explicitly — auto-inference fails |
| Firebase Cloud Functions | Must set INNGEST_SERVE_PATH env var |
| DigitalOcean Functions | Both serveOrigin and servePath required in serve() config |
Cloudflare Workers (Wrangler --remote) | Requires tunnel (ngrok/localtunnel) for Dev Server connection |
| Supabase Edge Functions | servePath must match function name — Supabase rewrites request paths |
| Google Cloud Run (1st gen) | Not officially supported; may cause signature verification errors |
| Docker | Must set INNGEST_DEV=1 — SDK defaults to Cloud mode |
| External webhooks (Stripe, Clerk) | Require tunnel solution (ngrok, localtunnel) for local testing |
Quick Reference
npx inngest-cli@latest dev
npx inngest-cli@latest dev -u http://localhost:3000/api/inngest
curl -s http://localhost:3000/api/inngest | jq
curl -X POST http://localhost:8288/e/test -d '{"name": "test/event", "data": {}}'
curl -X PUT https://your-app.com/api/inngest --fail-with-body
inngest start --event-key <key> --signing-key <key>
Troubleshooting
| Issue | Cause | Solution |
|---|
| Dev Server doesn't find functions | App not running or wrong port | Start your app first; use -u to specify the correct URL |
functionsFound: 0 in debug output | Functions not passed to serve() | Verify all functions are in the array passed to serve() |
| SDK connects to Cloud instead of Dev Server | INNGEST_DEV not set | Set INNGEST_DEV=1 in your environment |
| Functions sync to wrong Inngest environment | Wrong signing key | Check INNGEST_SIGNING_KEY matches target environment |
| Duplicate app in Inngest dashboard | App id was changed | Keep the id in new Inngest({ id }) stable across deploys |
| Webhook events not reaching Dev Server | No tunnel configured | Use ngrok or localtunnel for external webhook sources |
| "Unattached sync" in dashboard | Auto-sync failed silently | Check integration logs; resync manually |
See inngest-setup for SDK installation and inngest-durable-functions for function configuration.