# Self-hosting

Self-hosting support for Inngest is supported as of the 1.0 release.

- [Why self-host Inngest?](#why-self-host-inngest)
- [Inngest system architecture](#inngest-system-architecture)
- [How to self-host Inngest](#how-to-self-host-inngest)

> **Info:** Inngest's support team does not guarantee direct support for self-hosted instances. If you need help, please file an issue.
> &#x20;If you require dedicated support or service guarantees for self-hosting, please contact us to explore enterprise options.

## Why self-host Inngest?

While the easiest way to get started with Inngest is using our hosted platform, including our generous [free tier](/pricing?ref=docs-self-hosting), we understand that developers may want to self-host for a variety of reasons. If security or data privacy are concerns, review our [security documentation](/docs-markdown/learn/security?ref=docs-self-hosting) for more information including details about [end-to-end encryption](/docs-markdown/features/middleware/encryption-middleware?ref=docs-self-hosting).

## Inngest system architecture

To best understand how to self-host Inngest, it's important to understand the system architecture and components.

![Inngest system architecture diagram](/assets/docs-markdown/self-hosting/system-architecture-2024-09-23.png)

The system is composed of the following services:

- **Event API** - Receives events from SDKs via HTTP requests. Authenticates client requests via [Event Keys](/docs-markdown/events/creating-an-event-key?ref=docs-self-hosting). The Event API publishes event payloads to an internal event stream.
- **Event stream** - Acts as a buffer between the *Event API* and the *Runner*.
- **Runner** - Consumes incoming events and performs several actions:
  - Scheduling of new "function runs" (aka jobs) given the event type, creating initial run state in the *State store* database. Runs are added to queues given the function's flow control configuration.
  - Resumes functions paused via [`waitForEvent`](/docs-markdown/features/inngest-functions/steps-workflows/wait-for-event?ref=docs-self-hosting) with matching expressions.
  - Cancels running functions with matching [`cancelOn`](/docs-markdown/features/inngest-functions/cancellation/cancel-on-events?ref=docs-self-hosting) expressions.
  - Writes ingested events to a database for historical record and future replay.
- **Queue** - A multitenant-aware, multitier queue designed for fairness and various [flow control](/docs-markdown/guides/flow-control?ref=docs-self-hosting) methods (concurrency, throttling, prioritization, debouncing, rate limiting) and [batching](/docs-markdown/guides/batching?ref=docs-self-hosting).
- **Executor** - Responsible for executing functions, from initial execution, step execution, writing incremental function run state to the *State store*, and retries after failures.
- **State store (database)** - Persists data for pending and ongoing function runs. Data includes initial triggering event(s), step output and step errors.
- **Database** - Persists system data and history including Apps, Functions, Events, Function run results.
- **API** - GraphQL and REST APIs for programmatic access and management of system resources.
- **Dashboard UI** - The UI to manage apps, functions and view function run history.

The source code for Inngest and all services is [available on GitHub](https://github.com/inngest/inngest).

## How to self-host Inngest

To begin self-hosting Inngest, you only need to install the Inngest CLI. The Inngest CLI is a single binary that includes all Inngest services and can be run in any environment. Alternatively, you can download the binary directly from [GitHub releases](https://github.com/inngest/inngest/releases).

```plaintext {{ title: "Docker" }}
docker pull inngest/inngest
```

```plaintext {{ title: "npm" }}
npm install -g inngest-cli
```

```plaintext {{ title: "curl" }}
curl -sfL https://cli.inngest.com/install.sh
```

Now that you have the CLI installed, you can start the Inngest server using the `inngest start` command.

```plaintext {{ title: "Docker" }}
docker run -p 8288:8288 -p 8289:8289 -e INNGEST_EVENT_KEY=abcd -e INNGEST_SIGNING_KEY=1234 inngest/inngest inngest start
```

```plaintext {{ title: "shell" }}
inngest start --event-key abcd --signing-key 1234
```

This will start the Inngest server on the default port `8288` and use the default configuration, including SQLite for persistence. Additionally, the Connect gateway will be available on port `8289`.

### Configuration

Configuring the server can be done via command-line flags, environment variables, or a configuration file.

By default, the server will:

- Run on `localhost:8288` to serve the Event API, API, and Dashboard UI. `localhost:8289` is used for `connect` workers ([see docs for more connect-specific configuration](/docs-markdown/setup/connect#self-hosted-inngest)).
- Use an in-memory Redis server for the queue and state store. (See [Using external services](#using-external-services) for more information.)
- Use SQLite for persistence. The default database is located at `./.inngest/main.db`. Queue and state store snapshots are periodically saved to the SQLite database, including prior to shutdown.
- Disable app sync polling to check for new functions or updated configurations (see `--poll-interval` flag).

To securely configure your server, create your event and signing keys using whatever format you choose and start the Inngest server using them. You can also pass them via environment variable (see below):

> **Callout:** The signing key must be a valid hexadecimal string with an even number of characters. You can generate a secure signing key using:openssl rand -hex 32

```plaintext
inngest start --event-key <YOUR_EVENT_KEY> --signing-key <YOUR_SIGNING_KEY>
```

Then you can use these same keys as environment variables when starting your application (`INNGEST_EVENT_KEY` and `INNGEST_SIGNING_KEY`). [See below](#configuring-inngest-sdks-to-use-self-hosted-server) for an example Node.js startup command.

To see all the available options, run `inngest start --help`:

```
$ inngest start --help
NAME:
   inngest start - [Beta] Run Inngest as a single-node service.

USAGE:
   inngest start [options]

DESCRIPTION:
   Example: inngest start

OPTIONS:
   --config string                                              Path to an Inngest configuration file
   --event-key string [ --event-key string ]                    Event key(s) that will be used by apps to send events to the server.
   --help, -h                                                   show help
   --host string                                                Inngest server hostname
   --port string, -p string                                     Inngest server port (default: "8288")
   --sdk-url string, -u string [ --sdk-url string, -u string ]  App serve URLs to sync (ex. http://localhost:3000/api/inngest)
   --signing-key string                                         Signing key used to sign and validate data between the server and apps. Must be hex string with even number of chars

   Advanced

   --connect-gateway-port int  Port to expose connect gateway endpoint (default: 8289)
   --no-ui                     Disable the web UI and GraphQL API endpoint (default: false)
   --poll-interval int         Interval in seconds between polling for updates to apps (default: 0)
   --queue-workers int         Number of executor workers to execute steps from the queue (default: 100)
   --retry-interval int        Retry interval in seconds for linear backoff when retrying functions - must be 1 or above (default: 0)
   --tick int                  The interval (in milliseconds) at which the executor polls the queue (default: 150)

   Persistence

   --postgres-conn-max-idle-time int  Sets the maximum amount of time, in minutes, a PostgreSQL connection may be idle. (default: 5)
   --postgres-conn-max-lifetime int   Sets the maximum amount of time, in minutes, a PostgreSQL connection may be reused. (default: 30)
   --postgres-max-idle-conns int      Sets the maximum number of idle database connections in the PostgreSQL connection pool. (default: 10)
   --postgres-max-open-conns int      Sets the maximum number of open database connections allowed in the PostgreSQL connection pool. (default: 100)
   --postgres-uri string              PostgreSQL database URI for configuration and history persistence. Defaults to SQLite database.
   --redis-uri string                 Redis server URI for external queue and run state. Defaults to self-contained, in-memory Redis server with periodic snapshot backups.
   --sqlite-dir string                Directory for where to write SQLite database.

GLOBAL OPTIONS:
   --json                         Output logs as JSON.  Set to true if stdout is not a TTY. (default: false)
   --verbose                      Enable verbose logging. (default: false)
   --log-level string, -l string  Set the log level.  One of: trace, debug, info, warn, error. (default: "info")
```

**Environment variables**

Any CLI option can be set via environment variable by converting the flag to uppercase, replacing hyphens with underscores, and prefixing it with `INNGEST_`. For example, `--port 8288` can be set with the `INNGEST_PORT` environment variable.

To set the log level, use `LOG_LEVEL=WARN` or whatever level you choose. This environment variable does not use the Inngest prefix. The environment variable will always take precedence over the CLI `--log-level` flag.

**Configuration file** (`inngest.yaml`, `inngest.json`, etc.)

A configuration file can be specified with the `--config` flag. The file can be in YAML, JSON, TOML, or any other format supported by [Viper](https://github.com/spf13/viper). `urls` is used instead of `sdk-url` to specify your application's Inngest serve endpoints. An example configuration file is shown below:

```yaml {{ title: "inngest.yaml" }}
urls:
  - http://localhost:3000/api/inngest
poll-interval: 60
redis-uri: redis://localhost:6379
sqlite-dir: /app/data
```

```json {{ title: "inngest.json" }}
{
  "urls": [
    "http://localhost:3000/api/inngest"
  ],
  "poll-interval": 60,
  "redis-uri": "redis://localhost:6379",
  "sqlite-dir": "/app/data"
}
```

Note, configuring the log level can only be currently done via flag or environment variable.

### Configuring Inngest SDKs to use self-hosted server

By default, the Inngest SDK will use URLs of the managed Inngest platform. To connect to a self-hosted server, set the [`INNGEST_DEV`](/docs-markdown/sdk/environment-variables#inngest-dev) and [`INNGEST_BASE_URL`](/docs-markdown/sdk/environment-variables#inngest-base-url) environment variables. As mentioned above, you'll also need to set the `INNGEST_EVENT_KEY` and `INNGEST_SIGNING_KEY` environment variables for securely connecting your application to the Inngest server.

For example, to connect to a self-hosted server running on `localhost:8288` for a Node.js app, set the following environment variables:

```plaintext
INNGEST_EVENT_KEY=<YOUR_EVENT_KEY> \
  INNGEST_SIGNING_KEY=<YOUR_SIGNING_KEY> \
  INNGEST_DEV=0 \
  INNGEST_BASE_URL=http://localhost:8288 \
  node ./server.js
```

### Using external services

Inngest can be configured to use external services for the queue and state store, and soon, the database.

**External Redis server**

With the goal of simplifying the initial setup, the Inngest server will run an in-memory Redis server for the queue and state store. As this is running within the same process as the Inngest server, running your own Redis server can improve performance and reliability of the system. You may choose to run your own Redis server or use a cloud-based Redis service like AWS ElastiCache, Redis Cloud, etc.

To use an external Redis server, set the `redis-uri` flag to the Redis server URI.

**External Postgres database**

By default, the Inngest server uses SQLite for persistence. This is convenient for zero-dependency deployments, but does not support scaling beyond a single node. You may choose to run your own Postgres database or use a cloud-based Postgres service like AWS RDS, Neon, Supabase, etc.

To use an external Postgres database, set the `postgres-uri` flag to your Postgres connection string URI.

## Docker compose example

```yaml
services:
  inngest:
    image: inngest/inngest
    command: "inngest start"
    ports:
      - "8288:8288" # APIs and Dashboard
      - "8289:8289" # Connect WebSocket gateway
    environment:
      - INNGEST_EVENT_KEY=your_event_key_here # Must be hex string with even number of chars
      - INNGEST_SIGNING_KEY=your_signing_key_here # Must be hex string with even number of chars
      - INNGEST_POSTGRES_URI=postgres://inngest:password@postgres:5432/inngest
      - INNGEST_REDIS_URI=redis://redis:6379
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - inngest-network
    healthcheck:
      test: ["CMD", "inngest", "alpha", "doctor", "healthcheck"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  postgres:
    image: postgres:17
    environment:
      - POSTGRES_DB=inngest
      - POSTGRES_USER=inngest
      - POSTGRES_PASSWORD=password
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - inngest-network
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U inngest -d inngest"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    networks:
      - inngest-network
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

volumes:
  postgres_data:
  redis_data:

networks:
  inngest-network:
    driver: bridge

```

**Note**: The `inngest alpha doctor` command was added in `v1.19.3`, before this version, you can define a healthcheck using `test: ["CMD", "curl", "-f", "http://localhost:8288/health"]`

## Helm chart

Inngest provides a Helm chart for production-ready Kubernetes deployments. Key features include:

**Quick Setup & Production Ready**

- Installable from OCI Registry ([see docs](https://github.com/inngest/inngest-helm?tab=readme-ov-file#installation))
- Supports Kubernetes 1.20+ and Helm 3.0+ with simple installation
- Bundled PostgreSQL and Redis for easy setup, with support for external databases
- Consistent resource naming across all environments regardless of Helm release name
- Deployment examples for development, production, and hybrid scenarios

**Autoscaling & Performance**

- KEDA-based autoscaling using Inngest's Prometheus metrics
- Configurable replica counts, resource limits, and queue worker settings
- Horizontal pod autoscaling based on queue depth

**Security & Access**

- Security-first design with non-root execution and read-only filesystem
- Database credentials stored securely in Kubernetes Secrets
- Network policy support for additional cluster isolation
- Built-in ingress support with SSL certificate provisioning via cert-manager
- Let's Encrypt integration for production HTTPS endpoints

You can find the chart, documentation, and configuration examples here:

**inngest/inngest-helm**: [View source code, check release notes and file issues.]('https://github.com/inngest/inngest-helm')

## Prometheus metrics&#x20;

Inngest exposes a Prometheus-compatible scrape endpoint at `GET /metrics` on the server port (e.g. `http://localhost:8288/metrics`). By default it emits a single gauge, `inngest_queue_depth`. To enable per-function lifecycle counters (`inngest_function_run_*_total`, `inngest_sdk_req_*_total`), start the server with the experimental flag enabled: `INNGEST_EXPERIMENTAL_PROM_METRICS=1`. Point your Prometheus scrape config at the `/metrics` path on that port — no auth is configured by default.

## Performance considerations

Each system workload is different, so each deployment of the Inngest server may require different adjustments for scaling. We recommend monitoring your Redis and Postgres servers to asses how you may need to scale for your workflow. Consider some of the following options for improving performance:

- **Execution slowness** - Functions are executing slower than expected.
  - Function configuration: Adjust the [flow control](/docs-markdown/guides/flow-control) configuration for your functions that may be limiting throughput.
  - Increase `--queue-workers`: By default, each Inngest server only runs `100` queue workers in parallel for execution. Increase this number to increase the amount of concurrent executions for each Inngest server ([see configuration](#configuration)). Note that this will increase CPU and memory usage on your Inngest server.
  - Horizontal scaling: You can opt to horizontally scale your Inngest server to add more capacity by adding more replicas/containers to your setup instead of increasing `--queue-workers`.
  - Redis vertical scaling: If your Redis server is showing high CPU usage, you may need to vertically scale your Redis server(s).
- **Dashboard or API slowness** - It's slow to load parts of the Inngest server dashboard.
  - Postgres vertical scaling: If your Postgres server is undersized, it may cause slowness under load. Review Postgres metrics to help determine how you may need to scale your database.
  - Postgres table periodic cleanup: The Inngest server does not automatically delete old database rows within Postgres for events, runs, or traces. Large table growth can hurt performance for loading or searching runs or events.&#x20;
  - Custom indexes:

## Roadmap & feature requests

Planned features for self-hosting include:

- Improved diagnostic checks and APIs (via `inngest alpha doctor` and `inngest alpha debug` - currently in alpha)
- Postgres data retention guides and commands

To suggest additional features, report issues, submit pull requests, and view the source code please visit the repo on GitHub:

**inngest/inngest**: [View source code, check release notes and file issues.]('https://github.com/inngest/inngest')