Automation

How can I optimize the number of active workers in n8n queue mode to match my total vCPU availability?

Calculate the optimal n8n worker concurrency by aligning the --concurrency flag with vCPU cores and managing the Node.js event loop to prevent CPU throttling.

6 min read
How can I optimize the number of active workers in n8n queue mode to match my total vCPU availability?

We run the technical operations for our German-hosted stack at Hostna, and we frequently analyze how Node.js processes interact with virtualized hardware to ensure stability for automation workloads. When you transition from the standard execution mode to queue mode in n8n, the relationship between your workflow volume and your hardware resources changes fundamentally. The primary mechanism for controlling this relationship is the --concurrency flag or the N8N_CONCURRENCY_LIMIT environment variable. Setting this value too low results in an underutilized server and a growing execution queue, while setting it too high leads to CPU contention, increased latency, and potential system instability.

Symptom Probable Cause Technical Resolution
High CPU steal time Over-provisioned host or high concurrency Lower --concurrency or move to vds-hosting
Worker process exits abruptly OOM Killer intervention Increase RAM or lower N8N_CONCURRENCY_LIMIT
Slow webhook response Node.js event loop blocking Deploy more workers with lower individual concurrency

Understanding the Concurrency-to-vCPU Ratio

The optimal number of workers depends on whether your workflows are CPU-bound or I/O-bound. A CPU-bound workflow, such as one performing heavy data transformation or image processing, requires significant cycles from the processor. In these cases, a 1:1 ratio between the total concurrency across all workers and the number of available vCPU cores is a sensible default. If your linux-vps-hosting instance has 4 vCPUs, setting a total concurrency of 4 ensures that each execution has a high probability of accessing a physical thread without waiting in the kernel scheduler queue.

For I/O-bound workflows—those that spend most of their time waiting for responses from external APIs, databases, or file systems—you can often set the concurrency higher. In these scenarios, the CPU remains idle while waiting for network packets. A ratio of 2:1 or even 3:1 per vCPU core is often effective. However, this must be balanced against the memory overhead of each active execution. Each n8n worker process has a baseline memory footprint, and each active execution adds to this total. If the worker attempts to use more memory than the cgroup memory limits allow, the kernel will send a SIGKILL to the process to protect the host.

The Impact of Node.js Event Loop Blocking

Node.js is single-threaded by nature, meaning it uses an event loop to handle asynchronous operations. While n8n workers can handle multiple concurrent executions, a single long-running synchronous task in one workflow can block the event loop for all other executions assigned to that specific worker process. This is why horizontal scaling—running multiple worker containers with lower concurrency—is often more resilient than running a single worker with very high concurrency.

By spreading the load across multiple processes, you reduce the blast radius of a single blocking execution. For example, on a machine with 8 vCPUs, running four worker containers each with --concurrency=2 is generally more stable than one worker with --concurrency=8. This approach ensures that even if one event loop is blocked, the other three workers continue to process the queue. This is particularly important when scaling n8n workflows that involve complex JavaScript code in Function nodes, which are executed synchronously within the main process.

Memory Management and the OOM Killer

Memory is frequently the primary bottleneck before CPU exhaustion occurs. The swap and the OOM killer will intervene if the combined memory footprint of your workers exceeds the physical limit of the plan you are on. To calculate your safe concurrency limit, you must first establish the average memory usage of a single execution. You can monitor this using the top or htop command while a representative workflow is running.

# Check the number of available vCPU cores
nproc

# Monitor memory usage of n8n worker processes
top -p $(pgrep -d ',' -f "n8n worker")

If an average execution consumes 100 MB and your worker process has a 512 MB baseline, a concurrency of 10 would require at least 1.5 GB of RAM. If your entry-level hosting solutions do not provide sufficient overhead, the kernel will prioritize system stability by terminating the most memory-intensive processes. Monitoring the CPU steal time on your Hostna instance is the first step in identifying if the host is over-subscribed or if your worker is being throttled by the hypervisor.

Virtualization Architecture and Performance

The type of virtualization used for your virtual private server environments significantly impacts how the Node.js event loop interacts with the hardware. In a KVM virtualization environment, the guest operating system has its own kernel, providing better isolation and more predictable scheduling. This is a critical factor when managing a dedicated resource allocation, where the vCPU versus dedicated core distinction becomes apparent; a dedicated core is not subject to the same scheduling delays as a shared vCPU.

In a KVM versus OpenVZ comparison, the former allows for better kernel-level resource management, which is essential when the Node.js event loop is under heavy load. Furthermore, the NVMe IOPS versus throughput trade-off is critical when workers are reading large datasets from disk or writing extensive logs to a database. If your workflows are database-heavy, ensure that your managing a Linux instance includes sufficient I/O headroom to prevent the worker from entering a permanent iowait state.

Network Latency and Redis Optimization

In queue mode, the communication between the main n8n process, the workers, and the Redis broker is a frequent source of hidden latency. When configuring a reverse proxy for n8n, a TLS 1.3 one-RTT handshake reduces the initial connection latency for webhook triggers, but internal communication should ideally happen over a low-latency private network. Properly configuring the DNS TTL and propagation for your Redis and database endpoints ensures that workers can quickly reconnect during a failover event or a maintenance window.

If you notice that execution times are increasing despite low CPU usage, verify the network latency between your worker and the database service. You can use the ping command to check the round-trip time to your Redis instance. If the latency is high, consider moving the Redis broker to the same physical location as your workers. For high-performance requirements, bare metal performance may be necessary to eliminate the overhead introduced by the virtualization layer entirely.

According to the official n8n technical documentation, scaling involves not just increasing the number of workers, but also ensuring that the underlying database (PostgreSQL is recommended for production) can handle the increased connection pool. Each worker process maintains its own connection pool to the database, which can quickly exhaust the max_connections setting in your database configuration.

Troubleshooting and Support

If your workers are still failing after optimizing concurrency, the next step is to examine the logs for specific error codes. Look for ETIMEDOUT or ECONNRESET, which often indicate that the worker cannot communicate with Redis or the primary database. If the worker process restarts frequently without a clear error in the n8n logs, check the system journal for OOM killer messages using dmesg | grep -i oom. When contacting support, please provide the output of free -m, nproc, and the contents of your docker-compose.yml file, ensuring that all sensitive credentials are redacted. This allows for a precise analysis of the resource allocation on distributed computing resources.

This article was drafted with AI assistance from Hostna’s own research pipeline and published without individual review. Tell us if anything here is wrong.

Questions people also ask4
How many n8n workers should I run per vCPU?

For CPU-intensive tasks, a 1:1 ratio of concurrency to vCPU cores is the most suitable starting point. If your workflows are primarily waiting on external APIs, you can increase this to 2 or 3 executions per core. Always monitor memory usage, as each execution adds significant RAM overhead to the worker process baseline.

What is the difference between --concurrency and N8N_CONCURRENCY_LIMIT?

Both settings control how many active executions a single worker process can handle simultaneously. The --concurrency flag is used when starting the worker via the command line, while N8N_CONCURRENCY_LIMIT is an environment variable often used in containerized environments. They perform the same function of limiting concurrent task processing within the Node.js event loop.

Why does my n8n worker crash when I increase concurrency?

The most common reason is memory exhaustion. Each active execution consumes RAM, and if the total usage exceeds the limits of the plan you are on, the kernel OOM killer will terminate the process. To prevent this, calculate the average memory per execution and ensure your total concurrency fits within your available physical memory.

Should I run one large worker or multiple small workers?

Running multiple worker processes with lower concurrency is generally more reliable. Because Node.js is single-threaded, one blocking task can stall an entire worker process. By spreading executions across multiple containers, you ensure that a single slow workflow does not stop all other concurrent tasks from processing in the queue.