# 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)

## 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/learn/security?ref=docs-self-hosting) for more information including details about [end-to-end encryption](/docs/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/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/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/features/inngest-functions/steps-workflows/wait-for-event?ref=docs-self-hosting) with matching expressions.
  * Cancels running functions with matching [`cancelOn`](/docs/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/guides/flow-control?ref=docs-self-hosting) methods (concurrency, throttling, prioritization, debouncing, rate limiting) and [batching](/docs/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).

<CodeGroup>
```plaintext {{ title: 

npm" }}
npm install -g inngest-cli
```
```plaintext {{ title: "Docker" }}
docker pull inngest/inngest
```
```plaintext {{ title: "curl" }}
curl -sfL https://cli.inngest.com/install.sh
```
</CodeGroup>

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

<CodeGroup>
```plaintext {{ title: "shell" }}
inngest start --event-key abcd --signing-key 1234
```
```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
```
</CodeGroup>

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`.

##
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/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:
```plaintext
openssl rand -hex 32
```
</Callout>

```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`:

```plaintext
$ 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
   --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.
   --event-key string [ --event-key string ]                    Event key(s) that will be used by apps to send events to the server.
   --sqlite-dir string                                          Directory for where to write 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.
   --postgres-uri string                                        PostgreSQL database URI for configuration and history persistence. Defaults to SQLite database.
   --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-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)
   --poll-interval int                                          Interval in seconds between polling for updates to apps (default: 0)
   --retry-interval int                                         Retry interval in seconds for linear backoff when retrying functions - must be 1 or above (default: 0)
   --queue-workers int                                          Number of executor workers to execute steps from the queue (default: 100)
   --tick int                                                   The interval (in milliseconds) at which the executor polls the queue (default: 150)
   --connect-gateway-port int                                   Port to expose connect gateway endpoint (default: 8289)
   --no-ui                                                      Disable the web UI and GraphQL API endpoint (default: false)
   --help, -h                                                   show help

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")
```

<details>
<summary>**Environment variables**</summary>

  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.
</details>

<details>
<summary>**Configuration file** (`inngest.yaml`, `inngest.json`, etc.)</summary>

  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:

  <CodeGroup>
    ```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"
    }
    ```
  </CodeGroup>
</details>

### 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/sdk/environment-variables#inngest-dev) and [`INNGEST_BASE_URL`](/docs/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", "curl", "-f", "http://localhost:8288/health"]
      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

```

<DownloadLink url="/files/docker-compose.yml" children="Download docker-compose.yml" filename="docker-compose.yml" />

## Helm chart

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

**Quick Setup & Production Ready**
- 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:

[<Github size="1.25em" className="inline-block mr-1"/> inngest/inngest-helm](https://github.com/inngest/inngest-helm)

## Roadmap & feature requests

Planned features for self-hosting include:

* Improved health checks.
* Backlog monitoring endpoints for auto-scaling.
* High availability guide.
* Helm chart for easy deployment to Kubernetes.
* Data retention guides and recommendations.
* Event key and signing key management via API and UI.

To suggest additional features, please submit feedback on our [public roadmap](https://roadmap.inngest.com/roadmap).

Check out [the source code on GitHub](https://github.com/inngest/inngest) to file issues or submit pull requests.