Deploying a Private Blockchain
"We need our own blockchain" — phrase I hear once a month. Usually behind it is a task that can be solved with ordinary DB with audit log or permissioned smart contracts on public blockchain. But there are legitimate cases: consortium settlements between competing companies when neither trusts the other enough to place data on their server; state registries requiring decentralization without publicity; trading networks with their own settlement token.
If you fall into one of these cases — this document is about doing it right.
Platform Choice
Three real options for enterprise private blockchain in 2024-2025:
Hyperledger Besu — EVM-compatible Ethereum client, developed by ConsenSys, now Linux Foundation. Supports QBFT, IBFT2 consensus. Main advantage: full EVM compatibility — everything working on Ethereum works here without changes. Solidity contracts, Hardhat, MetaMask, ethers.js — everything works. For teams with EVM expertise — first choice.
Hyperledger Fabric — not EVM, chaincode in Go/Java/Node.js, channel architecture, MSP (Membership Service Provider) for managing participants. Better for complex permissioned scenarios with different participant roles. More complex to operate.
Polygon Edge / Polygon CDK — EVM-compatible, bridge to Ethereum mainnet or Polygon. If you need link to public blockchain — good choice. Actively developing as appchain framework.
For most corporate tasks I recommend Besu with QBFT: EVM compatibility opens access to huge ecosystem of tools, and QBFT provides deterministic finality without forks.
Network Architecture with Hyperledger Besu + QBFT
QBFT (Quorum Byzantine Fault Tolerance) — Byzantine fault tolerant consensus. Network of N nodes withstands up to ⌊(N-1)/3⌋ failures. For 4 nodes — 1 failure, for 7 — 2.
Minimal production topology:
┌──────────────────────────────────────────────────────┐
│ Private Network │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │Validator1│ │Validator2│ │ Validator3 │ │
│ │ (Org A) │──▶│ (Org B) │──▶│ (Org C) │ │
│ └──────────┘ └──────────┘ └──────────────────┘ │
│ │ │ │ │
│ └──────────────┴────────────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ RPC / Boot node│ │
│ │ (non-validator)│ │
│ └────────────────-┘ │
└──────────────────────────────────────────────────────┘
Validators participate in consensus. RPC/Boot node — entry point for applications, doesn't validate.
Genesis Block Configuration
Genesis defines initial network state. Critical parameters:
{
"config": {
"chainId": 12345,
"berlinBlock": 0,
"londonBlock": 0,
"qbft": {
"blockperiodseconds": 2,
"epochlength": 30000,
"requesttimeoutseconds": 4
}
},
"nonce": "0x0",
"timestamp": "0x5B3D92D7",
"gasLimit": "0x1fffffffffffff",
"difficulty": "0x1",
"mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365",
"coinbase": "0x0000000000000000000000000000000000000000",
"alloc": {
"0xYOUR_INITIAL_ACCOUNT": {
"balance": "90000000000000000000000"
}
},
"extraData": "0x<QBFT_EXTRA_DATA>"
}
chainId — unique network identifier. Choose not intersecting with public networks. List of occupied chainIds: chainlist.org.
blockperiodseconds: 2 — block every 2 seconds. For financial transactions sufficient, for high-frequency can reduce to 1.
extraData for QBFT encodes list of initial validators. Generated via Besu:
besu operator generate-blockchain-config \
--config-file=qbftConfigFile.json \
--to=networkFiles \
--private-key-file-name=key
Deploy with Docker Compose
version: '3.8'
services:
validator1:
image: hyperledger/besu:latest
volumes:
- ./data/validator1:/data
- ./genesis.json:/genesis.json
- ./networkFiles/keys/validator1:/keys
command: >
--data-path=/data
--genesis-file=/genesis.json
--node-private-key-file=/keys/key
--rpc-http-enabled
--rpc-http-api=ETH,NET,QBFT,ADMIN
--rpc-http-cors-origins=*
--rpc-http-port=8545
--p2p-port=30303
--bootnodes=enode://BOOTNODE_ENODE@bootnode:30303
--min-gas-price=0
--revert-reason-enabled
--metrics-enabled
--metrics-port=9545
ports:
- "8545:8545"
- "30303:30303"
validator2:
image: hyperledger/besu:latest
# similar config with different keys
validator3:
image: hyperledger/besu:latest
# ...
--min-gas-price=0 disables minimum gas fee. In private network transactions are free — standard for corporate networks.
For Kubernetes — Besu Helm chart: helm install besu-network hyperledger/besu.
Member Management (Permissioning)
Besu supports two levels: on-chain (contracts) and off-chain (config files).
On-chain permissioning (recommended for production):
// Allowlist contract deployed in genesis or after
contract NodeRules {
mapping(bytes32 => bool) private allowedNodes;
function connectionAllowed(
bytes32 sourceEnodeHigh,
bytes32 sourceEnodeLow,
bytes16 sourceEnodeIp,
uint16 sourceEnodePort,
bytes32 destEnodeHigh,
bytes32 destEnodeLow,
bytes16 destEnodeIp,
uint16 destEnodePort
) external view returns (bytes32) {
bytes32 nodeId = keccak256(abi.encodePacked(sourceEnodeHigh, sourceEnodeLow));
return allowedNodes[nodeId] ? 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff : 0x0;
}
}
Adding/removing nodes is a network transaction, requiring multisig or governance. Audit trail who and when added node — in blockchain.
Monitoring with Prometheus + Grafana
Besu natively exports metrics to Prometheus format:
# prometheus.yml
scrape_configs:
- job_name: 'besu'
static_configs:
- targets: ['validator1:9545', 'validator2:9545', 'validator3:9545']
Key metrics to monitor:
-
besu_blockchain_height— blockchain height on each node (should be same) -
besu_peers_connected_total— connected peers count -
besu_consensus_round_change_total— QBFT round changes (increase = consensus problems) -
besu_transaction_pool_transactions— mempool size
-- Grafana alert: nodes desynchronized
(max(besu_blockchain_height) - min(besu_blockchain_height)) > 5
BlockScout for block explorer — separate service, compatible with any EVM network.
Disaster Recovery
Private network = your responsibility for backup:
# Backup validator keys (critical!)
tar -czf validator-keys-backup-$(date +%Y%m%d).tar.gz \
./networkFiles/keys/ \
./genesis.json
# Backup blockchain data (for fast recovery)
besu blocks export \
--start-block=0 \
--end-block=$(besu blocks --rpc-http-host localhost:8545 latest) \
--to=blockchain-backup.bin
Validator keys — in different physical locations. Loss of keys = loss of consensus participation. If keys of most validators lost — network stops.
For production: HSM (Hardware Security Module) for validator keys, or AWS CloudHSM / Azure Dedicated HSM.
Typical Errors
Same chainId for test and production. MetaMask and wallets cache networks by chainId. Use different chainIds for each environment.
Gas limit not configured. 0x1fffffffffffff is maximum. In production may need more realistic limit to protect from resource-heavy transactions.
Validators in one datacenter. On datacenter failure lose consensus. Distribute validators across minimum two independent data centers.
No sync monitoring. Silent desynchronization — worst scenario. Alert on height difference > 5 blocks mandatory.
Workflow
Design (2-3 days): network topology, validator count, governance model (who adds/removes participants), permissioning requirements, platform choice.
Genesis and config development (1-2 days): consensus parameters, chainId, initial token distribution, permissioning contracts.
Deploy and testing (3-4 days): Docker Compose or Kubernetes, verify consensus on node failure, test adding/removing participant, load testing.
Monitoring and documentation (1-2 days): Prometheus/Grafana, runbook for operations, backup procedures.
Total 1-2 weeks for basic network. If integration with enterprise systems (Active Directory, HSM, SIEM) needed — add 1-2 more weeks.







