The latest version of this guide is published in the Chainlink documentation at https://docs.chain.link/docs/evm-performance-configuration/.

Optimizing EVM Performance | Chainlink Documentation

This guide explains how to configure Chainlink and your EVM nodes for high reliability and throughput.

For simple, small workloads with a handful of jobs that are rarely executed, the standard configuration is often enough. The most basic deployment of Chainlink has one primary node with a websocket URL and uses default configuration for everything.

However, as we scale up to hundreds of jobs and throughput moving into thousands of transactions per hour, we have to do things a bit differently. Large performance and reliability improvements are possible by configuring your Chainlink and RPC nodes correctly.

The first thing to understand is this: go-ethereum has bugs (as do its clones, they are all basically the same thing)

As an infra/devops person you will know that 90% of effort is spent on working around other people’s bugs. Working with go-ethereum and clones is no different. Much of the work done in core and specified here is done in order to mitigate and work around bugs in various different RPC implementations.

Using multiple nodes

<aside> 💡 Providing multiple primary nodes can improve performance and reliability

</aside>

As of 1.3.x and up, Chainlink supports multiple primary and sendonly nodes with automatic liveness detection and failover.

It is no longer necessary to run a load balancing failover RPC proxy between Chainlink and it’s EVM RPC nodes.

If you are using a failover proxy transparently (e.g. in the case of commercial node provider services) then it should continue to work properly as long as the RPC you are talking to acts just like a standard RPC node.

You can have as many primary nodes as you want. Requests are evenly distributed across all nodes so performance increase should be linear as you add more. If any particular node goes bad (e.g. no heads for X minutes, liveness checks fail) then it will be removed from the live pool and all requests gracefully routed over to one of the live nodes. If no live nodes are available it picks one of the dead ones at random.

You may also specify as many sendonly nodes as you want. Sendonly nodes are only used for broadcasting transactions and not for regular RPC calls. Specifying additional sendonly nodes uses a minimum of RPC calls and can help your transactions get included faster, they also act as backup in case your primary starts blackholing transactions (such behaviour has been observed in the wild many times).

<aside> 💡 Transaction broadcast is always sent to every single primary and sendonly node, therefore it is redundant and has no effect to try to specify the same URL for a sendonly node as an existing primary node

</aside>

Here is an example of how this can look:

export EVM_NODES='
[
	{
		"name": "primary_1",
		"evmChainId": "137",
		"wsUrl": "wss://endpoint-1.example.com/ws",
    "httpUrl": "<http://endpoint-1.example.com/>",
		"sendOnly": false
	},
	{
		"name": "primary_2",
		"evmChainId": "137",
		"wsUrl": "ws://endpoint-2.example.com/ws",
    "httpUrl": "<http://endpoint-2.example.com/>",
		"sendOnly": false
	},
	{
		"name": "primary_3",
		"evmChainId": "137",
		"wsUrl": "wss://endpoint-3.example.com/ws",
    "httpUrl": "<http://endpoint-3.example.com/>",
		"sendOnly": false
	},
	{
		"name": "sendonly_1",
		"evmChainId": "137",
		"httpUrl": "<http://endpoint-4.example.com/>",
		"sendOnly": true
	},
  {
		"name": "sendonly_2",
		"evmChainId": "137",
		"httpUrl": "<http://endpoint-5.example.com/>",
		"sendOnly": true
	},
]
'

Sendonly nodes

A quick note about sendonly nodes.