A Helm chart for deploying Inngest on Kubernetes clusters.
- Kubernetes 1.20+
- Helm 3.0+
- Persistent Volume support for PostgreSQL and Redis
- LoadBalancer or Ingress controller for external access (optional)
- Storage class supporting ReadWriteOnce volumes
- KEDA operator (optional, for autoscaling)
Deploy Inngest with bundled PostgreSQL and Redis instances:
# Install with required secrets (creates "inngest" namespace automatically)
helm install inngest . \
--set inngest.eventKey="your_event_key_here" \
--set inngest.signingKey="your_signing_key_here" \
--create-namespace
Important: The eventKey
and signingKey
are required and must be hexadecimal strings for Inngest to function properly.
Create a my-values.yaml
file:
inngest:
eventKey: "your_event_key_here" # Must be a hexadecimal string
signingKey: "your_signing_key_here" # Must be a hexadecimal string
# Customize resource limits
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 500m
memory: 512Mi
Install with custom values:
helm install inngest . -f my-values.yaml --create-namespace
Key Concept: This chart uses consistent resource naming regardless of your chosen Helm release name.
- Helm Release Name: The first argument (
inngest
) is your chosen release name for Helm tracking - Kubernetes Resource Names: Always consistent:
inngest
,inngest-postgresql
,inngest-redis
Examples:
# All of these create identical Kubernetes resource names
helm install my-production-inngest . --create-namespace
helm install dev-environment . --create-namespace
helm install company-inngest . --create-namespace
# All result in the same resources:
# - service/inngest
# - deployment/inngest
# - configmap/inngest
# - service/inngest-postgresql
# - service/inngest-redis
Benefits:
- Consistent resource names across all environments
- Documentation examples work for everyone
- Scripts and automation can rely on predictable names
- Easy to reference services from applications
This is the simplest setup with bundled dependencies:
# values-internal.yaml
inngest:
eventKey: "your_event_key_here" # Must be a hexadecimal string
signingKey: "your_signing_key_here" # Must be a hexadecimal string
# Internal PostgreSQL (enabled by default)
postgresql:
enabled: true
auth:
database: inngest
username: inngest
password: secure_password
persistence:
enabled: true
size: 20Gi
# Internal Redis (enabled by default)
redis:
enabled: true
persistence:
enabled: true
size: 8Gi
# Resource limits
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 500m
memory: 512Mi
Deploy:
helm install inngest . -f values-internal.yaml --create-namespace
For production deployments with external managed databases:
# values-external.yaml
inngest:
eventKey: "your_event_key_here" # Must be a hexadecimal string
signingKey: "your_signing_key_here" # Must be a hexadecimal string
postgres:
uri: "postgres://username:[email protected]:5432/inngest"
redis:
uri: "redis://redis.example.com:6379"
# Disable internal dependencies
postgresql:
enabled: false
redis:
enabled: false
Deploy:
helm install inngest-prod . -f values-external.yaml --create-namespace
Enable KEDA-based autoscaling using Prometheus metrics from Inngest:
Important: KEDA scaling uses a Prometheus sidecar container to scrape the Inngest /metrics
endpoint. The sidecar handles Bearer token authentication using the signingKey
, and KEDA queries the sidecar's Prometheus API for scaling decisions based on inngest_queue_depth
.
# values-keda.yaml
inngest:
eventKey: "your_event_key_here" # Must be a hexadecimal string
signingKey: "your_signing_key_here" # Must be a hexadecimal string
# Enable KEDA autoscaling
keda:
enabled: true
minReplicas: 2
maxReplicas: 20
pollingInterval: 30
cooldownPeriod: 300
triggers:
- type: prometheus
metadata:
metricName: inngest_queue_depth
threshold: "10"
query: inngest_queue_depth
Deploy with KEDA:
# Install KEDA using Helm (recommended method)
helm repo add kedacore https://kedacore.github.io/charts
helm repo update
helm install keda kedacore/keda --namespace keda-system --create-namespace
# Verify KEDA installation
kubectl get pods -n keda-system
# Deploy Inngest with KEDA
helm install inngest . -f values-keda.yaml --create-namespace
Alternative KEDA Installation Methods:
# Method 1: Using kubectl (if Helm method fails)
kubectl apply --server-side -f https://github.com/kedacore/keda/releases/download/v2.12.0/keda-2.12.0.yaml
# Method 2: Using specific version via Helm
helm install keda kedacore/keda --version 2.12.0 --namespace keda-system --create-namespace
A comprehensive production setup with external dependencies, ingress, and monitoring:
# values-production.yaml
replicaCount: 3
inngest:
eventKey: "your_production_event_key" # Must be a hexadecimal string
signingKey: "your_production_signing_key" # Must be a hexadecimal string
logLevel: "info"
queueWorkers: 200
postgres:
uri: "postgres://inngest:[email protected]:5432/inngest"
redis:
uri: "redis://redis-prod.example.com:6379"
# Use external managed databases
postgresql:
enabled: false
redis:
enabled: false
# External access (configure ingress separately if needed)
# KEDA autoscaling configuration
keda:
enabled: true
minReplicas: 3
maxReplicas: 50
triggers:
- type: prometheus
metadata:
metricName: inngest_queue_depth
threshold: "10"
query: inngest_queue_depth
# Resource limits
resources:
limits:
cpu: 2000m
memory: 4Gi
requests:
cpu: 1000m
memory: 2Gi
# Security context
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
podSecurityContext:
fsGroup: 1000
# Network policy for security
networkPolicy:
enabled: true
Deploy production setup:
helm install inngest-prod . -f values-production.yaml --create-namespace
A complete ingress setup using internal PostgreSQL and Redis for external access:
Security Warning: When exposing Inngest through ingress, the web UI and GraphQL endpoints are publicly accessible unless protected. This example disables the UI for security. If you need the UI, implement proper authentication/authorization at the ingress level.
# values-ingress.yaml
inngest:
eventKey: "your_event_key_here" # Must be a hexadecimal string
signingKey: "your_signing_key_here" # Must be a hexadecimal string
logLevel: "info"
queueWorkers: 150
noUI: true # Disable UI for security when exposed via ingress
# Internal PostgreSQL (enabled by default)
postgresql:
enabled: true
auth:
database: inngest
username: inngest
password: secure_password
persistence:
enabled: true
size: 30Gi
# Internal Redis (enabled by default)
redis:
enabled: true
persistence:
enabled: true
size: 10Gi
# Ingress configuration for external access
ingress:
enabled: true
className: "nginx" # Use your ingress controller class
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
cert-manager.io/cluster-issuer: "letsencrypt-prod" # Required for automatic Let's Encrypt certificates
hosts:
- host: inngest.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: inngest-tls
hosts:
- inngest.example.com
# Security context
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
# Network policy for additional security (optional)
networkPolicy:
enabled: true
Deploy with ingress:
helm install inngest . -f values-ingress.yaml --create-namespace
Access URLs after deployment:
- Event API:
https://inngest.example.com
- API Endpoints:
https://inngest.example.com/api/*
- Connect Gateway:
https://inngest.example.com:8289
- UI Dashboard: Disabled for security (noUI: true)
Note: Replace inngest.example.com
with your actual domain and ensure DNS points to your ingress controller.
For automatic SSL certificate provisioning using Let's Encrypt, you need to install and configure cert-manager.
- Ingress Controller: You need an ingress controller (like nginx-ingress) already installed
- cert-manager: For automatic SSL certificate management
- Valid Domain: A domain name pointing to your ingress controller's IP address
Install cert-manager using Helm (recommended):
# Add the cert-manager repository
helm repo add jetstack https://charts.jetstack.io
helm repo update
# Install cert-manager with CRDs
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--set installCRDs=true
Verify cert-manager installation:
kubectl get pods -n cert-manager
Create a ClusterIssuer for Let's Encrypt certificate provisioning:
# letsencrypt-clusterissuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected] # CHANGE THIS TO YOUR EMAIL
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginx
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: [email protected] # CHANGE THIS TO YOUR EMAIL
privateKeySecretRef:
name: letsencrypt-staging
solvers:
- http01:
ingress:
class: nginx
Apply the ClusterIssuer:
# Update the email address in the file first
kubectl apply -f letsencrypt-clusterissuer.yaml
Here's a complete example with automatic SSL certificate provisioning:
# values-https-ingress.yaml
inngest:
eventKey: "your_event_key_here" # Must be a hexadecimal string
signingKey: "your_signing_key_here" # Must be a hexadecimal string
noUI: true # Disable UI for security when exposed via ingress
# Ingress configuration with Let's Encrypt
ingress:
enabled: true
className: "nginx"
annotations:
# Let's Encrypt annotations for automatic SSL certificate provisioning
cert-manager.io/cluster-issuer: "letsencrypt-prod"
# Use letsencrypt-staging for testing, letsencrypt-prod for production
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
hosts:
- host: inngest.yourdomain.com # CHANGE TO YOUR DOMAIN
paths:
- path: /
pathType: Prefix
tls:
- secretName: inngest-tls
hosts:
- inngest.yourdomain.com # CHANGE TO YOUR DOMAIN
Deploy with HTTPS:
helm install inngest . -f values-https-ingress.yaml --create-namespace
Check certificate status:
# Check certificate resource
kubectl get certificates -n inngest
kubectl describe certificate inngest-tls -n inngest
# Check certificate request
kubectl get certificaterequests -n inngest
# Check cert-manager logs
kubectl logs -n cert-manager deployment/cert-manager
Common Issues:
-
Certificate shows "False" for READY: Usually DNS or HTTP-01 challenge issues
- Verify your domain points to the ingress controller IP
- Check ingress controller logs
- Ensure port 80 is accessible for Let's Encrypt validation
-
Rate limiting from Let's Encrypt: Use
letsencrypt-staging
issuer for testing -
DNS propagation delays: Wait for DNS changes to propagate globally
Testing with Staging:
For testing, use the staging ClusterIssuer to avoid rate limits:
ingress:
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-staging" # Use staging for testing
Once working, switch to letsencrypt-prod
for the trusted certificate.
The chart creates and manages its own namespace by default. You can customize this behavior:
# Uses default "inngest" namespace
helm install inngest . --create-namespace
# values-custom-namespace.yaml
namespace:
create: true
name: "my-inngest-ns"
inngest:
eventKey: "your_event_key_here" # Must be a hexadecimal string
signingKey: "your_signing_key_here" # Must be a hexadecimal string
helm install inngest . -f values-custom-namespace.yaml --create-namespace
# values-existing-namespace.yaml
namespace:
create: false
name: "existing-namespace"
inngest:
eventKey: "your_event_key_here" # Must be a hexadecimal string
signingKey: "your_signing_key_here" # Must be a hexadecimal string
# Create namespace first if it doesn't exist
kubectl create namespace existing-namespace
helm install inngest . -f values-existing-namespace.yaml
Note: When using an existing namespace, don't use --create-namespace
flag.
After deployment, you can access Inngest through:
Resource Names: Regardless of your chosen release name, the Kubernetes resources are always named:
- Main service:
inngest
- PostgreSQL:
inngest-postgresql
- Redis:
inngest-redis
kubectl port-forward svc/inngest 8288:8288 -n inngest
# Access UI at http://localhost:8288
Configure your applications to send events to:
- Internal:
http://inngest:8288
(within cluster) - External:
https://inngest.example.com
(see ingress example above)
kubectl get pods -l app.kubernetes.io/name=inngest -n inngest
kubectl logs -l app.kubernetes.io/name=inngest -f -n inngest
kubectl get configmap inngest -o yaml -n inngest
kubectl get secret inngest -o yaml -n inngest
kubectl get scaledobject -n inngest
kubectl describe scaledobject inngest -n inngest
Check for resource constraints or storage issues:
kubectl describe pods -l app.kubernetes.io/name=inngest -n inngest
kubectl get pvc -n inngest
kubectl get storageclass
helm upgrade inngest . -f your-values.yaml -n inngest
Note: Always specify the namespace when upgrading to ensure Helm finds the correct release.
Inngest handles database migrations automatically on startup. Ensure you have backups before upgrading.
helm uninstall inngest -n inngest
Note: This will not delete persistent volumes. To delete all data:
kubectl delete pvc -l app.kubernetes.io/name=inngest -n inngest
Parameter | Description | Default | Required |
---|---|---|---|
namespace.create |
Create namespace | true |
No |
namespace.name |
Namespace name | "inngest" |
No |
inngest.eventKey |
Event key for sending events | "" |
Yes |
inngest.signingKey |
Signing key for validation | "" |
Yes |
postgresql.enabled |
Enable bundled PostgreSQL | true |
No |
redis.enabled |
Enable bundled Redis | true |
No |
ingress.enabled |
Enable ingress | false |
No |
keda.enabled |
Enable KEDA autoscaling | false |
No |
Security: This chart implements security best practices by default:
- Non-root user execution (UID 1000 for Inngest, 999 for PostgreSQL/Redis)
- Read-only root filesystem with temporary volumes for writable directories
- Dropped capabilities and disabled privilege escalation
- Database credentials stored in Kubernetes Secrets (not plain text)
- Network policies available for additional isolation
Resource Names: All Kubernetes resources use consistent names regardless of Helm release name:
- Main application:
inngest
(service, deployment, configmap, secret) - PostgreSQL:
inngest-postgresql
(service, deployment, pvc) - Redis:
inngest-redis
(service, deployment, pvc)
For a complete list of configuration options, see values.yaml
.