6 minIteradian Engineeringinfrastructureoperations

Migrating off a self-hosted node without a flag day

Moving from your own node to a managed endpoint does not need a cutover window. This is the incremental path: shadow traffic first, move reads, keep a fallback, and the specific things that break if you skip a step.

The reason teams stay on a self-hosted node longer than they want to is rarely the node. It is the migration: a single cutover, at a time of day when someone is awake, with a rollback plan nobody has tested. It does not have to work that way. RPC is stateless from the client side, which makes it one of the easier things to move incrementally.

Step 1: shadow, do not switch

Point a copy of your read traffic at the new endpoint and throw the responses away. Compare them against what your node returned. You are looking for three things: responses that differ, latency that is worse under your real traffic shape rather than a synthetic benchmark, and methods your client uses that you had forgotten about.

That third one is the usual surprise. Most clients call more methods than their authors remember — a library doing a chain ID check on every reconnect, a health probe on an interval, a gas estimator you configured a year ago.

Step 2: move reads, keep writes

Reads are idempotent and safe to move first. Transaction submission is the one place where a duplicate has consequences, so leave it pointed at whatever you trust until the read path has been stable for a week under real load.

Step 3: handle throttling properly before you need to

A self-hosted node does not usually rate-limit you, so client code written against one often has no 429 handling at all. A managed endpoint has ceilings by definition. Write the handler before you move, not after your first incident.

On Iteradian the response is an HTTP 429 with a JSON-RPC 2.0 error body, and the error names which ceiling you hit — code -32005 for the per-second or concurrency ceiling, with data.retryAfter saying how many seconds to wait; -32006 when the monthly request quota is used up, with data.resetsAt saying when it resets; -32007 when the overage spend cap for the billing period is reached. Those are different problems with different fixes: the first is a spike you can smooth by waiting, the other two will not clear by retrying and need the reset date, a higher cap or a bigger plan. Code that treats every 429 the same cannot tell them apart.

typescript
const endpoint = "https://api.iteradian.com/rpc/v1/eth-mainnet";
const key = process.env.ITERADIAN_API_KEY ?? "";

async function call(body: unknown, attempt = 0): Promise<Response> {
  const res = await fetch(endpoint, {
    method: "POST",
    headers: { "X-Api-Key": key, "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });

  if (res.status !== 429 || attempt >= 4) return res;

  // The body names which ceiling was hit. Only -32005 clears by waiting;
  // the monthly quota (-32006) and the overage cap (-32007) will not.
  const { error } = await res.clone().json();
  if (error?.code !== -32005) throw new Error(`limit reached: ${error?.message}`);

  const wait = Number(error.data?.retryAfter) * 1000 || 2 ** attempt * 250;
  await new Promise((r) => setTimeout(r, wait));
  return call(body, attempt + 1);
}

Step 4: keep the node until you have a week of data

Decommissioning is the last step, not the first. Keep the node running and reachable as a fallback until the per-request analytics show a week of real traffic with no error class you cannot explain. The cost of a week of overlap is smaller than the cost of discovering a gap after the node is gone.

What does not migrate cleanly

  • Deep archive queries that predate the pruning window of the managed pool.
  • Custom runtime builds or nodes running a patched client.
  • Anything relying on node-local state — a mempool view specific to your peer set, for example.
  • Workloads shaped like a bulk export rather than an API client.

If your workload is on that list, a hybrid is the honest answer: managed endpoints for the API-shaped traffic, your own node for the part that genuinely needs one.

Frequently asked questions

01

Do I need a cutover window to move to a managed RPC endpoint?

No. RPC is stateless from the client side, so you can shadow read traffic against the new endpoint, move reads once the responses match, and keep transaction submission on the old path until the read path has been stable under real load.

02

What should my client do when it gets an HTTP 429?

Read the JSON-RPC error body rather than treating every 429 the same. A rate-ceiling error (-32005) clears after the data.retryAfter delay or with exponential backoff; the monthly quota (-32006) and the overage spend cap (-32007) do not clear by retrying and need the reset date, a higher cap or a plan change.

03

When should I keep running my own node?

For deep archive queries that predate the managed pool pruning window, custom runtime builds, anything depending on node-local state such as your own mempool view, and bulk-export shaped workloads.

Keep reading

Start

Point one endpoint at Iteradian and watch where the traffic goes.

Create a key, send a JSON-RPC call, and read that request back in your analytics a second later. No migration, no contract.

Trial
7 days, no card required
Setup
One endpoint, one header
Commitment
Monthly or annual, cancel any time