Logo
New RPC users get 35% off their first monthView the offer
OnFinality Learn
Network & Protocol Guides11 min read

How to Create a Blockchain Chain: From Genesis to a Verifiable Local Network

A reproducible workflow for creating your own EVM-compatible blockchain chain, from genesis configuration and node startup to RPC verification and troubleshooting.

TL;DR

Creating a blockchain chain means defining a genesis state, agreeing on consensus and network parameters, running at least one node, and proving the chain works through JSON-RPC. This guide walks through a reproducible EVM-compatible setup using genesis JSON, node configuration, and verification scripts. It separates documented client behavior from the measurements you should run yourself, and it explains where a managed RPC layer fits once the chain leaves your laptop. By the end you will have a chain you can query, a method for validating it, and a troubleshooting checklist for the failures that most often block first startup.

What creating a chain actually involves

A blockchain chain is not a single file or a single command. It is a set of agreed parameters (chain ID, genesis state, consensus rules, fork schedule), a set of nodes that each validate those parameters, and a peer-to-peer network that propagates blocks between them. Creating a chain means defining that agreement and then running software that enforces it.

This article focuses on EVM-compatible chains because the tooling is widely documented and easy to verify. The same mental model applies to non-EVM chains: define genesis, configure the node, start it, and verify through the chain's native RPC interface. If you only need to connect to an existing network rather than create one, the endpoint-selection work belongs in RPC Assistant; this page stays on the creation and verification workflow.

  • Genesis: the initial state and chain-wide parameters.
  • Consensus: how blocks are produced and finalized.
  • Node configuration: data directory, ports, peers, and RPC exposure.
  • Verification: proving the chain responds correctly to standard calls.

Prerequisites and assumptions

You need a machine with Docker or a native EVM client installed, a free local port range, and basic command-line familiarity. The examples below use placeholders such as RPC_URL and PRIVATE_KEY; never commit real keys to a repository. For production or shared networks, plan for dedicated infrastructure rather than a laptop, and review dedicated node options before exposing anything publicly.

Assumptions: you are building a private or test chain first, you control all initial validators, and you will not bridge real assets until the chain has been independently reviewed. Creating a chain is straightforward; operating one securely is the harder problem.

Defining the genesis file

The genesis file is the root of trust for a new chain. Every node must use a byte-identical genesis file, or nodes will reject each other's blocks. At minimum, define the chain ID, the initial account balances, the gas limit, and the consensus configuration. The example below is a minimal development genesis; replace the alloc address with one you control.

Keep the genesis file under version control and record its hash. When you later change consensus or fork parameters, you are effectively creating a new chain, so treat those changes as breaking.

{
  "config": {
    "chainId": 1337,
    "homesteadBlock": 0,
    "eip150Block": 0,
    "eip155Block": 0,
    "eip158Block": 0,
    "byzantiumBlock": 0,
    "constantinopleBlock": 0,
    "petersburgBlock": 0,
    "istanbulBlock": 0,
    "berlinBlock": 0,
    "londonBlock": 0,
    "clique": { "period": 5, "epoch": 30000 }
  },
  "difficulty": "0x1",
  "gasLimit": "0x1c9c380",
  "alloc": {
    "0x0000000000000000000000000000000000000001": {
      "balance": "0xde0b6b3a7640000"
    }
  }
}

Configuring and starting the first node

With genesis defined, initialize the node's data directory and start it with RPC enabled on localhost only. The exact flags depend on your client; the pattern is consistent: point at the genesis, set the data directory, enable the HTTP RPC namespace you need, and restrict host binding until you have verified the chain.

The command below uses a generic client binary name as a placeholder. Substitute the binary you installed and confirm flag names against that client's documentation, since flags differ between clients and versions.

# Initialize the data directory with the genesis file
your-client init --datadir ./chain-data genesis.json

# Start the node with local-only RPC
your-client \
  --datadir ./chain-data \
  --networkid 1337 \
  --http \
  --http.addr 127.0.0.1 \
  --http.port 8545 \
  --http.api eth,net,web3 \
  --mine \
  --miner.etherbase 0x0000000000000000000000000000000000000001

Verifying the chain over JSON-RPC

A chain is only real once it answers standard calls. Use curl to confirm the chain ID, then confirm that blocks are being produced. Set RPC_URL to your local endpoint, for example http://127.0.0.1:8545. These calls are read-only and safe to repeat.

The expected result is a chain ID matching your genesis and a block number that increases over time. If the chain ID is wrong, your node is reading a different genesis. If the block number stays at zero, block production is not running.

curl -s -X POST "$RPC_URL" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}'

curl -s -X POST "$RPC_URL" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"eth_blockNumber","params":[]}'

A repeatable measurement procedure

Do not trust a single successful call. Run a short script that samples the chain over time and records the results. The table below is a template: fill in the observed values from your own run rather than copying numbers from anywhere else. This keeps your verification honest and reproducible.

CheckMethodExpectedObserved
Chain identityeth_chainIdMatches genesis chainId(record)
Block productioneth_blockNumber sampled twiceSecond value greater(record)
Peer countnet_peerCountAt least 1 for multi-node(record)
RPC latencyTimed curl loopStable, no timeouts(record)

The JavaScript snippet below automates the sampling. It uses the built-in fetch available in modern Node.js and writes a simple pass/fail summary. Replace RPC_URL with your endpoint.

const RPC_URL = process.env.RPC_URL;

async function call(method, params = []) {
  const res = await fetch(RPC_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params })
  });
  const json = await res.json();
  if (json.error) throw new Error(json.error.message);
  return json.result;
}

(async () => {
  const chainId = await call('eth_chainId');
  const first = await call('eth_blockNumber');
  await new Promise(r => setTimeout(r, 12000));
  const second = await call('eth_blockNumber');
  const advanced = parseInt(second, 16) > parseInt(first, 16);
  console.log({ chainId, first, second, advanced });
})();

Adding a second node and confirming consensus

A single node proves the software runs; two nodes prove the network works. Start a second node with the same genesis and network ID, then connect it to the first using the first node's enode URL. Confirm that net_peerCount rises on both nodes and that both report the same latest block hash.

If the second node rejects blocks or never syncs, the most common causes are a mismatched genesis hash, a different network ID, or a firewall blocking the peer-to-peer port. Compare the genesis hash on both machines before debugging anything else.

Troubleshooting first-startup failures

Most failures when creating a chain fall into a small number of categories. Work through them in order, because later symptoms often disappear once an earlier cause is fixed.

If the node exits immediately, check that the data directory was initialized with the correct genesis and that the binary supports the flags you passed. If RPC calls fail, confirm the HTTP server is enabled and bound to the address you are calling. If blocks are not produced, confirm the mining or validator configuration and that the signing account is unlocked or configured correctly.

  • Genesis mismatch: nodes silently reject peers; compare genesis hashes.
  • Port conflicts: another process holds 8545 or the P2P port.
  • RPC namespace disabled: the method returns 'method not found'.
  • Clock skew: timestamps cause block rejection on some consensus engines.
  • Insufficient disk: the node stops writing and appears to hang.

Limitations and tradeoffs

A locally created chain is a development artifact, not a production network. It has no economic security, no independent validators, and no resilience to operator failure. Moving toward production requires validator onboarding, monitoring, upgrade governance, and a plan for RPC availability.

Running your own RPC layer gives you control but adds operational burden: uptime, rate limiting, and version upgrades. Many teams keep a self-hosted node for validation and use a managed service for public read traffic. The RPC endpoint guide explains how to think about that split, and RPC pricing helps estimate the cost of serving external traffic.

Next steps after the chain runs

Once your chain produces blocks and a second node syncs, the next work is tooling and access. Deploy a test contract, connect a wallet, and confirm that logs and receipts behave as expected. Then decide how external developers will reach the chain.

If you plan to expose the chain to other teams, review the API service options and the list of supported networks to understand how managed access is structured. For endpoint-specific recommendations, use RPC Assistant rather than guessing at public URLs. Keep your genesis file, node configuration, and verification scripts together so the chain can be rebuilt from scratch by anyone on the team.

Never Worry about Infrastructure Again

OnFinality takes away the heavy lifting of DevOps so you can build smarter and faster.

Get Started