How to run a feed relay
If running a single node, there is no need to run a feed relay. When running more than one node, it is strongly recommended to run a single feed relay per data center, which will reduce ingress fees and improve stability.
Feed endpoints will soon require compression with a custom dictionary, so if connecting to a feed with anything other than a standard node, it is strongly suggested to run a local feed relay, which will provide an uncompressed feed by default.
For context on where the feed relay fits into Arbitrum's overall data flow, see Data availability. For the wire format the feed delivers, see How to read the sequencer feed. For how the feed relay compares to the other Nitro node roles, see How to assign roles to a Nitro node.
The feed relay is in the same Docker image as the Nitro node.
-
Here is an example of how to run the feed relay for Arbitrum One:
docker run --rm -it -p 0.0.0.0:9642:9642 --entrypoint relay offchainlabs/nitro-node:v3.11.3-beb2108 --node.feed.output.addr=0.0.0.0 --node.feed.input.url=wss://arb1-feed.arbitrum.io/feed --chain.id=42161 -
Here is an example of how to run
nitro-nodefor Arbitrum One with a custom relay:docker run --rm -it -v /some/local/dir/arbitrum:/home/user/.arbitrum -p 0.0.0.0:8547:8547 -p 0.0.0.0:8548:8548 offchainlabs/nitro-node:v3.11.3-beb2108 --parent-chain.connection.url=https://l1-mainnet-node:8545 --chain.id=42161 --http.api=net,web3,eth --http.corsdomain=* --http.addr=0.0.0.0 --http.vhosts=* --node.feed.input.url=ws://local-relay-address:9642
Note that Arbitrum Classic does not communicate with Nitro sequencer, so classic relay is no longer used.
Helm charts (Kubernetes)
If you are using Kubernetes to run your feed relay, a Helm chart is available at ArtifactHUB. It supports running a Nitro relay by providing the feed input URL. Find more information in the OCL community Helm charts repository.
Feed connection behavior
The sequencer feed is a long-lived WebSocket. Long-lived connections get terminated in normal operation by every layer they cross:
- Edge and CDN infrastructure. Public feed endpoints are typically served through CDN or edge proxy layers. Edge providers routinely restart servers as they roll out code across their networks, which drops the WebSocket connections those servers carry. Cloudflare, for example, documents this in its WebSockets technical note.
- Load balancing and capacity changes. When feed capacity scales up or down (for example, to absorb a traffic burst), connections are redistributed across instances. Every redistribution is a disconnect/reconnect for the affected clients.
- Ordinary transit events. Route changes, edge maintenance, and idle and lifetime limits along the path.
A healthy client logs a transient connection error at warning level (an EOF, a read timeout, a connection reset; the exact message varies by failure mode and client version), reconnects, and reports the feed connected again within seconds. Occurring a few times per day per connection, this is normal operation.
Two properties of the system bound the impact of any feed interruption:
- The feed is a latency optimization, not the source of truth. Every message is also posted to the parent chain in batches. A node that misses feed messages backfills them from the parent chain automatically. Feed loss can add seconds to minutes of head latency; it cannot cause data loss or an incorrect chain.
- Messages carry monotonic sequence numbers, so clients holding multiple simultaneous feed connections remove duplicates by sequence number.
What happens on reconnect (and why gaps appear)
On reconnect, a client asks the server to resume from a requested sequence number. A feed instance can only replay what is in its own catchup buffer. After a capacity change, a reconnecting client may land on a fresh instance that does not hold the earlier history, and gets resumed at that instance's current position instead; the client logs a warning that the incoming sequence number is greater than the one it expected. The node fills that gap from the parent chain. This is safe, and it is the event that multi-primary redundancy eliminates: with two or more simultaneous primaries, the other connection has been streaming the whole time and there is no gap to fill.
In summary: redundancy must live on the client side, as multiple simultaneous connections. Server-side resume semantics cannot guarantee gapless delivery across a single connection's lifecycle.
Reference architecture for node providers
If you run a fleet of nodes on behalf of customers (RPC provider, explorer, indexer), architect your relays and nodes for redundancy:
- Run your own feed relays. At least two, in different geographic regions, with distinct egress IPs.
- Each relay maintains at least two primary upstream connections to the upstream feed endpoint.
- Each node connects to at least two of your relays as primary feeds (a list of primary URLs), and at least one of them must be in a different region than the node. Do not use the secondary/fallback mechanism for redundancy.
- Never point a node fleet directly at the public feed. The relay layer collapses your fleet's footprint to a handful of upstream connections.
- Do not treat individual reconnects as incidents. Alert on sustained absence of feed data and on sustained head lag, not on individual transient connection errors.
If you follow 1-3, a disconnect on any single connection, relay, or region is invisible to your nodes. A single node run for your own use needs none of this: it will see periodic reconnects, briefly trail the chain head during them, and always converge via parent chain batches.
Layer 1: your relays
- At least two relays, geographically distributed. Edge and transit problems are usually regional (a single CDN point of presence, one provider's backbone). Two relays entering the network from different regions reduces those failures.
- Distinct egress IPs. Feed load balancers commonly route connections to backends by client source IP (session affinity), so all connections from one egress IP tend to land on the same backend and stay there. Assume your effective upstream redundancy is bounded by how many distinct egress IPs you use, not by how many relay pods or connections you run. Ten relays behind one NAT IP is one unit of redundancy.
- At least two primary upstream connections per relay:
relay \
--chain.id=42161 \
--node.feed.input.url=wss://arb1-feed.arbitrum.io/feed,wss://arb1-delayed-feed.arbitrum.io/feed \
--node.feed.output.addr=0.0.0.0 \
--node.feed.output.port=9642
The feed input accepts a comma-separated list of URLs. All of them are connected simultaneously and deduplicated by sequence number. Where a chain publishes only one public feed hostname, two connections to the same hostname from distinct egress IPs still provide meaningful redundancy (session affinity will generally pin them to different backends). Do not exceed two upstream connections per endpoint: each one doubles bandwidth, and excessive connections may be rate-limited at the edge.
Some chains publish more than one feed endpoint. Arbitrum One, for example, also publishes a delayed feed that intentionally lags the real-time sequencer feed. Adding an endpoint like this as an extra primary upstream broadens a relay's redundancy: under sequence number duplication removal it contributes nothing while a real-time connection is ahead, and it keeps messages flowing, at its own intentional delay, if the real-time connections are interrupted. Check the chain's documentation for the endpoints it publishes.
Layer 2: your nodes
Every node lists at least two of your relays as primary feeds, and the set must span regions: the relay local to the node plus at least one relay in another region:
nitro \
--node.feed.input.url=ws://relay-region1.internal:9642,ws://relay-region2.internal:9642 \
...
Nitro connects to every URL in the primary list at the same time and removes duplication by sequence number. A reconnect, restart, or regional problem on one relay is invisible: the other connection never stopped streaming.
Two relays in the same region share edge and transit fate: a regional event (a degraded CDN point of presence, a transit problem, a datacenter issue) interrupts both primaries at once. The relay in the other region keeps the node streaming through it. The cross-region hop adds a small amount of latency on that connection; sequence number deduplication means the node always advances at the pace of whichever connection is ahead, so the local relay still sets your steady-state latency.
Multiple primaries vs secondary-url fallback
Nitro has two distinct mechanisms for listing more than one feed source, and they behave very differently.
Multiple primaries (--node.feed.input.url with a list): every URL is connected simultaneously and permanently. Messages have duplicates removed by sequence number, and the node advances at the pace of whichever connection is ahead. Failover is instant and gapless because there is nothing to fail over: the other stream never stopped. The cost is bandwidth, since each connection carries the full feed.
Fallback (--node.feed.input.secondary-url): a standby list. In steady state, a secondary is not connected at all. The client opens a secondary connection only after the primaries have delivered no messages for a short inactivity window (on the order of seconds), brings secondaries up one at a time, and tears them back down once a primary has been delivering continuously again for a sustained period (on the order of minutes). Three consequences follow:
- It is inactivity-triggered, not lag-triggered. A primary that is behind but still streaming never trips it, so it provides no protection against a lagging upstream.
- Activation is not gapless: by the time it fires, the node has already been silent for the length of the inactivity window, and normal reconnect and catchup dynamics apply on the new connection.
- When active, it is additive: the secondary runs alongside the primary rather than replacing it.
When a fallback is appropriate:
- As a break-glass input of a different class than your primaries, on a limited subset of nodes. For example: primaries on your two relays and a secondary pointing at the public feed, so that a total failure of your own relay layer still self-heals. Keep this to a small subset of nodes: applied fleet-wide, it creates a large direct public feed footprint when activated (every node opens its own secondary), subject to per-IP affinity and edge rate limiting.
- When bandwidth genuinely rules out a second always-on stream for a given node.
A fallback is not a redundancy mechanism between equivalent sources. If two feed sources are both acceptable to consume continuously, list them both as primaries and let sequence number deduplication do the work. Avoid the inverted arrangement (your own relay as the only primary, with the public feed as a fleet-wide secondary): it provides no protection against a lagging relay and produces a fleet-wide public feed footprint whenever it activates.
Never point the fleet directly at the public feed
Nitro's reconnect loop retries roughly every 15 seconds, per node, indefinitely. A fleet of direct clients aggregates into a per-IP connection pattern that edge protections rate limit, and a rate-limited client can stay banned because it keeps retrying. Two relays collapse an arbitrarily large fleet into a handful of upstream connections.
Operational guidance for your fleet
- Health checks: do not mark a node unhealthy because it logged a feed reconnect. Gate on sustained head lag, with tolerance for brief catch-up bursts after a reconnect, where messages per second spike while the node drains the backlog.
- Alert on state, not events:
arb_feed_sources_connected == 0sustained for more than a minute (the node has no feed input at all)- head lag versus a reference RPC sustained beyond your latency SLO
- relay upstream disconnect rate materially above its own baseline