import { Time } from '@n8n/constants'; import { z } from 'zod'; import { Config, Env, Nested } from '../decorators'; const workerPoolNameSchema = z .string() .regex( /^([a-z0-9][a-z0-9-]{0,62})?$/, 'N8N_WORKER_POOL_NAME must be empty or 1-63 chars of lowercase alphanumeric and hyphens, starting with alphanumeric', ); @Config export class WorkerPoolConfig { /** * Whether worker pools are enabled. When off, all executions route to the default queue and the pool UI is hidden. * * **Experimental** - Use at your own risk. */ @Env('N8N_WORKER_POOLS_ENABLED') enabled: boolean = false; /** * Label identifying the worker pool this worker belongs to. * Empty (default) means the worker listens to the unlabeled `jobs` queue. * When set, the worker listens to `jobs-` instead. * Read only on worker instances; ignored on main and webhook. */ @Env('N8N_WORKER_POOL_NAME', workerPoolNameSchema) name: string = ''; } @Config class HealthConfig { /** * Whether to enable worker health endpoints: `/healthz` (liveness) and `/healthz/readiness` (DB and Redis ready). */ @Env('QUEUE_HEALTH_CHECK_ACTIVE') active: boolean = false; /** Port the worker HTTP server listens on for health checks. */ @Env('QUEUE_HEALTH_CHECK_PORT') port: number = 5678; /** IP address the worker server binds to. Use `::` for all interfaces. */ @Env('N8N_WORKER_SERVER_ADDRESS') address: string = '::'; } @Config class RedisTlsConfig { /** SNI extension servername for TLS handshake. */ @Env('QUEUE_BULL_REDIS_TLS_SERVERNAME') serverName: string = ''; /** * When TLS enabled validate certificates. * - true (default): Recommended for secure production deployments to ensure redis connections are not vulnerable to MITM attacks. * - false: Accept any certificate presented by the server for local development or self-signed certificate scenarios. */ @Env('QUEUE_BULL_REDIS_TLS_VALIDATE_CERTIFICATE') rejectUnauthorized: boolean = true; } @Config class RedisConfig { /** Redis database for Bull queue. */ @Env('QUEUE_BULL_REDIS_DB') db: number = 0; /** Redis host for Bull queue. */ @Env('QUEUE_BULL_REDIS_HOST') host: string = 'localhost'; /** Password to authenticate with Redis. */ @Env('QUEUE_BULL_REDIS_PASSWORD') password: string = ''; /** Port for Redis to listen on. */ @Env('QUEUE_BULL_REDIS_PORT') port: number = 6379; /** Max cumulative timeout (in milliseconds) of connection retries before process exit. */ @Env('QUEUE_BULL_REDIS_TIMEOUT_THRESHOLD') timeoutThreshold: number = 10 * Time.seconds.toMilliseconds; /** Slot refresh timeout (in milliseconds) before a timeout occurs while refreshing slots from the cluster. */ @Env('QUEUE_BULL_REDIS_SLOT_REFRESH_TIMEOUT') slotsRefreshTimeout: number = 1 * Time.seconds.toMilliseconds; /** Slot refresh interval (in milliseconds) between every automatic slot refresh. */ @Env('QUEUE_BULL_REDIS_SLOT_REFRESH_INTERVAL') slotsRefreshInterval: number = 5 * Time.seconds.toMilliseconds; /** Redis username. Redis 6.0 or higher required. */ @Env('QUEUE_BULL_REDIS_USERNAME') username: string = ''; /** Redis cluster startup nodes, as comma-separated list of `{host}:{port}` pairs. @example 'redis-1:6379,redis-2:6379' */ @Env('QUEUE_BULL_REDIS_CLUSTER_NODES') clusterNodes: string = ''; /** Whether to enable TLS on Redis connections. */ @Env('QUEUE_BULL_REDIS_TLS') tls: boolean = false; @Nested tlsConfig: RedisTlsConfig; /** * DNS resolution strategy for Redis hostnames on initial client connection. * - `LOOKUP` (default): Use system DNS resolver to resolve hostnames to IP addresses. * - `NONE`: Disable DNS resolution and pass hostnames directly to Redis client. * * DNS lookups can be error prone, especially in combination with TLS certificates. * Especially AWS Elasticache cluster connections often lead to invalid certificate errors due to hostname/ip mismatches. * For AWS ElastiCache clusters with TLS, it is recommended to set this option to `NONE`. * @see https://github.com/redis/ioredis?tab=readme-ov-file#special-note-aws-elasticache-clusters-with-tls */ @Env('QUEUE_BULL_REDIS_DNS_LOOKUP_STRATEGY') dnsResolveStrategy: 'LOOKUP' | 'NONE' = 'LOOKUP'; /** Whether to enable dual-stack hostname resolution for Redis connections. */ @Env('QUEUE_BULL_REDIS_DUALSTACK') dualStack: boolean = false; /** Whether to enable TCP keep-alive on Redis connections. */ @Env('QUEUE_BULL_REDIS_KEEP_ALIVE') keepAlive: boolean = false; /** TCP keep-alive initial delay in milliseconds. */ @Env('QUEUE_BULL_REDIS_KEEP_ALIVE_DELAY') keepAliveDelay: number = 5 * Time.seconds.toMilliseconds; /** TCP keep-alive interval in milliseconds. */ @Env('QUEUE_BULL_REDIS_KEEP_ALIVE_INTERVAL') keepAliveInterval: number = 5 * Time.seconds.toMilliseconds; /** Whether to reconnect to Redis on READONLY errors i.e., failover events. */ @Env('QUEUE_BULL_REDIS_RECONNECT_ON_FAILOVER') reconnectOnFailover: boolean = true; } @Config class SettingsConfig { /** How long (in milliseconds) is the lease period for a worker processing a job. */ @Env('QUEUE_WORKER_LOCK_DURATION') lockDuration: number = 1 * Time.minutes.toMilliseconds; /** How often (in milliseconds) a worker must renew the lease. */ @Env('QUEUE_WORKER_LOCK_RENEW_TIME') lockRenewTime: number = 10 * Time.seconds.toMilliseconds; /** How often (in milliseconds) Bull must check for stalled jobs. `0` to disable. */ @Env('QUEUE_WORKER_STALLED_INTERVAL') stalledInterval: number = 30 * Time.seconds.toMilliseconds; } @Config class BullConfig { /** Prefix for Bull keys on Redis. @example 'bull:jobs:23' */ @Env('QUEUE_BULL_PREFIX') prefix: string = 'bull'; @Nested redis: RedisConfig; /** @deprecated How long (in seconds) a worker must wait for active executions to finish before exiting. Use `N8N_GRACEFUL_SHUTDOWN_TIMEOUT` instead */ @Env('QUEUE_WORKER_TIMEOUT') gracefulShutdownTimeout: number = 30; @Nested settings: SettingsConfig; } @Config export class ScalingModeConfig { @Nested health: HealthConfig; @Nested bull: BullConfig; @Nested workerPool: WorkerPoolConfig; /** * Whether a worker that receives a shutdown signal suspends in-flight * production executions at the next node boundary. Suspended executions are * persisted as 'waiting' and resumed on another worker, instead of running * to completion (or force-kill) on the exiting worker. An execution that * still owes its webhook caller a response is not suspended. */ @Env('N8N_WORKER_SUSPEND_EXECUTIONS_ON_SHUTDOWN') suspendExecutionsOnShutdown: boolean = false; }