> For a complete page index, fetch <https://docs.arbitrum.io/llms.txt>

# Sequencer architecture and transaction flow

This deep dive follows a transaction through a single Sequencer instance: how it arrives, how it waits in the transaction queue, how blocks get created, and how the result reaches the rest of the network. It is aimed at chain operators and integration partners who need to reason about queueing, timeouts, and block timing.

Two companion pages cover the surrounding context, and this page assumes you have read them:

* [The Sequencer and censorship resistance](/how-arbitrum-works/deep-dives/sequencer.md) explains the Sequencer's role, the real-time feed, batch posting, and finality.
* [Transaction lifecycle](/how-arbitrum-works/deep-dives/transaction-lifecycle.md) explains the different ways to submit a transaction, including bypassing the Sequencer entirely.

> **NOTE** — Scope of this page
>
> This page describes the internals of a single Sequencer instance. For the behavior of Arbitrum One's public sequencer endpoint (latency expectations, retries, and fallback patterns), see [RPC endpoints and providers](/arbitrum-essentials/reference/node-providers.md). For running multiple redundant Sequencers with Redis-based coordination, see [How to set up a high-availability sequencer](/launch-arbitrum-chain/run-a-node/high-availability-sequencer.md) and [How to run a Sequencer Coordinator Manager](/run-arbitrum-node/sequencer/run-sequencer-coordination-manager.md).

All code references below point to [Nitro `v3.11.0`](https://github.com/OffchainLabs/nitro/tree/a618155919315241665356fe60f3cd00d66d5e46).

## Transaction flow at a glance

![Transaction flow from a user through an RPC node into the Sequencer. Inside the Sequencer, a two-stage mempool holds an unordered waiting list, and each ordering round promotes the whole list into a priority queue keyed on the priority fee. Block creation takes the highest priority fee first, publishes each transaction to the Fast Feed inside the round, and publishes the closed block to the sequencer feed for full nodes and feed relays. The batch poster sends the compressed sequence to the sequencer inbox on the parent chain, where validators verify it.](/img/haw-sequencer-transaction-flow.svg)

1. A user submits a signed transaction to any RPC node on the chain.
2. The RPC node does not execute or pool the transaction; it forwards it to the Sequencer.
3. The Sequencer places the transaction in a bounded, in-memory queue.
4. The block creation loop drains the queue in the order the chain's ordering policy dictates, and executes transactions one at a time to build a block.
5. The new block is published on the Sequencer Feed for real-time consumers, and the batch poster later posts the compressed sequence to the sequencer inbox on the parent chain.
6. Validators re-execute the sequence posted on the parent chain to verify and assert the chain's state.

> **NOTE** — Validators do not accept user transactions
>
> Validators only read the ordered transactions from the parent chain and re-execute them locally to compute the chain's state. They have no transaction queue and no way to accept, order, or process a user transaction directly. The only ingestion point for user transactions is the Sequencer, and RPC nodes exist to forward transactions to it. (The one exception, submitting through the delayed inbox on the parent chain, is covered in [Transaction lifecycle](/how-arbitrum-works/deep-dives/transaction-lifecycle.md).)

## How transactions reach the Sequencer

Only one node on the chain actively sequences transactions at any given time (in a high-availability setup, several sequencer-capable nodes may run behind a coordinator, which picks the active one; see the scope note above). Every other node runs a forwarder: when it receives a transaction over RPC, it immediately relays the raw transaction to the Sequencer's endpoint instead of processing it locally ([`forwarder.go`](https://github.com/OffchainLabs/nitro/blob/a618155919315241665356fe60f3cd00d66d5e46/execution/gethexec/forwarder.go#L126)). The target is configured with `--execution.forwarding-target`; for known chains, Nitro fills it in automatically from the chain's configuration when the node is not a sequencer. A non-sequencer node with no forwarding target (explicitly set to `"null"`) simply drops incoming transactions.

Unlike Ethereum, there is **no public mempool**. Transactions are not gossiped between nodes and never sit in a public pending pool. The Sequencer receives them directly into its own in-memory structures, which stay private no matter which ordering policy the chain runs.

### Ordering policies

What the Sequencer does with a queued transaction depends on the chain's transaction ordering policy. Three exist, and a chain runs exactly one:

| Policy                                                                         | What decides the order                            | Where it runs                        |
| ------------------------------------------------------------------------------ | ------------------------------------------------- | ------------------------------------ |
| First-come, first-serve (FCFS)                                                 | Arrival time at the Sequencer                     | The default for a new Arbitrum chain |
| [Priority Gas Auctions (PGA)](/how-arbitrum-works/priority-gas-auction/pga.md) | The priority fee each transaction pays            | Arbitrum One                         |
| [Timeboost](/how-arbitrum-works/timeboost/gentle-introduction.md)              | An auctioned express lane, FCFS for everyone else | Available to any chain owner         |

The FIFO behavior this page describes assumes FCFS. The two other policies change the order, not the intake path: the queue, the deadlines, and the `context deadline exceeded` behavior below apply the same way under all three.

Under PGA, the queue described in the next section is the first stage of a two-stage mempool. Each ordering round moves the whole queue into a priority queue keyed on the priority fee, which EIP-1559 computes as `min(max_priority_fee_per_gas, max_fee_per_gas - base_fee_per_gas)`. Ties break on arrival time, and a transaction left waiting gains an anti-starvation boost each round, so a zero-fee transaction still lands within a small number of blocks. [Introduction to PGA](/how-arbitrum-works/priority-gas-auction/pga.md) covers the rounds and the boost.

Under Timeboost, transactions from the current express lane controller are sequenced as soon as they arrive, while every other transaction has its arrival timestamp delayed (by default, 200 milliseconds) before taking its place in the queue.

Arbitrum One runs PGA

Arbitrum One used Timeboost from April 2025 until PGA activated. PGA removes the 200ms delay that Timeboost applied to transactions outside the express lane, so no transaction waits on another's lane. Chain owners can still choose Timeboost, but a chain that enables both policies behaves unpredictably.

## The transaction queue

The heart of the intake path is a bounded, in-memory queue, sized by `--execution.sequencer.queue-size` (default `1024`) ([`sequencer.go#L461`](https://github.com/OffchainLabs/nitro/blob/a618155919315241665356fe60f3cd00d66d5e46/execution/gethexec/sequencer.go#L461)). Its behavior has three distinct zones:

* **Inside the queue**: under FCFS, transactions are ordered strictly **FIFO**, so whatever enters the queue first gets sequenced first. Under PGA, the queue is an unordered waiting list, and the priority fee decides the order when a round promotes it.
* **At the queue boundary, when the queue is full**: a transaction has "reached the Sequencer but is not yet queued." The Sequencer holds the submission open and waits for a slot to free up ([`sequencer.go#L731-L735`](https://github.com/OffchainLabs/nitro/blob/a618155919315241665356fe60f3cd00d66d5e46/execution/gethexec/sequencer.go#L731-L735)). Ordering among these waiting transactions is **not guaranteed**: when a slot frees up, which waiting transaction claims it depends on runtime scheduling, not arrival order.
* **Rejected**: if a transaction cannot be queued and sequenced before its deadline (see below), it is rejected and never executes.

### Queue timeout and `context deadline exceeded`

Every transaction receives a deadline when it arrives, set by `--execution.sequencer.queue-timeout` (default `12s`) ([`sequencer.go#L557-L560`](https://github.com/OffchainLabs/nitro/blob/a618155919315241665356fe60f3cd00d66d5e46/execution/gethexec/sequencer.go#L557-L560)). The deadline is enforced at two points:

1. **While waiting to enter a full queue**: if no slot frees up before the deadline, the submission fails immediately.
2. **Again at dequeue time**: when the block creation loop pops a transaction from the queue, it first checks whether the transaction's deadline already expired while it sat in the queue, and rejects it if so ([`sequencer.go#L1360-L1364`](https://github.com/OffchainLabs/nitro/blob/a618155919315241665356fe60f3cd00d66d5e46/execution/gethexec/sequencer.go#L1360-L1364)).

In both cases, the caller receives an error containing the string `context deadline exceeded`. This error means exactly one thing: the chain could not sequence the transaction within `queue-timeout`, and the transaction **was not executed**. For a user or integration partner, the correct response is:

* Treat it as backpressure, not as a permanent failure. It is safe to resubmit the same signed transaction (same nonce) with a backoff.
* Make sure your client-side HTTP timeout is longer than the chain's `queue-timeout`; otherwise your client gives up before the Sequencer reports the outcome.
* If you see this error persistently rather than in bursts, the chain's intake is saturated: see [Tuning guidance for chain operators](#tuning-guidance-for-chain-operators) below.

## Block creation and timing

The Sequencer produces blocks from a single loop: attempt to create a block; if a block was produced, wait until `max-block-speed` has elapsed since the attempt started before trying again; if not, retry immediately ([`sequencer.go#L1773-L1781`](https://github.com/OffchainLabs/nitro/blob/a618155919315241665356fe60f3cd00d66d5e46/execution/gethexec/sequencer.go#L1773-L1781)).

`--execution.sequencer.max-block-speed` (default `250ms`) is therefore the **minimum delay between blocks**, which caps block production at four blocks per second under FCFS. It is not a block time:

* **When the queue is empty**, the block creation routine simply blocks waiting for the next transaction to arrive ([`sequencer.go#L1314-L1341`](https://github.com/OffchainLabs/nitro/blob/a618155919315241665356fe60f3cd00d66d5e46/execution/gethexec/sequencer.go#L1314-L1341)). No transactions means no blocks: the Sequencer does not produce empty blocks, and `createBlock` reports that no block was made unless at least one transaction was successfully sequenced.
* **When blocks are heavy**, the actual interval stretches beyond `max-block-speed`, because execution itself takes time and the timer only sets a floor.

In short, **there is no fixed block time on the child chain**. Block timestamps and block numbers advance with demand, which is why time-based logic in contracts should never assume a constant block interval.

Within one block creation pass, the Sequencer pulls the first transaction (waiting for it if necessary), then keeps draining additional queued transactions for a short window (`--execution.sequencer.read-from-tx-queue-timeout`, default `10ms`) before sealing the set into a block. Transactions that don't fit in the block's gas limit are pushed to an internal retry queue and get first priority in the next block.

### Block creation under PGA

PGA replaces that single drain window with fixed ordering rounds. A new round starts every `B/K`, where `B` is the block time and `K` is the number of rounds per block. On Arbitrum One, `B` is 250ms and `K` is 2, so rounds last 125ms. Each round takes in new arrivals for its full window, then promotes the waiting list into the priority queue and transfers that queue into the block, highest priority fee first.

A block closes when it reaches the first of these limits:

* A gas target of 32 Mgas. The hard limit is 64 Mgas, and a single transaction can use at most 32 Mgas.
* A calldata limit of 95,000 bytes.
* The end of its last round.

If a block fills before its last round, the Sequencer finalizes it right away and starts the next block rather than idling for the rest of the window. Under load, the chain then produces blocks faster than its nominal block time: between `1/B` and `K/B`, or 4 to 8 blocks per second on Arbitrum One. That upper cap keeps the spacing between rounds consistent, which the anti-starvation boost depends on.

`K` stays adjustable for two years after PGA activates, to any value from 1 to 10. That gives round lengths from 250ms down to 25ms.

### Execution is strictly sequential

When building a block, transactions are executed **one at a time** through the state transition function ([`block_processor.go#L372-L407`](https://github.com/OffchainLabs/nitro/blob/a618155919315241665356fe60f3cd00d66d5e46/arbos/block_processor.go#L372-L407)), under a lock that ensures only one block is ever being built at once. There is no parallel execution: total throughput is bounded by single-threaded EVM execution speed and the per-block gas limit, not by how fast transactions can be queued.

## What happens during a surge

Suppose 100,000 transactions arrive at effectively the same moment, with default settings:

1. The first `1024` transactions occupy the queue. The rest wait at the queue boundary, each holding its own 12-second deadline, with no ordering guarantee among them.
2. Every `250ms` or more, the Sequencer drains a batch of queued transactions into a block, executing them sequentially. Freed slots are claimed by waiting transactions.
3. Any transaction that cannot make it through the queue and into a block within its 12-second deadline fails with `context deadline exceeded`.

Under PGA, the same backpressure applies, but the priority fee decides who gets through it. Each round promotes whatever is waiting and drains the highest-paying transactions first, so a surge raises the fee needed to land early rather than stretching one arrival queue. The anti-starvation boost still bounds the wait for low-fee transactions, and any transaction that cannot reach a block within its 12-second deadline fails the same way.

The queue is deliberately a short buffer, not a mempool: with default settings it never holds more than about 12 seconds' worth of work. Everything beyond what the chain can execute in that window is shed back to the submitter, who is expected to retry. This keeps latency bounded and predictable for the transactions that do get in, at the cost of pushing burst-absorption out to the edges (RPC clients, or an operator-run relayer, described below).

## From block to the rest of the network

As soon as a block is created, the transaction streamer hands the new message to the broadcaster, which publishes it over WebSocket on the sequencer feed ([`transaction_streamer.go#L1307`](https://github.com/OffchainLabs/nitro/blob/a618155919315241665356fe60f3cd00d66d5e46/arbnode/transaction_streamer.go#L1307)). Full nodes and [feed relays](/run-arbitrum-node/run-feed-relay.md) consume the feed to give sub-second soft confirmations; see [How to read the sequencer feed](/run-arbitrum-node/sequencer/read-sequencer-feed.md).

On Arbitrum One, the [Fast Feed](/how-arbitrum-works/priority-gas-auction/fast-feed.md) publishes each transaction inside its PGA round, before the block closes and before the standard feed carries it. It is a paid, authenticated stream aimed at latency-sensitive readers, and a Nitro node cannot consume it. Soft confirmation still comes from the standard feed.

Independently, the [batch poster](/how-arbitrum-works/deep-dives/batchposter.md) compresses the sequenced transactions and posts them to the sequencer inbox on the parent chain, at which point they inherit the parent chain's finality. Validators then re-execute the posted sequence and assert the resulting state. [The Sequencer and censorship resistance](/how-arbitrum-works/deep-dives/sequencer.md#persistence-and-broadcast) covers the feed, batch posting, and the finality trade-offs in detail, and [Assertions](/how-arbitrum-works/deep-dives/assertions.md) covers validation.

## Tuning guidance for chain operators

For chains with different traffic profiles than Arbitrum One, the queue parameters are the primary tuning surface:

* `--execution.sequencer.queue-size` (default `1024`): raising it lets the Sequencer absorb larger instantaneous bursts without making submitters wait at the queue boundary. The limit to keep in mind: the queue-timeout deadline keeps counting while a transaction sits in the queue, so a queue deeper than what the chain can execute within `queue-timeout` only moves rejections from enqueue time to dequeue time. Size the queue to roughly what your chain can drain in one timeout window.
* `--execution.sequencer.queue-timeout` (default `12s`): raising it lets transactions ride out longer spikes at the cost of slower failure feedback and longer-held connections; lowering it makes overload fail fast. Whatever you choose, communicate it to integration partners so their client timeouts and retry logic stay consistent with it.
* `--execution.sequencer.max-block-speed` (default `250ms`): lowering it raises the block production cap and reduces best-case latency; it does not increase execution throughput, which stays bounded by sequential execution and the per-block gas limit.

If you run PGA, the rounds-per-block count `K` is a fourth tuning surface. Raising `K` shortens each round, which lowers the latency between a transaction arriving and a round promoting it, and raises the ceiling on blocks per second under load. It also makes the anti-starvation boost smaller per round, because the boost is `p / (2K)`. See [PGA for Arbitrum chains](/launch-arbitrum-chain/chain-config/sequencer/pga.md) for the configuration steps.

### Operator-run relayer cache

If your workload has sustained bursts that no reasonable `queue-size`/`queue-timeout` setting absorbs (for example, game events or airdrops that generate far more than one timeout window's worth of transactions at once), the standard pattern is a **relayer cache in front of the Sequencer**: an operator-run service that accepts transactions immediately, holds them durably, and submits them to the Sequencer at the rate the queue drains, retrying on `context deadline exceeded`.

This pattern complements rather than replaces queue tuning: `queue-size` and `queue-timeout` define how much burst the Sequencer itself absorbs, while the relayer holds everything beyond that and controls its own submission order and retry policy. The trade-off is that transactions waiting in the relayer have no onchain ordering guarantee until they actually enter the Sequencer's queue, so the relayer becomes a trusted component of your chain's ingestion path.

## Related resources

* [The Sequencer and censorship resistance](/how-arbitrum-works/deep-dives/sequencer.md): feed, batch posting, finality, and the delayed inbox escape hatch
* [Introduction to PGA](/how-arbitrum-works/priority-gas-auction/pga.md): the two-stage mempool, ordering rounds, and the anti-starvation boost
* [Introduction to the Fast Feed](/how-arbitrum-works/priority-gas-auction/fast-feed.md): the paid per-transaction stream that publishes inside a PGA round
* [Finality](/how-arbitrum-works/deep-dives/finality.md): what soft and hard finality guarantee, and which one to wait for
* [The batch poster](/how-arbitrum-works/deep-dives/batchposter.md): what happens after sequencing, from segment building to the parent chain transaction
* [Transaction lifecycle](/how-arbitrum-works/deep-dives/transaction-lifecycle.md): all submission pathways, including bypassing the Sequencer
* [RPC endpoints and providers](/arbitrum-essentials/reference/node-providers.md): public endpoint behavior, retries, and fallback patterns
* [How to set up a high-availability sequencer](/launch-arbitrum-chain/run-a-node/high-availability-sequencer.md): redundant Sequencers with Redis-based coordination
* [How to run a Sequencer Coordinator Manager](/run-arbitrum-node/sequencer/run-sequencer-coordination-manager.md): managing the sequencer priority list
* [How to read the sequencer feed](/run-arbitrum-node/sequencer/read-sequencer-feed.md) and [How to run a feed relay](/run-arbitrum-node/run-feed-relay.md)
