How to Set Up an Ethereum Private Chain and Deploy Smart Contracts

·

Setting up an Ethereum private chain is a powerful way to experiment with blockchain technology, test smart contracts, and simulate decentralized applications in a controlled environment. Whether you're a developer exploring Ethereum's core mechanics or building a proof-of-concept dApp, a private network offers full control over consensus rules, gas pricing, and account management — all without the cost or unpredictability of the public mainnet.

This comprehensive guide walks you through every step: from compiling Geth to initializing a genesis block, creating accounts, mining Ether, sending transactions, and deploying and interacting with a smart contract using Solidity.


Step 1: Download the Geth Client

To begin, you’ll need the Go implementation of Ethereum — Geth. It allows you to run a full Ethereum node and interact directly with the blockchain.

Clone the official repository:

git clone https://github.com/ethereum/go-ethereum.git

This command downloads the latest version of the Ethereum protocol written in Go, giving you access to essential tools like geth, the command-line interface for managing your node.

👉 Learn how blockchain nodes power decentralized networks.


Step 2: Compile Geth

Navigate into the project directory and compile the geth binary:

cd go-ethereum
make geth

After compilation completes, the executable will be located at ./build/bin/geth. For convenience, add this path to your system’s PATH environment variable so you can run geth from anywhere.

This self-built version ensures you're working with a clean, customizable Ethereum client — ideal for development and testing.


Step 3: Configure the Genesis Block

The genesis block defines the initial state of your private blockchain. All chain parameters — including chain ID, difficulty, gas limit, and pre-funded accounts — are set here.

Create a file named genesis.json with the following configuration:

{
  "config": {
    "chainId": 7777,
    "homesteadBlock": 0,
    "eip150Block": 0,
    "eip155Block": 0,
    "eip158Block": 0,
    "byzantiumBlock": 0,
    "constantinopleBlock": 0,
    "petersburgBlock": 0,
    "ethash": {}
  },
  "difficulty": "1",
  "gasLimit": "8000000",
  "alloc": {
    "7df9a875a174b3bc565e6424a0050ebc1b2d1d82": { "balance": "300000" },
    "f41c74c9ae680c1aa78f42e5647a62f353b7bdde": { "balance": "400000" }
  }
}

Key points:

Initialize the blockchain with:

geth init --datadir data genesis.json

This creates a new blockchain data directory (data) based on your custom genesis file.


Step 4: Launch Your Ethereum Node

Start your private node with mining enabled and peer discovery disabled (to keep it isolated):

geth --datadir data --nodiscover --mine console

You’ll enter the interactive JavaScript console where you can execute Ethereum commands. The --mine flag enables built-in mining, while --nodiscover prevents unwanted connections from external nodes.

Your private Ethereum network is now live.


Step 5: Create Accounts

Inside the Geth console, generate a new Ethereum account:

> personal.newAccount()
Passphrase:
Repeat passphrase:
"0x45281eb7bbaa41fef62d40ae881378c14335c1cf"

Securely store both the password and keystore file path shown in the warning message. Losing them means losing access to your funds.

List all available accounts:

> personal.listAccounts
["0x45281eb7bbaa41fef62d40ae881378c14335c1cf", "0x96b638097bb4dd40d9bca6f1197b8d24b1705cfa"]

These accounts exist only on your private chain but follow standard Ethereum address formats.


Step 6: Start Mining Ether

Begin mining to generate Ether for your accounts:

> miner.start()

You’ll see logs indicating new blocks being sealed. To stop mining:

> miner.stop()

Check an account’s balance:

> web3.fromWei(eth.getBalance("0x45281eb7bbaa41fef62d40ae881378c14335c1cf"), "ether")
196

Mining rewards accumulate in the coinbase (default mining) address — typically your first account.


Step 7: Send Transactions Between Accounts

Before sending Ether, unlock the sender account:

> personal.unlockAccount(eth.accounts[0])
Passphrase:
true

Send 10 Ether to the second account:

> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(10, 'ether')})

The transaction hash confirms submission. Mine one block to confirm it:

> miner.start(1); admin.sleepBlocks(1); miner.stop();

Verify the recipient balance:

> web3.fromWei(eth.getBalance(eth.accounts[1]), 'ether')
10

You’ve successfully executed your first cross-account transfer.


Step 8: Write and Compile a Smart Contract

Create a simple Solidity contract (demo.sol) that stores and retrieves a number:

pragma solidity >=0.7.0 <0.9.0;

contract SimpleStorage {
    uint storedData;

    function set(uint x) public {
        storedData = x;
    }

    function get() public view returns (uint) {
        return storedData;
    }
}

Compile it using Solc:

solc --optimize --combined-json abi,bin demo.sol

This outputs ABI (interface) and bytecode (bin), which are required for deployment.


Step 9: Deploy and Interact With the Smart Contract

Unlock the deploying account:

> personal.unlockAccount("0x45281eb7bbaa41fef62d40ae881378c14335c1cf")

Parse the compilation output and prepare deployment:

var storageContractJson = /* paste full solc output */
var storageContract = eth.contract(storageContractJson.contracts["demo.sol:SimpleStorage"].abi)
var storageContractObj = {
  from: eth.accounts[0],
  data: "0x" + storageContractJson.contracts["demo.sol:SimpleStorage"].bin,
  gas: 1000000
}

Deploy the contract:

var storageContractIns = storageContract.new(storageContractObj)

Mine the transaction:

miner.start(1); admin.sleepBlocks(1); miner.stop();

Retrieve the deployed contract address:

var storageContractInsAddress = eth.getTransactionReceipt(storageContractIns.transactionHash).contractAddress

Reconnect to the contract instance:

var storageContractIns = storageContract.at(storageContractInsAddress)

Test interaction:

> storageContractIns.set.sendTransaction(77, {from: eth.accounts[0], gas: 1000000})
// Mine again
> storageContractIns.get.call()
77

Your smart contract is now live and functional on your private Ethereum chain.

👉 Explore how smart contracts enable trustless automation.


FAQ Section

Q: Why use a private Ethereum chain?
A: A private chain lets developers test dApps, smart contracts, and network configurations safely and cost-effectively without relying on public testnets or spending real Ether.

Q: Can I connect MetaMask to my private chain?
A: Yes. Add a custom RPC network in MetaMask using your node’s URL (e.g., http://localhost:8545) and import accounts via private keys or JSON files.

Q: What is the purpose of the genesis.json file?
A: It defines the starting state of your blockchain — including initial accounts, balances, consensus settings, and protocol versions — ensuring all nodes agree on the chain’s foundation.

Q: How do I stop my Geth node gracefully?
A: Press Ctrl+C in the terminal or run exit in the console. This safely shuts down the node and flushes data to disk.

Q: Is mining necessary on a private chain?
A: Yes, if you’re using Proof-of-Work (Ethash). Mining creates new blocks and rewards. Alternatively, you can switch to Proof-of-Authority (e.g., Clique) for faster block times without heavy computation.

Q: Can I reuse my private chain after restarting Geth?
A: Absolutely. As long as you keep the --datadir folder intact, your accounts, contracts, and blockchain history persist across sessions.


By following these steps, you now have a fully functional Ethereum private network — perfect for learning, development, or enterprise use cases requiring isolated blockchain environments.

Whether you're testing gas optimization strategies or simulating multi-node interactions, mastering private chains is a foundational skill in blockchain engineering.

👉 Discover tools that accelerate blockchain development workflows.