op-batcher, your chain’s SystemConfig fee
parameters, and, for throttling, the sequencer’s execution client.
Is this guide for you?
Use this guide if:- You operate an OP Stack chain: you control the
op-batcherdeployment and can reach the SystemConfig owner when a parameter change needs it. - Your goal is economic (L1 posting costs look too high, or user fees aren’t covering them) rather than a batcher outage or sync problem.
Before you start
You should already have:- A running
op-batcherposting batches for your chain, and the ability to change its flags or environment variables and restart it. - Access to the batch submitter’s L1 spend history (its address on an L1 explorer) so you can measure the effect of changes.
- Batcher metrics scraped somewhere you can query; see chain monitoring options.
- For Step 5, transaction access as the SystemConfig owner (fee scalars are set on L1).
Step 1: Map where the fees go
Two fee flows matter, and they are tuned by different knobs: the fees you pay L1 to post data (batcher settings), and the fees users pay you (SystemConfig fee parameters). Read Transaction fees and take away:- The three components of a user fee (execution gas fee, L1 data fee, operator fee), and that the L1 data fee is the component that exists to recover your batcher spend.
- Which scalars price the L1 data fee (
basefeeScalarandblobBaseFeeScalar); you will set them in Step 5.
Step 2: Balance your sequencing-window budget
Cost tuning trades posting cost against batch-submission speed, and the sequencing window puts a hard cap on that trade. The faster you submit batches, the sooner your users’ transactions get Ethereum finality guarantees; the longer you accumulate before posting, the cheaper each byte gets, up to the cap. Read the batcher policy section of the batcher configuration guide and take away:- Your chain’s sequencing window, which is 3,600 L1 blocks (12 hours) on standard chains, and that batches must always land on L1 inside it. Breached sequencing windows result in a 12 hour reorg.
- The standard requirement to target batch submission at 1,800 L1 blocks (6 hours) or lower, and why operators leave a congestion buffer below that.
- That a long submission interval stalls the safe head for up to that interval, which delays exchanges and bridges that wait for Ethereum finality.
Step 3: Choose a data availability type
The batcher posts to Ethereum as calldata or as blobs, controlled by--data-availability-type (OP_BATCHER_DATA_AVAILABILITY_TYPE); valid
values are calldata (the flag’s default), blobs, and auto. A blob must
be bought whole, about 130 KB of usable capacity, whether or not you fill
it. In practice that rarely favors calldata: outside short demand spikes,
the blob base fee has sat at or near its floor since blobs launched, so even
a partly filled blob usually costs less than posting the same bytes as
calldata, and L1’s Pectra upgrade (EIP-7623) raised calldata pricing for
data-heavy transactions on top of that.
To check which market is cheaper right now, compare the current blob base
fee with the L1 base fee on any gas tracker that shows both, or let
auto
make the comparison per channel. To execute a switch, follow
Using Blobs, which covers
switching to blobs,
switching back to calldata,
and auto mode;
take away that a DA-type switch also changes the correct scalar values, so
plan Step 5 in the same change window.
Step 4: Size your channels
With the DA type fixed, the biggest cost levers are how long the batcher accumulates data before posting and, on blobs, how many blobs it packs per transaction:--max-channel-duration(OP_BATCHER_MAX_CHANNEL_DURATION), in L1 blocks. The default is0(duration tracking disabled). Read the channel duration recommendation and take away the recommended 1,500-block (5-hour) ceiling, the reasons not to exceed it, and that a full channel is posted early regardless of this setting.--target-num-frames(OP_BATCHER_TARGET_NUM_FRAMES), the number of frames (and so blobs per blob transaction) to target. The default is1. Read the multi-blob recommendation and take away the companion transaction-manager settings a multi-blob configuration needs and the blob-congestion caveat.--batch-type=1(OP_BATCHER_BATCH_TYPE) enables span batches, which cut batch overhead; the default is0(singular). Follow Enable span batches, which includes confirming the Delta upgrade is active first.
To get a concrete blob count, estimate the compressed batch data your
chain produces per channel duration and divide by blob capacity (~130 KB);
Step 7’s utilization metrics will confirm or correct the estimate.
Step 5: Recover the spend with fee scalars
Posting cheaply is half the loop; the L1 data fee your users pay must track what you now actually spend. Follow the L1 fee section of Transaction Fees 101 to read and setbasefeeScalar and blobBaseFeeScalar on your
SystemConfig, and take away the direction of each adjustment. For the
formula the scalars feed and the values OP Mainnet runs, see the
fee parameters reference.
Size the scalars against your measured batcher spend so they recover it
plus your target margin.
If you switched DA type in Step 3, set the new scalars in the same
maintenance window: blob-appropriate scalars under calldata (or the reverse)
misprice every user transaction until corrected.
Step 6: Decide your throttling posture
Throttling is the batcher’s defense against traffic spikes outrunning your DA budget: when its backlog of un-posted data grows past a threshold, it instructs the sequencer’s execution client (via theminer_setMaxDASize
RPC) to limit DA-heavy transactions and blocks. It is on by default, so
this step is about checking the defaults fit your cost posture rather than
turning something on. Read the
batcher sequencer throttling section
and take away:
- The requirement that the sequencer’s execution client exposes the
minerRPC namespace, and the follow-the-sequencer caveat for multi-node setups. - The default backlog thresholds at which throttling engages and reaches
maximum intensity (
--throttle.unsafe-da-bytes-lower/upper-threshold), and that the default controller isquadratic(--throttle.controller-type; the options arestep,linear,quadratic, andpid).
--throttle.unsafe-da-bytes-lower-threshold=0. For controller selection
and tuning depth, use the throttling deep dive in the next steps.
Step 7: Verify the outcome
Give a change at least a few full channel cycles, then check both sides of the loop. The batcher exports Prometheus metrics under theop_batcher_<procname> namespace (op_batcher_default_* unless you set a
custom process name):
- Blob utilization (blobs only): the
blob_used_byteshistogram should sit near blob capacity (~130 KB). Persistently part-empty blobs mean your channel duration or frame count is oversized for your throughput; revisit Step 4. - Posting cadence: batcher transactions from your batch submitter address should appear on L1 at roughly the interval you chose, and never approach the sequencing-window deadline from Step 2.
- Throttling at rest:
throttle_intensityshould be0andunsafe_da_bytesbelow the lower threshold in normal operation. If throttling engages routinely, your chain’s steady-state throughput exceeds your posting budget; revisit Steps 3 and 4 before touching Step 6’s thresholds. - The economic bottom line: over a representative window, compare the batch submitter’s L1 spend against L1-fee revenue arriving in your fee vaults. A healthy result is revenue at or above spend by your target margin; if not, revisit Step 5.
Next steps
- Batcher configuration reference:
the full flag and environment-variable catalogue. Hand-pinned to
op-batcher/v1.10.0as of 2026-07-16; confirm values againstop-batcher --helpfor the release you run. - op-batcher throttling deep dive:
the only in-depth documentation of the four throttling controllers,
their runtime-management RPCs, and the experimental PID controller’s
tuning profiles. In-repo document on
develop, as of 2026-07-16. - op-batcher readme:
batcher architecture and operational detail beyond configuration.
In-repo document on
develop, as of 2026-07-16. - Frame format in the derivation spec: the normative definition of channels and frames, for when you need to reason about what a channel actually contains.