From Zero to Private Ethereum: Launch Your Hyperledger Besu Network in 30 Minutes!

Parth PanotParth Panot
5 min read

Welcome back, everyone! In Part 1, we had explored the concept of Private Networks. We discussed what private blockchain networks are, their significance for businesses and enterprises, and how Hyperledger Besu is driving this transformation.

In this blog, we are going to setup a real private blockchain network using Hyperledger Besu, from scratch.

We will cover:

  • Setting up genesis file

  • Configuring bootnodes and validators

  • Running RPC nodes

  • Interfacing with MetaMask

Prerequisites:

  • Linux/Unix based environment

  • Java JDK 17+

  • Besu cli

Setup with project directory:

MyPrivateNet/
├── data(generated)/
│   ├── rpcnode/
│   ├── validator1/
│   └── validator2/
├── genesisConfig.json
└── networkFiles(generated)/
    ├── genesis.json
    └── keys/
        ├── 0xb565c8463fd2a79e95a45e2ab4055a750b1af53f/
        │   ├── key
        │   └── key.pub
        ├── 0xcb1ae74b12f0c686ecf8f9a4892cdc62069d3d3a/
        │   ├── key
        │   └── key.pub
        └── rpcnode/
            └── key

Setting up GenesisConfig File:

Genesis file is the starting point of any blockchain network. You can think of it as “birth certificate” of your blockchain. It defines parameters like ChainID, Consensus mechanism, Initial account balances and allocations, Block parameters, etc…

Here is demo of genesisConfig.json file I have used to create my private net:

{
  "genesis": {
    "config": {
      "chainId": 2025,
      "berlinBlock": 0,
      "londonBlock": 0,
      "zeroBaseFee": true,
      "qbft": {
        "epochlength": 30000,
        "blockperiodseconds": 5,
        "requesttimeoutseconds": 10
      }
    },
    "nonce": "0x0",
    "timestamp": "0x5b3d92d7",
    "extraData": "0xf87aa00000000000000000000000000000000000000000000000000000000000000000f8549464a702e6263b7297a96638cac6ae65e6541f4169943923390ad55e90c237593b3b0e401f3b08a0318594aefdb9a738c9f433e5b6b212a6d62f6370c2f69294c7eeb9a4e00ce683cf93039b212648e01c6c6b78c080c0",
    "gasLimit": "0x29b92700",
    "difficulty": "0x1",
    "mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365",
    "coinbase": "0x0000000000000000000000000000000000000000",
    "alloc": {
      "0xBCAb09ee9Ea9B72AaaCCeF960a1dEE1Da73fE12F": {
        "balance": "1000000000000000000000"
      },
      "0x4227feAf79c7F997850fb09767B1fa054694F20f": {
        "balance": "10000000000000000000000000000"
      },
      "0xC24E4D143F25856053dF9820d86b320b099c7025": {
        "balance": "10000000000000000000000000000"
      }
    },
    "number": "0x0",
    "gasUsed": "0x0",
    "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
  },
  "blockchain": {
    "nodes": {
      "generate": true,
      "count": 2
    }
  }
}
  • chainId: Unique identifier for your blockchain network (2025).

  • zeroBaseFee: Disables EIP-1559 base fee for private networks.

  • qbft: Config section for the QBFT consensus mechanism:

    • epochlength: Number of blocks after which validators are shuffled (30,000 here).

    • blockperiodseconds: Target time between blocks (5 seconds).

    • requesttimeoutseconds: Timeout for proposing or committing blocks.

  • extraData: Contains initial validator addresses for QBFT.

  • gasLimit: Max gas allowed per block (0x29b92700 = ~700 million).

  • coinbase: Address receiving block rewards (irrelevant in QBFT, usually 0x0).

  • alloc: Pre-funds these addresses with given balances in wei. Remember to replace 0xADDRESS1, 0xADDRESS2, 0xADDRESS3 with actual addresses you'll use.

  • generate: true: Indicates node keys should be auto-generated.

  • count: 2: Number of validator node key pairs to generate.

Generating Node Keys and Genesis File

Use this command to generate Node Keys and the final genesis.json file, which will be stored in the networkFiles directory.

besu operator generate-blockchain-config --config-file genesisConfig.json --to=networkFiles --private-key-file-name=key

Configuring and Launching Nodes

We'll launch two validator nodes and one RPC node. Open a separate terminal window for each.

Getting enode URLs for Bootnodes

Nodes find each other using enode URLs. These combine a node's public key (Node ID), IP address, and P2P port: enode://<node-ID>@<ip>:<port>.

  • Your Node ID is found in networkFiles/keys/<node-address>/key.pub.

  • For a local setup, the <ip> is 127.0.0.1.

  • The <port> is the P2P port you assign (e.g., 30303, 30304).

You will use the enode URL of one validator as the bootnodes for other nodes to connect to.

Start Validator Node 1

Run this in Terminal 1:

besu-25.7.0/bin/besu \
 --data-path=./data/validator1 \
 --genesis-file ./networkFiles/genesis.json \
 --node-private-key-file ./networkFiles/keys/0xb565c8463fd2a79e95a45e2ab4055a750b1af53f/key \
 --p2p-port=30303 \
 --rpc-http-enabled --rpc-http-host=0.0.0.0 --rpc-http-cors-origins="*" --rpc-http-port=8545 \
 --rpc-http-api=ETH,NET,WEB3,ADMIN,QBFT \
 --host-allowlist=* \
 --sync-mode=FULL \
 --bootnodes enode://<NODE-2-ID>@127.0.0.1:30304

Key Flags Explained:

  • --data-path: Unique storage location for this node's data.

  • --genesis-file: Points to the network's genesis.json.

  • --node-private-key-file: Identifies this node. (Update with your actual key path)

  • --p2p-port: For node-to-node communication.

  • --rpc-http-enabled/--rpc-http-port: Enables HTTP API for DApp/wallet interaction.

  • --rpc-http-api=ETH,NET,WEB3,ADMIN,QBFT: Essential API modules for interaction and monitoring.

  • --sync-mode=FULL: Syncs the entire blockchain history.

  • --bootnodes: Connects to Node 2 to discover the network. (Update with Node 2's actual enode URL)

Start Validator Node 2

Run this in Terminal 2:

besu-25.7.0/bin/besu \
 --data-path=./data/validator2 \
 --genesis-file ./networkFiles/genesis.json \
 --node-private-key-file ./networkFiles/keys/0xcb1ae74b12f0c686ecf8f9a4892cdc62069d3d3a/key \
 --p2p-port=30304 \
 --rpc-http-enabled --rpc-http-host=0.0.0.0 --rpc-http-cors-origins="*" --rpc-http-port=8546 \
 --rpc-http-api=ETH,NET,WEB3,ADMIN,QBFT \
 --host-allowlist=* \
 --sync-mode=FULL \
 --bootnodes enode://<NODE-1-ID>@127.0.0.1:30303

(**Important: Update the key path and bootnodes URL to point to Node 1.)

  • Differences: Unique data-path, node-private-key-file, p2p-port, and rpc-http-port.

Start RPC Node

Run this in Terminal 3:

besu-25.7.0/bin/besu \
 --data-path=./data/rpcnode \
 --genesis-file ./networkFiles/genesis.json \
 --node-private-key-file ./networkFiles/keys/rpcnode/key \
 --p2p-port=30307 \
 --rpc-http-enabled --rpc-http-host=0.0.0.0 --rpc-http-cors-origins="*" --rpc-http-port=8550 \
 --rpc-ws-enabled --rpc-ws-port=8551 \
 --rpc-http-api=ETH,NET,WEB3,ADMIN,DEBUG,TXPOOL,QBFT \
 --rpc-ws-api=ETH,NET,WEB3,ADMIN,DEBUG,TXPOOL,QBFT \
 --host-allowlist=* \
 --bootnodes enode://<NODE-1-ID>@127.0.0.1:30303

(**Important: Update the key path and bootnodes URL to point to a validator like Node 1.)

  • Differences: Unique data-path, node-private-key-file, p2p-port, rpc-http-port, and rpc-ws-port.

  • --rpc-ws-enabled: Enables WebSockets for real-time events.


Verifying Your Network

Once nodes are running, confirm your chain is alive!

1. Check Terminal Logs

Look for:

  • Imported #X: New blocks being added.

  • Synchronizer: current block X: Node's current block height.

  • QBFT: new round started/QBFT: adding prepared certificate: Validator activity.

2. Use Curl to Test Endpoints

Run these in a new terminal:

  • Get current block number:

      curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' http://127.0.0.1:8550
    

    Expected result: A hexadecimal block number (e.g., 0x...).

  • Get connected peers count:

      curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"net_peerCount","params":[],"id":1}' http://127.0.0.1:8550
    

    Expected result: {"jsonrpc":"2.0","id":1,"result":"0x2"} (meaning 2 peers connected).

3. Connect with MetaMask

  • Open MetaMask.

  • Click network dropdown -> "Add network" -> "Add a network manually".

  • Network Name: My Besu Private Chain

  • New RPC URL: http://127.0.0.1:8550 (or 8545 for Validator 1)

  • Chain ID: 2025 (Must match genesisConfig.json)

  • Currency Symbol: ETH (or your choice)

  • Click "Save." You should now be connected!

If you get valid responses, congratulations! Your private Hyperledger Besu network is up and running.

You Did It!

You've successfully launched your own private Ethereum-compatible blockchain using Hyperledger Besu – and hopefully, in well under 30 minutes! This hands-on experience provides a powerful development environment for your blockchain projects.

Now that you've mastered the setup, what can you do with this private chain? That's exactly what we'll explore in the final part of this series. We'll dive into real-world applications, discuss my personal learnings, and outline exciting next steps.

Stay tuned for Part 3: "Beyond Setup: What's Next for Your Hyperledger Besu Private Chain?"

1
Subscribe to my newsletter

Read articles from Parth Panot directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Parth Panot
Parth Panot