From d4c9d186eefd57cb1c0e3a6749bc8da7f3026d56 Mon Sep 17 00:00:00 2001 From: Jason-Wanxt Date: Tue, 30 Jun 2026 04:43:41 +0800 Subject: [PATCH 1/8] add add-helm-fullnode-guide page --- docs/run-arbitrum-node/02-run-full-node.mdx | 6 + .../run-full-node-with-helm.mdx | 280 ++++++++++++++++++ sidebars.js | 5 + 3 files changed, 291 insertions(+) create mode 100644 docs/run-arbitrum-node/run-full-node-with-helm.mdx diff --git a/docs/run-arbitrum-node/02-run-full-node.mdx b/docs/run-arbitrum-node/02-run-full-node.mdx index 7c67978c62..b372184ef8 100644 --- a/docs/run-arbitrum-node/02-run-full-node.mdx +++ b/docs/run-arbitrum-node/02-run-full-node.mdx @@ -18,6 +18,12 @@ To view the short and long term support policy, visit the [Nitro support policy] ::: + + +This page covers running a node with Docker. If you want to deploy on Kubernetes, or you want a more production-ready setup with health and log monitoring, network egress guidance, and day-2 operations, follow [How to run a full node with Helm on Kubernetes](/run-arbitrum-node/run-full-node-with-helm.mdx) instead. + + + ## Putting it into practice: run a node :::warning Caution diff --git a/docs/run-arbitrum-node/run-full-node-with-helm.mdx b/docs/run-arbitrum-node/run-full-node-with-helm.mdx new file mode 100644 index 0000000000..7b98e7cd7d --- /dev/null +++ b/docs/run-arbitrum-node/run-full-node-with-helm.mdx @@ -0,0 +1,280 @@ +--- +title: 'How to run a full node with Helm on Kubernetes' +sidebar_label: 'Run a full node (Helm)' +description: 'Deploy and operate an Arbitrum chain full node on Kubernetes with the community Helm chart, including health and log monitoring, network egress, and day-2 operations.' +user_story: 'As a node operator, I want to deploy and reliably operate an Arbitrum chain full node on Kubernetes using the community Helm chart.' +content_type: how-to +author: 'Jason-W123' +sme: 'Jason-W123' +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +This guide shows how to deploy and operate a full node for an Arbitrum chain on Kubernetes using the [community Helm chart](https://github.com/OffchainLabs/community-helm-charts/tree/main/charts/nitro). It applies to any Arbitrum chain — [Arbitrum One](https://arbitrum.io), Nova, Sepolia, and your own Arbitrum chain — and covers installation, memory configuration, first sync from a snapshot, monitoring, the log signals that distinguish a healthy node from a broken one, the outbound endpoints to allow through a firewall, and day-2 operations. + +The chart's defaults target Arbitrum One, so Arbitrum One is used as the worked example throughout. Each step notes what changes for other chains. + + + +If you want to run a node with Docker instead of Kubernetes, see [How to run a full node for an Arbitrum chain](/run-arbitrum-node/run-full-node.mdx). This page assumes you've reviewed the [Start here](/run-arbitrum-node/start-here.mdx) page, which explains the RPC endpoints, Nitro version, and database snapshots required to run a node. + + + +## Prerequisites + +- **Parent chain access:** an execution RPC endpoint for the chain's parent. For Arbitrum One, Nova, and Sepolia the parent is Ethereum, so you also need an **L1 beacon/blob** endpoint (see [L1 Ethereum RPC providers](/run-arbitrum-node/l1-ethereum-beacon-chain-rpc-providers.mdx)). For an Arbitrum chain whose parent is another Arbitrum chain, supply that parent chain's RPC; a beacon endpoint is only required when the parent posts blobs to Ethereum. Keep the parent-chain client current — incompatible versions can crash or stall the node. +- **Cluster:** Kubernetes with Helm installed. +- **Disk:** raise the chart's default `persistence.size` (`500Gi`) as needed for your chain's database, and allow roughly 2× the snapshot size of temporary disk for extraction on the first run. + +## Step 1: Deploy with Helm + +First add the chart repo: + +```shell +helm repo add offchainlabs https://charts.arbitrum.io +helm repo update +``` + +Then install, selecting the configuration for your chain: + + + + +The chart defaults to Arbitrum One (`chain.id` `42161`, `parent-chain.id` `1`), so an Arbitrum One node needs only three values: + +```shell +# Arbitrum One +helm install arb1-fullnode offchainlabs/nitro \ + --set configmap.data.parent-chain.connection.url= \ + --set configmap.data.parent-chain.blob-client.beacon-url= \ + --set configmap.data.init.latest=pruned # pruned snapshot, recommended for Arbitrum One +``` + +For Nova or Sepolia, override the chain and parent-chain IDs. For example, Arbitrum Sepolia: + +```shell +# Arbitrum Sepolia (parent chain is Ethereum Sepolia, 11155111) +helm install arbsepolia-fullnode offchainlabs/nitro \ + --set configmap.data.parent-chain.id=11155111 \ + --set configmap.data.parent-chain.connection.url= \ + --set configmap.data.parent-chain.blob-client.beacon-url= \ + --set configmap.data.chain.id=421614 +``` + + + + +A node for your own Arbitrum chain needs its chain info, sequencer endpoint, and feed URL. Because `chain.info-json` is a large JSON string, supply these through a values file rather than `--set`: + +```yaml +# values-mychain.yaml +configmap: + data: + parent-chain: + id: + connection: + url: + chain: + id: + name: + info-json: '' + execution: + forwarding-target: + node: + feed: + input: + url: +``` + +```shell +helm install mychain-fullnode offchainlabs/nitro -f values-mychain.yaml +``` + +If the parent chain is Ethereum, also set `configmap.data.parent-chain.blob-client.beacon-url`. AnyTrust chains additionally need a Data Availability configuration (`node.data-availability.*` and a `rest-aggregator`); see [Data Availability](/run-arbitrum-node/data-availability.mdx). + + + + +### Why only three flags for Arbitrum One? + +The rest of the node configuration is baked into the chart's defaults, which target Arbitrum One. Only three values must be supplied for Arbitrum One; other chains additionally override `chain.id` / `parent-chain.id` (DAO-governed networks) or supply `chain.info-json` (your Arbitrum chain), as shown above. + +| Flag | Chart default | Status | +| ------------------------------------- | ------------------------------------ | ----------------------------------- | +| `parent-chain.connection.url` | empty | **Must set** (1 of 3) | +| `parent-chain.blob-client.beacon-url` | empty | **Must set** (1 of 3) | +| `init.latest=pruned` / `init.url` | empty | **First sync** (1 of 3) | +| `chain.id` | `42161` (Arbitrum One) | Pre-set — override for other chains | +| `parent-chain.id` | `1` (Ethereum) | Pre-set — override for other chains | +| `http.api` | `[arb,eth,net,web3,txpool,arbdebug]` | Pre-set | +| `http.corsdomain` | `"*"` | Pre-set | +| `http.addr` | `0.0.0.0` | Pre-set | +| `http.vhosts` | `"*"` | Pre-set | + +The chart also pre-sets flags the Docker example doesn't even show: the WebSocket settings (`ws.*` on port `8548`), `http.port=8547`, `log-type=json`, the allowed WASM module roots, `persistent.chain`, and the config env prefix. + + + +Otherwise "three flags" is misleading: + +- **Don't copy the chart README's init example verbatim.** It uses `nitro-genesis.tar`, which syncs from genesis — huge and slow on a chain with a long history such as Arbitrum One. Where a pruned snapshot exists, prefer `configmap.data.init.latest=pruned` (default base `https://snapshot.arbitrum.foundation/`). +- **The chart changes the RPC path prefix.** Defaults are `http.rpcprefix=/rpc` and `ws.rpcprefix=/ws`, so RPC is served at `http://host:8547/rpc`, not vanilla Nitro's `/`. Clients that omit `/rpc` get a 404. +- **Metrics are off by default.** Enable `configmap.data.metrics=true` and `serviceMonitor.enabled=true` to scrape them (see [Step 5](#step-5-enable-monitoring)). + + + +## Step 2: Configure memory management + +For a containerized node, set a memory limit and tune the allocator. This applies to every chain: + +- **`resources.limits.memory`** — when set, the chart automatically derives `GOMEMLIMIT` (default 90% of the limit). The auto-`GOMEMLIMIT` behavior is on by default but only takes effect once a memory limit is present. +- **`MALLOC_ARENA_MAX=2`** — not set by the chart. Add it through `extraEnv`, which is a **list** spliced directly into the pod's `env`: + + ```yaml + extraEnv: + - name: MALLOC_ARENA_MAX + value: '2' + ``` + +- **`node.resource-mgmt.mem-free-limit`** (optional) — an RPC memory throttle that's disabled by default. Recommended if you expose this node as a public RPC. + +For the allocator details, the `GOMEMLIMIT` formula, and `MALLOC_ARENA_MAX`, see the [memory management deep-dive](/run-arbitrum-node/nitro/nitro-memory-management.mdx) and the [memory management section](/run-arbitrum-node/run-full-node#memory-management) of the Docker full-node guide. + +## Step 3: First sync from a snapshot + +Whether a snapshot is required depends on the chain: + +- **Arbitrum One** requires a snapshot on the first run because of its Classic-era history. Use `configmap.data.init.latest=pruned`. +- **Nova and Sepolia** have published snapshots that speed up the initial sync but aren't strictly required. +- **Your Arbitrum chain** typically syncs from genesis with no snapshot, unless you provide one via `init.url`. + +When using a snapshot, the default base is `https://snapshot.arbitrum.foundation/`. Initial sync takes a while. The init flag is ignored once a database already exists, so it's safe to leave it in place across restarts. Confirm the current snapshot type and download details in the [Nitro database snapshots](/run-arbitrum-node/nitro/nitro-database-snapshots.mdx) guide. + +## Step 4: Verify the node is live + +Once the snapshot is extracted and the node starts following the chain: + +- The RPC endpoint answers at `http://:8547/rpc` (remember the `/rpc` prefix from Step 1). +- `eth_syncing` trends toward `false`. When `false`, the node considers itself synced; when behind, it returns an object describing how far behind it is. +- The logs show `created block` advancing (see [Log signals](#log-signals-to-watch)). + + + +`eth_syncing=false` only means the node _thinks_ it's synced. For a more robust health check that also compares the local head against a trusted reference RPC and detects a stalled head, use the sync-check tooling in the [community-helm-charts repository](https://github.com/OffchainLabs/community-helm-charts). + + + +## Step 5: Enable monitoring + +Turn on metrics and a `ServiceMonitor` so Prometheus can scrape the node: + +```shell +helm upgrade offchainlabs/nitro \ + --reuse-values \ + --set configmap.data.metrics=true \ + --set serviceMonitor.enabled=true +``` + +### Grafana dashboard + +The chart README points to the repository's [Releases page](https://github.com/OffchainLabs/community-helm-charts/releases) for the Grafana dashboard. If a release doesn't attach the dashboard JSON, pull the last published copy from the repository's git history and import it: + +```shell +git clone https://github.com/OffchainLabs/community-helm-charts +git -C community-helm-charts show 430a2cc~1:operations/grafana/dashboards/overview.json > overview.json +``` + +Then in Grafana, go to **Dashboards → Import** and paste `overview.json`. + + + +This exported dashboard has no `__inputs` datasource prompt, and its `Source` variable is hard-pinned to an internal Mimir UID. After importing, you **must** repoint the `Source` variable to your own Prometheus, or every panel reads "No data." Then set `nitronodejob` to this node's scrape job, and leave `sequencerjob`, `validatorjob`, and `relayjob` empty so only the full-node rows render. + + + +### Probes + +The chart wires a built-in **startup probe** by default, but **liveness and readiness probes are off** unless you set `livenessProbe` and `readinessProbe`. The startup probe's long failure window protects the initial sync (so a slow sync won't trigger a restart), but it doesn't catch a hang after the node is up. **Configure a liveness probe yourself** for ongoing hang detection. + +## Log signals to watch + +The chart sets `log-type=json`. The following INFO/WARN/ERROR strings distinguish a healthy node from a broken one on any Arbitrum chain (the sequencer hostnames in the examples below are Arbitrum One's; your chain's will differ): + +| Event | Healthy signal | Broken / warning signal | +| ---------------------------------- | ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| **New block created** | `INFO created block` with `l2Block` / `l2BlockHash`, advancing continuously while producing | No advancing `created block` line → block production stalled | +| **User tx forwarded to sequencer** | Success is not logged | `WARN error forwarding transaction trying different target`; `ERROR Failed to publish transaction to any of the forwarding targets` | +| **Sequencer feed message** | `INFO Feed connected`; `DEBUG received batch item` | `WARN failed connect to sequencer broadcast, waiting and retrying`; `ERROR Server connection timed out without receiving data` | +| **Inbox messages (parent chain)** | `INFO InboxTracker` with `sequencerBatchCount` / `messageCount` / `l1Block` advancing | `WARN error reading inbox`. Note: `backwards reorg of delayed messages` is logged at INFO level — it's normal on parent-chain reorgs, not an alert. | + +### Why a full node can't forward transactions to the sequencer + +A full node forwards user transactions to the sequencer. When forwarding fails, the cause falls into one of three buckets. Forwarding failures are **log-based, not metric-based** — the forwarder emits no metrics — so alert by grepping the log strings below. + +| Cause | Signal | +| ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| **All forwarding targets unreachable** (egress blocked, or sequencer + all fallbacks down) | `ERROR Failed to publish transaction to any of the forwarding targets`, preceded by per-target `WARN` lines | +| **Sequencer returns a business error** (non-connection, e.g. `nonce too low`) | `WARN error forwarding transaction trying different target` with the sequencer's `err` — not escalated; the error is returned to the caller | +| **Memory 429 throttling** (rejected before forwarding) | The client receives `HTTP 429 Too many requests` and the metric `arb/rpc/limitcheck/failure` increments. There is **no per-request log line**. | + +Representative log lines (hostnames shown are Arbitrum One's): + +```text +# (a) ALL TARGETS UNREACHABLE — egress blocked, or sequencer + all fallbacks down +WARN error forwarding transaction trying different target current target=https://arb1-sequencer.arbitrum.io/rpc err="dial tcp: lookup ...: no such host" +WARN error forwarding transaction to a backup target target=https://arb1-sequencer-fallback-1.arbitrum.io/rpc pos=1 total targets=6 err="timeout exceeded" +ERROR Failed to publish transaction to any of the forwarding targets numTargets=6 + +# (b) SEQUENCER RETURNS A BUSINESS ERROR (non-connection) — logged once, NOT escalated, returned to caller +WARN error forwarding transaction trying different target current target=https://arb1-sequencer.arbitrum.io/rpc err="nonce too low: address 0x..., tx: 5 state: 7" +# (no "Failed to publish..." line follows — the error is handed straight back to the RPC client) + +# (c) MEMORY 429 THROTTLING — rejected BEFORE it reaches the forwarder; no per-request log line. +# The signals are an HTTP 429 to the client and an increment of arb/rpc/limitcheck/failure. +INFO Cgroups v2 detected, enabling memory limit RPC throttling # startup, confirms throttling is active +ERROR Error checking memory limit err=... checker=CgroupsMemoryLimitChecker # only if the cgroup read fails +``` + +## Network egress allowlist + +A full node reaches out to a fixed set of endpoints. The specific hostnames depend on the chain: for DAO-governed networks they come from the chain's built-in chain info, and for your own Arbitrum chain they come from the `chain.info-json`, `execution.forwarding-target`, and feed URLs you configure. The categories are the same across chains: + +| Purpose | Where the endpoint comes from | When | +| ----------------------------- | ------------------------------------------------------------------- | ------------------------------------------- | +| **Sequencer** (tx forwarding) | chain info `sequencer-url` / `execution.forwarding-target` | Always — the node forwards user txs here | +| Sequencer fallbacks | chain info `secondary-forwarding-target` | Failover | +| **Sequencer feed** | chain info `feed-url` / `node.feed.input.url` | Always | +| Feed fallbacks / delayed | chain info `secondary-feed-url` / `node.feed.input.secondary-url` | Failover | +| Block metadata | chain info `block-metadata-url` | Only if tracking block metadata / Timeboost | +| DB snapshot | the `init` source host | Initial sync only | +| **Parent chain** | your parent-chain RPC + beacon (beacon only for an Ethereum parent) | Always | +| DA / REST endpoints | `rest-aggregator` URL list | AnyTrust chains only (e.g. Nova) | + +For **Arbitrum One**, those resolve to: + +| Purpose | Endpoint(s) | +| ------------------------- | ---------------------------------------------------------------------------------------------- | +| Sequencer (tx forwarding) | `https://arb1-sequencer.arbitrum.io/rpc` | +| Sequencer fallbacks | `https://arb1-sequencer-fallback-{1..5}.arbitrum.io/rpc` | +| Sequencer feed (primary) | `wss://arb1-feed.arbitrum.io/feed` | +| Feed fallbacks / delayed | `wss://arb1-delayed-feed.arbitrum.io/feed`, `wss://arb1-feed-fallback-{1..5}.arbitrum.io/feed` | +| Block metadata | `https://arb1.arbitrum.io/rpc` | +| DB snapshot | `https://snapshot.arbitrum.foundation/` | +| Parent chain | Your Ethereum L1 execution RPC + L1 beacon/blob endpoint | + + + +For a default Arbitrum One full node, allow `*.arbitrum.io` (443 HTTPS + WSS), `snapshot.arbitrum.foundation` (443, init only), and your own L1 RPC + beacon hosts. For other chains, allow that chain's sequencer, feed, and snapshot hosts plus your parent-chain endpoints. **Additional outbound paths exist only if you enable them:** the version alerter (off by default, queries an operator-set endpoint), the classic redirect, or DA/REST endpoints (AnyTrust chains such as Nova). + + + +## Stay current with releases + +Track new node versions on the [Nitro releases page](https://github.com/OffchainLabs/nitro/releases). Upgrade with tagged **release** versions only — beta and RC builds risk database corruption (see [Day-2 operations](#day-2-operations)). Read each release's notes for required ArbOS bumps or config changes before upgrading a mainnet full node, and review the [Nitro support policy](/run-arbitrum-node/nitro-support-policy.mdx). + +## Day-2 operations + +- **Upgrades:** run `helm upgrade` using **release versions only** (no beta/RC — risk of database corruption). +- **Graceful shutdown:** the chart sets `terminationGracePeriodSeconds=600` so state flushes cleanly on shutdown. Raise it for a large database. +- **Disk growth:** prune HashDB nodes with `--init.prune full` if disk usage grows. diff --git a/sidebars.js b/sidebars.js index 52c67e1afb..726ccc625b 100644 --- a/sidebars.js +++ b/sidebars.js @@ -634,6 +634,11 @@ const sidebars = { id: 'run-arbitrum-node/run-full-node', label: 'Run a full node', }, + { + type: 'doc', + id: 'run-arbitrum-node/run-full-node-with-helm', + label: 'Run a full node (Helm)', + }, { type: 'doc', id: 'run-arbitrum-node/run-local-full-chain-simulation', From 4181a0082ffde924ea4b96ecff9e34f967eb8ddf Mon Sep 17 00:00:00 2001 From: Jason-Wanxt Date: Tue, 30 Jun 2026 05:16:54 +0800 Subject: [PATCH 2/8] Refine Helm full-node guide: trim sections, generalize parent chain, add metrics endpoint Co-Authored-By: Claude Opus 4.8 --- docs/run-arbitrum-node/02-run-full-node.mdx | 2 +- .../run-full-node-with-helm.mdx | 66 ++++--------------- 2 files changed, 13 insertions(+), 55 deletions(-) diff --git a/docs/run-arbitrum-node/02-run-full-node.mdx b/docs/run-arbitrum-node/02-run-full-node.mdx index b372184ef8..c522376942 100644 --- a/docs/run-arbitrum-node/02-run-full-node.mdx +++ b/docs/run-arbitrum-node/02-run-full-node.mdx @@ -20,7 +20,7 @@ To view the short and long term support policy, visit the [Nitro support policy] -This page covers running a node with Docker. If you want to deploy on Kubernetes, or you want a more production-ready setup with health and log monitoring, network egress guidance, and day-2 operations, follow [How to run a full node with Helm on Kubernetes](/run-arbitrum-node/run-full-node-with-helm.mdx) instead. +This page covers running a node with Docker. If you want to deploy on Kubernetes, or you want a more production-ready setup with monitoring, log signals, and network egress guidance, follow [How to run a full node with Helm on Kubernetes](/run-arbitrum-node/run-full-node-with-helm.mdx) instead. diff --git a/docs/run-arbitrum-node/run-full-node-with-helm.mdx b/docs/run-arbitrum-node/run-full-node-with-helm.mdx index 7b98e7cd7d..d6239732ac 100644 --- a/docs/run-arbitrum-node/run-full-node-with-helm.mdx +++ b/docs/run-arbitrum-node/run-full-node-with-helm.mdx @@ -1,7 +1,7 @@ --- title: 'How to run a full node with Helm on Kubernetes' sidebar_label: 'Run a full node (Helm)' -description: 'Deploy and operate an Arbitrum chain full node on Kubernetes with the community Helm chart, including health and log monitoring, network egress, and day-2 operations.' +description: 'Deploy an Arbitrum chain full node on Kubernetes with the community Helm chart, including memory configuration, monitoring, log signals, and network egress.' user_story: 'As a node operator, I want to deploy and reliably operate an Arbitrum chain full node on Kubernetes using the community Helm chart.' content_type: how-to author: 'Jason-W123' @@ -11,7 +11,7 @@ sme: 'Jason-W123' import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -This guide shows how to deploy and operate a full node for an Arbitrum chain on Kubernetes using the [community Helm chart](https://github.com/OffchainLabs/community-helm-charts/tree/main/charts/nitro). It applies to any Arbitrum chain — [Arbitrum One](https://arbitrum.io), Nova, Sepolia, and your own Arbitrum chain — and covers installation, memory configuration, first sync from a snapshot, monitoring, the log signals that distinguish a healthy node from a broken one, the outbound endpoints to allow through a firewall, and day-2 operations. +This guide shows how to deploy a full node for an Arbitrum chain on Kubernetes using the [community Helm chart](https://github.com/OffchainLabs/community-helm-charts/tree/main/charts/nitro). It applies to any Arbitrum chain — [Arbitrum One](https://arbitrum.io), Nova, Sepolia, and your own Arbitrum chain — and covers installation, memory configuration, first sync from a snapshot, monitoring, the log signals that distinguish a healthy node from a broken one, and the outbound endpoints to allow through a firewall. The chart's defaults target Arbitrum One, so Arbitrum One is used as the worked example throughout. Each step notes what changes for other chains. @@ -23,9 +23,9 @@ If you want to run a node with Docker instead of Kubernetes, see [How to run a f ## Prerequisites -- **Parent chain access:** an execution RPC endpoint for the chain's parent. For Arbitrum One, Nova, and Sepolia the parent is Ethereum, so you also need an **L1 beacon/blob** endpoint (see [L1 Ethereum RPC providers](/run-arbitrum-node/l1-ethereum-beacon-chain-rpc-providers.mdx)). For an Arbitrum chain whose parent is another Arbitrum chain, supply that parent chain's RPC; a beacon endpoint is only required when the parent posts blobs to Ethereum. Keep the parent-chain client current — incompatible versions can crash or stall the node. +- **Parent chain access:** an execution RPC endpoint for the chain's parent. For Arbitrum One, Nova, and Sepolia the parent is Ethereum, so you also need an **L1 beacon/blob** endpoint (see [L1 Ethereum RPC providers](/run-arbitrum-node/l1-ethereum-beacon-chain-rpc-providers.mdx)). For an Arbitrum chain whose parent is another chain, supply that parent chain's RPC; a beacon endpoint is only required when the chain posts blobs to its parent chain. - **Cluster:** Kubernetes with Helm installed. -- **Disk:** raise the chart's default `persistence.size` (`500Gi`) as needed for your chain's database, and allow roughly 2× the snapshot size of temporary disk for extraction on the first run. +- **Disk:** size storage for the chain you're running. A node's database size varies widely from chain to chain and grows over time, so check the expected size with your chain operator before provisioning (for Arbitrum One it runs to multiple TB). Raise the chart's default `persistence.size` (`500Gi`) accordingly, and allow roughly 2× the snapshot size of additional temporary disk for extraction on the first run. ## Step 1: Deploy with Helm @@ -96,37 +96,17 @@ If the parent chain is Ethereum, also set `configmap.data.parent-chain.blob-clie -### Why only three flags for Arbitrum One? - -The rest of the node configuration is baked into the chart's defaults, which target Arbitrum One. Only three values must be supplied for Arbitrum One; other chains additionally override `chain.id` / `parent-chain.id` (DAO-governed networks) or supply `chain.info-json` (your Arbitrum chain), as shown above. - -| Flag | Chart default | Status | -| ------------------------------------- | ------------------------------------ | ----------------------------------- | -| `parent-chain.connection.url` | empty | **Must set** (1 of 3) | -| `parent-chain.blob-client.beacon-url` | empty | **Must set** (1 of 3) | -| `init.latest=pruned` / `init.url` | empty | **First sync** (1 of 3) | -| `chain.id` | `42161` (Arbitrum One) | Pre-set — override for other chains | -| `parent-chain.id` | `1` (Ethereum) | Pre-set — override for other chains | -| `http.api` | `[arb,eth,net,web3,txpool,arbdebug]` | Pre-set | -| `http.corsdomain` | `"*"` | Pre-set | -| `http.addr` | `0.0.0.0` | Pre-set | -| `http.vhosts` | `"*"` | Pre-set | - -The chart also pre-sets flags the Docker example doesn't even show: the WebSocket settings (`ws.*` on port `8548`), `http.port=8547`, `log-type=json`, the allowed WASM module roots, `persistent.chain`, and the config env prefix. - - - -Otherwise "three flags" is misleading: + - **Don't copy the chart README's init example verbatim.** It uses `nitro-genesis.tar`, which syncs from genesis — huge and slow on a chain with a long history such as Arbitrum One. Where a pruned snapshot exists, prefer `configmap.data.init.latest=pruned` (default base `https://snapshot.arbitrum.foundation/`). - **The chart changes the RPC path prefix.** Defaults are `http.rpcprefix=/rpc` and `ws.rpcprefix=/ws`, so RPC is served at `http://host:8547/rpc`, not vanilla Nitro's `/`. Clients that omit `/rpc` get a 404. -- **Metrics are off by default.** Enable `configmap.data.metrics=true` and `serviceMonitor.enabled=true` to scrape them (see [Step 5](#step-5-enable-monitoring)). +- **Metrics are off by default.** Enable `configmap.data.metrics=true` and `serviceMonitor.enabled=true` to scrape them (see [Step 4](#step-4-enable-monitoring)). -## Step 2: Configure memory management +## Step 2: Configure memory management (optional) -For a containerized node, set a memory limit and tune the allocator. This applies to every chain: +For a containerized node, you can set a memory limit and tune the allocator. This is optional but recommended, and applies to every chain: - **`resources.limits.memory`** — when set, the chart automatically derives `GOMEMLIMIT` (default 90% of the limit). The auto-`GOMEMLIMIT` behavior is on by default but only takes effect once a memory limit is present. - **`MALLOC_ARENA_MAX=2`** — not set by the chart. Add it through `extraEnv`, which is a **list** spliced directly into the pod's `env`: @@ -151,21 +131,7 @@ Whether a snapshot is required depends on the chain: When using a snapshot, the default base is `https://snapshot.arbitrum.foundation/`. Initial sync takes a while. The init flag is ignored once a database already exists, so it's safe to leave it in place across restarts. Confirm the current snapshot type and download details in the [Nitro database snapshots](/run-arbitrum-node/nitro/nitro-database-snapshots.mdx) guide. -## Step 4: Verify the node is live - -Once the snapshot is extracted and the node starts following the chain: - -- The RPC endpoint answers at `http://:8547/rpc` (remember the `/rpc` prefix from Step 1). -- `eth_syncing` trends toward `false`. When `false`, the node considers itself synced; when behind, it returns an object describing how far behind it is. -- The logs show `created block` advancing (see [Log signals](#log-signals-to-watch)). - - - -`eth_syncing=false` only means the node _thinks_ it's synced. For a more robust health check that also compares the local head against a trusted reference RPC and detects a stalled head, use the sync-check tooling in the [community-helm-charts repository](https://github.com/OffchainLabs/community-helm-charts). - - - -## Step 5: Enable monitoring +## Step 4: Enable monitoring Turn on metrics and a `ServiceMonitor` so Prometheus can scrape the node: @@ -176,6 +142,8 @@ helm upgrade offchainlabs/nitro \ --set serviceMonitor.enabled=true ``` +Once enabled, the node exposes Prometheus metrics at `http://:6070/debug/metrics/prometheus` — the metrics server binds to `0.0.0.0:6070` by default (`configmap.data.metrics-server.port`), and the `ServiceMonitor` scrapes that port and path (`serviceMonitor.path` defaults to `/debug/metrics/prometheus`). + ### Grafana dashboard The chart README points to the repository's [Releases page](https://github.com/OffchainLabs/community-helm-charts/releases) for the Grafana dashboard. If a release doesn't attach the dashboard JSON, pull the last published copy from the repository's git history and import it: @@ -238,7 +206,7 @@ ERROR Error checking memory limit err=... checker=CgroupsMemoryLimitChecker ## Network egress allowlist -A full node reaches out to a fixed set of endpoints. The specific hostnames depend on the chain: for DAO-governed networks they come from the chain's built-in chain info, and for your own Arbitrum chain they come from the `chain.info-json`, `execution.forwarding-target`, and feed URLs you configure. The categories are the same across chains: +A full node reaches out to a fixed set of endpoints. If you run on a cloud provider, outbound traffic is often restricted by default, so you'll likely need to allow these destinations explicitly in your cloud security group or firewall rules (for example, AWS security groups, GCP firewall rules, or an egress `NetworkPolicy`). The specific hostnames depend on the chain: for DAO-governed networks they come from the chain's built-in chain info, and for your own Arbitrum chain they come from the `chain.info-json`, `execution.forwarding-target`, and feed URLs you configure. The categories are the same across chains: | Purpose | Where the endpoint comes from | When | | ----------------------------- | ------------------------------------------------------------------- | ------------------------------------------- | @@ -268,13 +236,3 @@ For **Arbitrum One**, those resolve to: For a default Arbitrum One full node, allow `*.arbitrum.io` (443 HTTPS + WSS), `snapshot.arbitrum.foundation` (443, init only), and your own L1 RPC + beacon hosts. For other chains, allow that chain's sequencer, feed, and snapshot hosts plus your parent-chain endpoints. **Additional outbound paths exist only if you enable them:** the version alerter (off by default, queries an operator-set endpoint), the classic redirect, or DA/REST endpoints (AnyTrust chains such as Nova). - -## Stay current with releases - -Track new node versions on the [Nitro releases page](https://github.com/OffchainLabs/nitro/releases). Upgrade with tagged **release** versions only — beta and RC builds risk database corruption (see [Day-2 operations](#day-2-operations)). Read each release's notes for required ArbOS bumps or config changes before upgrading a mainnet full node, and review the [Nitro support policy](/run-arbitrum-node/nitro-support-policy.mdx). - -## Day-2 operations - -- **Upgrades:** run `helm upgrade` using **release versions only** (no beta/RC — risk of database corruption). -- **Graceful shutdown:** the chart sets `terminationGracePeriodSeconds=600` so state flushes cleanly on shutdown. Raise it for a large database. -- **Disk growth:** prune HashDB nodes with `--init.prune full` if disk usage grows. From f287a114da1f204a282f6b05abcf110cce956efa Mon Sep 17 00:00:00 2001 From: Jason-Wanxt Date: Thu, 2 Jul 2026 03:57:11 +0800 Subject: [PATCH 3/8] fix memory-management --- .../run-full-node-with-helm.mdx | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/docs/run-arbitrum-node/run-full-node-with-helm.mdx b/docs/run-arbitrum-node/run-full-node-with-helm.mdx index d6239732ac..993f315523 100644 --- a/docs/run-arbitrum-node/run-full-node-with-helm.mdx +++ b/docs/run-arbitrum-node/run-full-node-with-helm.mdx @@ -17,13 +17,13 @@ The chart's defaults target Arbitrum One, so Arbitrum One is used as the worked -If you want to run a node with Docker instead of Kubernetes, see [How to run a full node for an Arbitrum chain](/run-arbitrum-node/run-full-node.mdx). This page assumes you've reviewed the [Start here](/run-arbitrum-node/start-here.mdx) page, which explains the RPC endpoints, Nitro version, and database snapshots required to run a node. +If you want to run a node with Docker instead of Kubernetes, see [How to run a full node for an Arbitrum chain](/run-arbitrum-node/02-run-full-node.mdx). This page assumes you've reviewed the [Start here](/run-arbitrum-node/start-here.mdx) page, which explains the RPC endpoints, Nitro version, and database snapshots required to run a node. ## Prerequisites -- **Parent chain access:** an execution RPC endpoint for the chain's parent. For Arbitrum One, Nova, and Sepolia the parent is Ethereum, so you also need an **L1 beacon/blob** endpoint (see [L1 Ethereum RPC providers](/run-arbitrum-node/l1-ethereum-beacon-chain-rpc-providers.mdx)). For an Arbitrum chain whose parent is another chain, supply that parent chain's RPC; a beacon endpoint is only required when the chain posts blobs to its parent chain. +- **Parent chain access:** an execution RPC endpoint for the chain's parent. For Arbitrum One, Nova, and Sepolia the parent is Ethereum, so you also need an **L1 beacon/blob** endpoint (see [L1 Ethereum RPC providers](/run-arbitrum-node/04-l1-ethereum-beacon-chain-rpc-providers.mdx)). For an Arbitrum chain whose parent is another chain, supply that parent chain's RPC; a beacon endpoint is only required when the chain posts blobs to its parent chain. - **Cluster:** Kubernetes with Helm installed. - **Disk:** size storage for the chain you're running. A node's database size varies widely from chain to chain and grows over time, so check the expected size with your chain operator before provisioning (for Arbitrum One it runs to multiple TB). Raise the chart's default `persistence.size` (`500Gi`) accordingly, and allow roughly 2× the snapshot size of additional temporary disk for extraction on the first run. @@ -106,20 +106,21 @@ If the parent chain is Ethereum, also set `configmap.data.parent-chain.blob-clie ## Step 2: Configure memory management (optional) -For a containerized node, you can set a memory limit and tune the allocator. This is optional but recommended, and applies to every chain: +For a containerized node, set a memory limit so the chart can size the Go runtime. This is optional but recommended, and applies to every chain: -- **`resources.limits.memory`** — when set, the chart automatically derives `GOMEMLIMIT` (default 90% of the limit). The auto-`GOMEMLIMIT` behavior is on by default but only takes effect once a memory limit is present. -- **`MALLOC_ARENA_MAX=2`** — not set by the chart. Add it through `extraEnv`, which is a **list** spliced directly into the pod's `env`: +- **`resources.limits.memory`** — set this to activate the chart's automatic `GOMEMLIMIT`. When a memory limit is present, the chart derives `GOMEMLIMIT` from it (`env.nitro.goMemLimit`, on by default — it subtracts an estimate of non-Go memory and applies a `0.9` multiplier). +- **`MALLOC_ARENA_MAX=2`** — already set by the chart by default (`env.nitro.mallocArenaMax`, `enabled: true`, `value: 2`), so you don't normally need to configure it. Adjust or disable it under `env.nitro.mallocArenaMax` if needed. +- **`node.resource-mgmt.mem-free-limit`** (optional) — an RPC memory throttle that's disabled by default. Recommended if you expose this node as a public RPC. - ```yaml - extraEnv: - - name: MALLOC_ARENA_MAX - value: '2' - ``` +To set any other environment variable, use `extraEnv`, whose entries are spliced directly into the pod's `env`: -- **`node.resource-mgmt.mem-free-limit`** (optional) — an RPC memory throttle that's disabled by default. Recommended if you expose this node as a public RPC. +```yaml +extraEnv: + - name: SOME_VAR + value: 'value' +``` -For the allocator details, the `GOMEMLIMIT` formula, and `MALLOC_ARENA_MAX`, see the [memory management deep-dive](/run-arbitrum-node/nitro/nitro-memory-management.mdx) and the [memory management section](/run-arbitrum-node/run-full-node#memory-management) of the Docker full-node guide. +For the allocator details, the `GOMEMLIMIT` formula, and `MALLOC_ARENA_MAX`, see the [memory management deep-dive](/run-arbitrum-node/nitro/05-nitro-memory-management.mdx) and the [memory management section](/run-arbitrum-node/02-run-full-node.mdx#memory-management) of the Docker full-node guide. ## Step 3: First sync from a snapshot @@ -129,7 +130,7 @@ Whether a snapshot is required depends on the chain: - **Nova and Sepolia** have published snapshots that speed up the initial sync but aren't strictly required. - **Your Arbitrum chain** typically syncs from genesis with no snapshot, unless you provide one via `init.url`. -When using a snapshot, the default base is `https://snapshot.arbitrum.foundation/`. Initial sync takes a while. The init flag is ignored once a database already exists, so it's safe to leave it in place across restarts. Confirm the current snapshot type and download details in the [Nitro database snapshots](/run-arbitrum-node/nitro/nitro-database-snapshots.mdx) guide. +When using a snapshot, the default base is `https://snapshot.arbitrum.foundation/`. Initial sync takes a while. The init flag is ignored once a database already exists, so it's safe to leave it in place across restarts. Confirm the current snapshot type and download details in the [Nitro database snapshots](/run-arbitrum-node/nitro/03-nitro-database-snapshots.mdx) guide. ## Step 4: Enable monitoring @@ -146,7 +147,7 @@ Once enabled, the node exposes Prometheus metrics at `http://:6070/debug/me ### Grafana dashboard -The chart README points to the repository's [Releases page](https://github.com/OffchainLabs/community-helm-charts/releases) for the Grafana dashboard. If a release doesn't attach the dashboard JSON, pull the last published copy from the repository's git history and import it: +The repository's README points to the [Releases page](https://github.com/OffchainLabs/community-helm-charts/releases) for the Grafana dashboard. If a release doesn't attach the dashboard JSON, pull the last published copy from the repository's git history and import it: ```shell git clone https://github.com/OffchainLabs/community-helm-charts From 46de0bc6d15abb3de792c772b51c358972348fb9 Mon Sep 17 00:00:00 2001 From: Jason-Wanxt Date: Thu, 2 Jul 2026 04:04:55 +0800 Subject: [PATCH 4/8] Recommend node.da.anytrust.* over deprecated node.data-availability.* Co-Authored-By: Claude Opus 4.8 --- docs/run-arbitrum-node/run-full-node-with-helm.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/run-arbitrum-node/run-full-node-with-helm.mdx b/docs/run-arbitrum-node/run-full-node-with-helm.mdx index 993f315523..3121059eb4 100644 --- a/docs/run-arbitrum-node/run-full-node-with-helm.mdx +++ b/docs/run-arbitrum-node/run-full-node-with-helm.mdx @@ -91,7 +91,7 @@ configmap: helm install mychain-fullnode offchainlabs/nitro -f values-mychain.yaml ``` -If the parent chain is Ethereum, also set `configmap.data.parent-chain.blob-client.beacon-url`. AnyTrust chains additionally need a Data Availability configuration (`node.data-availability.*` and a `rest-aggregator`); see [Data Availability](/run-arbitrum-node/data-availability.mdx). +If the parent chain is Ethereum, also set `configmap.data.parent-chain.blob-client.beacon-url`. AnyTrust chains additionally need a Data Availability configuration (`node.da.anytrust.*`, including a `rest-aggregator`); see [Data Availability](/run-arbitrum-node/data-availability.mdx). The older `node.data-availability.*` flags still work but are deprecated in Nitro and will be removed in a future release. From f1024d35f7be771e9fdfb63bca968dd083afab36 Mon Sep 17 00:00:00 2001 From: Pete Date: Thu, 2 Jul 2026 11:45:13 -0500 Subject: [PATCH 5/8] Apply suggestions from code review Co-authored-by: Pete --- .../run-full-node-with-helm.mdx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/run-arbitrum-node/run-full-node-with-helm.mdx b/docs/run-arbitrum-node/run-full-node-with-helm.mdx index 3121059eb4..f8c7793015 100644 --- a/docs/run-arbitrum-node/run-full-node-with-helm.mdx +++ b/docs/run-arbitrum-node/run-full-node-with-helm.mdx @@ -23,13 +23,13 @@ If you want to run a node with Docker instead of Kubernetes, see [How to run a f ## Prerequisites -- **Parent chain access:** an execution RPC endpoint for the chain's parent. For Arbitrum One, Nova, and Sepolia the parent is Ethereum, so you also need an **L1 beacon/blob** endpoint (see [L1 Ethereum RPC providers](/run-arbitrum-node/04-l1-ethereum-beacon-chain-rpc-providers.mdx)). For an Arbitrum chain whose parent is another chain, supply that parent chain's RPC; a beacon endpoint is only required when the chain posts blobs to its parent chain. +- **Parent chain access:** an execution RPC endpoint for the chain's parent. For Arbitrum One, Nova, and Sepolia, the parent is Ethereum, so you also need an **L1 beacon/blob** endpoint (see [L1 Ethereum RPC providers](/run-arbitrum-node/04-l1-ethereum-beacon-chain-rpc-providers.mdx)). For an Arbitrum chain whose parent is another chain, use the RPC of that parent chain; a beacon endpoint is required only when the chain posts blobs to its parent chain. - **Cluster:** Kubernetes with Helm installed. -- **Disk:** size storage for the chain you're running. A node's database size varies widely from chain to chain and grows over time, so check the expected size with your chain operator before provisioning (for Arbitrum One it runs to multiple TB). Raise the chart's default `persistence.size` (`500Gi`) accordingly, and allow roughly 2× the snapshot size of additional temporary disk for extraction on the first run. +- **Disk:** size storage for the chain you're running. A node's database size varies widely from chain to chain and grows over time, so check the expected size with your chain operator before provisioning (for Arbitrum One, it runs to multiple TB). Raise the chart's default `persistence.size` (`500Gi`) accordingly, and allow roughly 2× the snapshot size for an additional temporary disk for extraction on the first run. ## Step 1: Deploy with Helm -First add the chart repo: +First, add the chart repo: ```shell helm repo add offchainlabs https://charts.arbitrum.io @@ -98,7 +98,7 @@ If the parent chain is Ethereum, also set `configmap.data.parent-chain.blob-clie -- **Don't copy the chart README's init example verbatim.** It uses `nitro-genesis.tar`, which syncs from genesis — huge and slow on a chain with a long history such as Arbitrum One. Where a pruned snapshot exists, prefer `configmap.data.init.latest=pruned` (default base `https://snapshot.arbitrum.foundation/`). +- **Don't copy the chart README's init example verbatim.** It uses `nitro-genesis.tar`, which syncs from genesis — a huge, slow process on a chain with a long history, such as Arbitrum One. Where a pruned snapshot exists, prefer `configmap.data.init.latest=pruned` (default base `https://snapshot.arbitrum.foundation/`). - **The chart changes the RPC path prefix.** Defaults are `http.rpcprefix=/rpc` and `ws.rpcprefix=/ws`, so RPC is served at `http://host:8547/rpc`, not vanilla Nitro's `/`. Clients that omit `/rpc` get a 404. - **Metrics are off by default.** Enable `configmap.data.metrics=true` and `serviceMonitor.enabled=true` to scrape them (see [Step 4](#step-4-enable-monitoring)). @@ -108,7 +108,7 @@ If the parent chain is Ethereum, also set `configmap.data.parent-chain.blob-clie For a containerized node, set a memory limit so the chart can size the Go runtime. This is optional but recommended, and applies to every chain: -- **`resources.limits.memory`** — set this to activate the chart's automatic `GOMEMLIMIT`. When a memory limit is present, the chart derives `GOMEMLIMIT` from it (`env.nitro.goMemLimit`, on by default — it subtracts an estimate of non-Go memory and applies a `0.9` multiplier). +- **`resources.limits.memory`** — set this to activate the chart's automatic `GOMEMLIMIT`. When a memory limit is present, the chart derives `GOMEMLIMIT` from it (`env.nitro.goMemLimit`, on by default—it subtracts an estimate of non-Go memory and applies a `0.9` multiplier). - **`MALLOC_ARENA_MAX=2`** — already set by the chart by default (`env.nitro.mallocArenaMax`, `enabled: true`, `value: 2`), so you don't normally need to configure it. Adjust or disable it under `env.nitro.mallocArenaMax` if needed. - **`node.resource-mgmt.mem-free-limit`** (optional) — an RPC memory throttle that's disabled by default. Recommended if you expose this node as a public RPC. @@ -126,7 +126,7 @@ For the allocator details, the `GOMEMLIMIT` formula, and `MALLOC_ARENA_MAX`, see Whether a snapshot is required depends on the chain: -- **Arbitrum One** requires a snapshot on the first run because of its Classic-era history. Use `configmap.data.init.latest=pruned`. +- **Arbitrum One** requires a snapshot during the first run due to its Classic-era history. Use `configmap.data.init.latest=pruned`. - **Nova and Sepolia** have published snapshots that speed up the initial sync but aren't strictly required. - **Your Arbitrum chain** typically syncs from genesis with no snapshot, unless you provide one via `init.url`. @@ -147,7 +147,7 @@ Once enabled, the node exposes Prometheus metrics at `http://:6070/debug/me ### Grafana dashboard -The repository's README points to the [Releases page](https://github.com/OffchainLabs/community-helm-charts/releases) for the Grafana dashboard. If a release doesn't attach the dashboard JSON, pull the last published copy from the repository's git history and import it: +The repository’s README points to the Grafana dashboard's [Releases page](https://github.com/OffchainLabs/community-helm-charts/releases). If a release doesn't attach the dashboard JSON, pull the last published copy from the repository's git history and import it: ```shell git clone https://github.com/OffchainLabs/community-helm-charts @@ -158,7 +158,7 @@ Then in Grafana, go to **Dashboards → Import** and paste `overview.json`. -This exported dashboard has no `__inputs` datasource prompt, and its `Source` variable is hard-pinned to an internal Mimir UID. After importing, you **must** repoint the `Source` variable to your own Prometheus, or every panel reads "No data." Then set `nitronodejob` to this node's scrape job, and leave `sequencerjob`, `validatorjob`, and `relayjob` empty so only the full-node rows render. +This exported dashboard has no `__inputs` datasource prompt, and its `Source` variable is hard-pinned to an internal Mimir UID. After importing, you **must** repoint the `Source` variable to your own Prometheus instance, or every panel will read "No data." Then set `nitronodejob` to this node's scrape job, and leave `sequencerjob`, `validatorjob`, and `relayjob` empty so only the full-node rows render. @@ -207,7 +207,7 @@ ERROR Error checking memory limit err=... checker=CgroupsMemoryLimitChecker ## Network egress allowlist -A full node reaches out to a fixed set of endpoints. If you run on a cloud provider, outbound traffic is often restricted by default, so you'll likely need to allow these destinations explicitly in your cloud security group or firewall rules (for example, AWS security groups, GCP firewall rules, or an egress `NetworkPolicy`). The specific hostnames depend on the chain: for DAO-governed networks they come from the chain's built-in chain info, and for your own Arbitrum chain they come from the `chain.info-json`, `execution.forwarding-target`, and feed URLs you configure. The categories are the same across chains: +A full node reaches out to a fixed set of endpoints. If you run on a cloud provider, outbound traffic is often restricted by default, so you'll likely need to allow these destinations explicitly in your cloud security group or firewall rules (for example, AWS security groups, GCP firewall rules, or an egress `NetworkPolicy`). The specific hostnames depend on the chain: for DAO-governed networks, they come from the chain's built-in chain info, and for your own Arbitrum chain, they come from the `chain.info-json`, `execution.forwarding-target`, and feed URLs you configure. The categories are the same across chains: | Purpose | Where the endpoint comes from | When | | ----------------------------- | ------------------------------------------------------------------- | ------------------------------------------- | @@ -234,6 +234,6 @@ For **Arbitrum One**, those resolve to: -For a default Arbitrum One full node, allow `*.arbitrum.io` (443 HTTPS + WSS), `snapshot.arbitrum.foundation` (443, init only), and your own L1 RPC + beacon hosts. For other chains, allow that chain's sequencer, feed, and snapshot hosts plus your parent-chain endpoints. **Additional outbound paths exist only if you enable them:** the version alerter (off by default, queries an operator-set endpoint), the classic redirect, or DA/REST endpoints (AnyTrust chains such as Nova). +For a default Arbitrum One full node, allow `*.arbitrum.io` (443 HTTPS + WSS), `snapshot.arbitrum.foundation` (443, init only), and your own L1 RPC + beacon hosts. For other chains, allow that chain's sequencer, feed, and snapshot hosts, plus your parent-chain endpoints. **Additional outbound paths exist only if you enable them:** the version alerter (off by default, queries an operator-set endpoint), the classic redirect, or DA/REST endpoints (AnyTrust chains such as Nova). From 013cffef0229ca3e3c36a42fb013f1f23d797199 Mon Sep 17 00:00:00 2001 From: Pete Date: Thu, 2 Jul 2026 11:47:12 -0500 Subject: [PATCH 6/8] Apply suggestions from code review Co-authored-by: Pete --- docs/run-arbitrum-node/run-full-node-with-helm.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/run-arbitrum-node/run-full-node-with-helm.mdx b/docs/run-arbitrum-node/run-full-node-with-helm.mdx index f8c7793015..b9e4ebfc48 100644 --- a/docs/run-arbitrum-node/run-full-node-with-helm.mdx +++ b/docs/run-arbitrum-node/run-full-node-with-helm.mdx @@ -11,7 +11,7 @@ sme: 'Jason-W123' import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -This guide shows how to deploy a full node for an Arbitrum chain on Kubernetes using the [community Helm chart](https://github.com/OffchainLabs/community-helm-charts/tree/main/charts/nitro). It applies to any Arbitrum chain — [Arbitrum One](https://arbitrum.io), Nova, Sepolia, and your own Arbitrum chain — and covers installation, memory configuration, first sync from a snapshot, monitoring, the log signals that distinguish a healthy node from a broken one, and the outbound endpoints to allow through a firewall. +This guide shows how to deploy a full node for an Arbitrum chain on Kubernetes using the [community Helm chart](https://github.com/OffchainLabs/community-helm-charts/tree/main/charts/nitro). It applies to any Arbitrum chain—[Arbitrum One](https://arbitrum.io), Nova, Sepolia, and your own Arbitrum chain—and covers installation, memory configuration, first sync from a snapshot, monitoring, the log signals that distinguish a healthy node from a broken one, and the outbound endpoints to allow through a firewall. The chart's defaults target Arbitrum One, so Arbitrum One is used as the worked example throughout. Each step notes what changes for other chains. From b9499dd500cac2d4625c3ce5871c4ccd045a8716 Mon Sep 17 00:00:00 2001 From: Pete Date: Thu, 2 Jul 2026 11:52:21 -0500 Subject: [PATCH 7/8] Apply suggestions from code review Co-authored-by: Pete --- docs/run-arbitrum-node/run-full-node-with-helm.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/run-arbitrum-node/run-full-node-with-helm.mdx b/docs/run-arbitrum-node/run-full-node-with-helm.mdx index b9e4ebfc48..0994ac441d 100644 --- a/docs/run-arbitrum-node/run-full-node-with-helm.mdx +++ b/docs/run-arbitrum-node/run-full-node-with-helm.mdx @@ -2,7 +2,7 @@ title: 'How to run a full node with Helm on Kubernetes' sidebar_label: 'Run a full node (Helm)' description: 'Deploy an Arbitrum chain full node on Kubernetes with the community Helm chart, including memory configuration, monitoring, log signals, and network egress.' -user_story: 'As a node operator, I want to deploy and reliably operate an Arbitrum chain full node on Kubernetes using the community Helm chart.' +user_story: 'As a node operator, I want to deploy and reliably operate an full node for an Arbitrum chain on Kubernetes using the community Helm chart.' content_type: how-to author: 'Jason-W123' sme: 'Jason-W123' From c351a5ff5b2d94808835b60669605b7544b292e1 Mon Sep 17 00:00:00 2001 From: Pete Date: Thu, 2 Jul 2026 11:59:33 -0500 Subject: [PATCH 8/8] Moving run full node helm to launch-arbitrum-chain/run-a-node; modifying sidebar title; minor styling --- .../run-a-node}/run-full-node-with-helm.mdx | 28 +++++++++---------- docs/run-arbitrum-node/02-run-full-node.mdx | 2 +- sidebars.js | 10 +++---- 3 files changed, 20 insertions(+), 20 deletions(-) rename docs/{run-arbitrum-node => launch-arbitrum-chain/run-a-node}/run-full-node-with-helm.mdx (91%) diff --git a/docs/run-arbitrum-node/run-full-node-with-helm.mdx b/docs/launch-arbitrum-chain/run-a-node/run-full-node-with-helm.mdx similarity index 91% rename from docs/run-arbitrum-node/run-full-node-with-helm.mdx rename to docs/launch-arbitrum-chain/run-a-node/run-full-node-with-helm.mdx index 0994ac441d..87311a9c2a 100644 --- a/docs/run-arbitrum-node/run-full-node-with-helm.mdx +++ b/docs/launch-arbitrum-chain/run-a-node/run-full-node-with-helm.mdx @@ -1,6 +1,6 @@ --- title: 'How to run a full node with Helm on Kubernetes' -sidebar_label: 'Run a full node (Helm)' +sidebar_label: 'Run a full node (Helm/Kubernetes)' description: 'Deploy an Arbitrum chain full node on Kubernetes with the community Helm chart, including memory configuration, monitoring, log signals, and network egress.' user_story: 'As a node operator, I want to deploy and reliably operate an full node for an Arbitrum chain on Kubernetes using the community Helm chart.' content_type: how-to @@ -108,9 +108,9 @@ If the parent chain is Ethereum, also set `configmap.data.parent-chain.blob-clie For a containerized node, set a memory limit so the chart can size the Go runtime. This is optional but recommended, and applies to every chain: -- **`resources.limits.memory`** — set this to activate the chart's automatic `GOMEMLIMIT`. When a memory limit is present, the chart derives `GOMEMLIMIT` from it (`env.nitro.goMemLimit`, on by default—it subtracts an estimate of non-Go memory and applies a `0.9` multiplier). -- **`MALLOC_ARENA_MAX=2`** — already set by the chart by default (`env.nitro.mallocArenaMax`, `enabled: true`, `value: 2`), so you don't normally need to configure it. Adjust or disable it under `env.nitro.mallocArenaMax` if needed. -- **`node.resource-mgmt.mem-free-limit`** (optional) — an RPC memory throttle that's disabled by default. Recommended if you expose this node as a public RPC. +- **`resources.limits.memory`** set this to activate the chart's automatic `GOMEMLIMIT`. When a memory limit is present, the chart derives `GOMEMLIMIT` from it (`env.nitro.goMemLimit`, on by default—it subtracts an estimate of non-Go memory and applies a `0.9` multiplier). +- **`MALLOC_ARENA_MAX=2`** already set by the chart by default (`env.nitro.mallocArenaMax`, `enabled: true`, `value: 2`), so you don't normally need to configure it. Adjust or disable it under `env.nitro.mallocArenaMax` if needed. +- **`node.resource-mgmt.mem-free-limit`** (optional) an RPC memory throttle that's disabled by default. Recommended if you expose this node as a public RPC. To set any other environment variable, use `extraEnv`, whose entries are spliced directly into the pod's `env`: @@ -143,7 +143,7 @@ helm upgrade offchainlabs/nitro \ --set serviceMonitor.enabled=true ``` -Once enabled, the node exposes Prometheus metrics at `http://:6070/debug/metrics/prometheus` — the metrics server binds to `0.0.0.0:6070` by default (`configmap.data.metrics-server.port`), and the `ServiceMonitor` scrapes that port and path (`serviceMonitor.path` defaults to `/debug/metrics/prometheus`). +Once enabled, the node exposes Prometheus metrics at `http://:6070/debug/metrics/prometheus`—the metrics server binds to `0.0.0.0:6070` by default (`configmap.data.metrics-server.port`), and the `ServiceMonitor` scrapes that port and path (`serviceMonitor.path` defaults to `/debug/metrics/prometheus`). ### Grafana dashboard @@ -170,21 +170,21 @@ The chart wires a built-in **startup probe** by default, but **liveness and read The chart sets `log-type=json`. The following INFO/WARN/ERROR strings distinguish a healthy node from a broken one on any Arbitrum chain (the sequencer hostnames in the examples below are Arbitrum One's; your chain's will differ): -| Event | Healthy signal | Broken / warning signal | -| ---------------------------------- | ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | -| **New block created** | `INFO created block` with `l2Block` / `l2BlockHash`, advancing continuously while producing | No advancing `created block` line → block production stalled | -| **User tx forwarded to sequencer** | Success is not logged | `WARN error forwarding transaction trying different target`; `ERROR Failed to publish transaction to any of the forwarding targets` | -| **Sequencer feed message** | `INFO Feed connected`; `DEBUG received batch item` | `WARN failed connect to sequencer broadcast, waiting and retrying`; `ERROR Server connection timed out without receiving data` | -| **Inbox messages (parent chain)** | `INFO InboxTracker` with `sequencerBatchCount` / `messageCount` / `l1Block` advancing | `WARN error reading inbox`. Note: `backwards reorg of delayed messages` is logged at INFO level — it's normal on parent-chain reorgs, not an alert. | +| Event | Healthy signal | Broken / warning signal | +| ---------------------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| **New block created** | `INFO created block` with `l2Block` / `l2BlockHash`, advancing continuously while producing | No advancing `created block` line → block production stalled | +| **User tx forwarded to sequencer** | Success is not logged | `WARN error forwarding transaction trying different target`; `ERROR Failed to publish transaction to any of the forwarding targets` | +| **Sequencer feed message** | `INFO Feed connected`; `DEBUG received batch item` | `WARN failed connect to sequencer broadcast, waiting and retrying`; `ERROR Server connection timed out without receiving data` | +| **Inbox messages (parent chain)** | `INFO InboxTracker` with `sequencerBatchCount` / `messageCount` / `l1Block` advancing | `WARN error reading inbox`. **Note**: `backwards reorg of delayed messages` is logged at INFO level—it's normal on parent-chain reorgs, not an alert. | ### Why a full node can't forward transactions to the sequencer -A full node forwards user transactions to the sequencer. When forwarding fails, the cause falls into one of three buckets. Forwarding failures are **log-based, not metric-based** — the forwarder emits no metrics — so alert by grepping the log strings below. +A full node forwards user transactions to the sequencer. When forwarding fails, the cause falls into one of three buckets. Forwarding failures are **log-based, not metric-based**—the forwarder emits no metrics—so alert by grepping the log strings below. | Cause | Signal | | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | | **All forwarding targets unreachable** (egress blocked, or sequencer + all fallbacks down) | `ERROR Failed to publish transaction to any of the forwarding targets`, preceded by per-target `WARN` lines | -| **Sequencer returns a business error** (non-connection, e.g. `nonce too low`) | `WARN error forwarding transaction trying different target` with the sequencer's `err` — not escalated; the error is returned to the caller | +| **Sequencer returns a business error** (non-connection, e.g. `nonce too low`) | `WARN error forwarding transaction trying different target` with the sequencer's `err`—not escalated; the error is returned to the caller | | **Memory 429 throttling** (rejected before forwarding) | The client receives `HTTP 429 Too many requests` and the metric `arb/rpc/limitcheck/failure` increments. There is **no per-request log line**. | Representative log lines (hostnames shown are Arbitrum One's): @@ -211,7 +211,7 @@ A full node reaches out to a fixed set of endpoints. If you run on a cloud provi | Purpose | Where the endpoint comes from | When | | ----------------------------- | ------------------------------------------------------------------- | ------------------------------------------- | -| **Sequencer** (tx forwarding) | chain info `sequencer-url` / `execution.forwarding-target` | Always — the node forwards user txs here | +| **Sequencer** (tx forwarding) | chain info `sequencer-url` / `execution.forwarding-target` | Always—the node forwards user txs here | | Sequencer fallbacks | chain info `secondary-forwarding-target` | Failover | | **Sequencer feed** | chain info `feed-url` / `node.feed.input.url` | Always | | Feed fallbacks / delayed | chain info `secondary-feed-url` / `node.feed.input.secondary-url` | Failover | diff --git a/docs/run-arbitrum-node/02-run-full-node.mdx b/docs/run-arbitrum-node/02-run-full-node.mdx index c522376942..cad4a078de 100644 --- a/docs/run-arbitrum-node/02-run-full-node.mdx +++ b/docs/run-arbitrum-node/02-run-full-node.mdx @@ -20,7 +20,7 @@ To view the short and long term support policy, visit the [Nitro support policy] -This page covers running a node with Docker. If you want to deploy on Kubernetes, or you want a more production-ready setup with monitoring, log signals, and network egress guidance, follow [How to run a full node with Helm on Kubernetes](/run-arbitrum-node/run-full-node-with-helm.mdx) instead. +This page covers running a node with Docker. If you want to deploy on Kubernetes, or you want a more production-ready setup with monitoring, log signals, and network egress guidance, follow [How to run a full node with Helm on Kubernetes](/launch-arbitrum-chain/run-a-node/run-full-node-with-helm.mdx) instead. diff --git a/sidebars.js b/sidebars.js index 059ddbd4cc..4da6e0ebc8 100644 --- a/sidebars.js +++ b/sidebars.js @@ -562,6 +562,11 @@ const sidebars = { // q: why use an anchor html tag here? // a: see note at end of file }, + { + type: 'doc', + id: 'launch-arbitrum-chain/run-a-node/run-full-node-with-helm', + label: 'Run a full node (Helm/Kubernetes)', + }, { type: 'html', value: @@ -644,11 +649,6 @@ const sidebars = { id: 'run-arbitrum-node/run-full-node', label: 'Run a full node', }, - { - type: 'doc', - id: 'run-arbitrum-node/run-full-node-with-helm', - label: 'Run a full node (Helm)', - }, { type: 'doc', id: 'run-arbitrum-node/run-local-full-chain-simulation',