By:  Omar Metwally, MD


  1. To create a private Ethereum network
  2. To deploy a simple contract to your private Ethereum network
  3. To interact with a contract on your private Ethereum network

Target audience:  

Ethereum developers.


The need for this tutorial arises from outdated Ethereum documentation, resulting in hair-pulling, heartburn, and insomnia.


Isolate your development environment, and your private Ethereum network, from any real Ether you might have. It’s easy to compromise your machine and lose real money if you slip up.

Part Two

After you finish this tutorial, check out Part Deux: How to connect 3+ nodes in a private Ethereum network.

Step 1: Set up a virtual server and install Ethereum command-line tools

Many tutorials guide you through deploying contracts using the Ethereum wallet GUI. I’m using the Go Ethereum client (geth) and encourage others to learn how to use the command line interface (CLI). The better you understand the Ethereum client’s internal workings and the anatomy of a blockchain, the more power to you. It doesn’t matter which hosting service you use,  but these instructions assume you’re running an Ubuntu server. [Geth Installation instructions for Ubuntu] 

My private network is called “UCSFnet” at this (fake) IP address

Very important: make sure you set aside at least 2GB RAM on your virtual server in order for mining to work. If you skimp on RAM, mining may not work.

Now open a new terminal window, log into your Ubuntu server via SSH and create your working directory called “ucsfnet”

ssh root@
mkdir ucsfnet
cd ucsfnet
mkdir data
mkdir source

Step 2: Create genesis block

The genesis block is the first block of any blockchain and its parameters are specified in “genesis.json”, which I saved under /root/ucsfnet/genesis.json. My genesis block looks something like this:

"config": {
        "chainId": 15,
        "homesteadBlock": 0,
        "eip155Block": 0,
        "eip158Block": 0

  "alloc"      : {
  "0x0000000000000000000000000000000000000001": {"balance": "111111111"},
  "0x0000000000000000000000000000000000000002": {"balance": "222222222"}

  "coinbase"   : "0x0000000000000000000000000000000000000000",
  "difficulty" : "0x00001",
  "extraData"  : "",
  "gasLimit"   : "0x2fefd8",
  "nonce"      : "0x0000000000000107",
  "mixhash"    : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "timestamp"  : "0x00"

Note that chainId=1 refers to the main Ethereum network, so it’s important to create a unique chainId for your network so your client doesn’t confuse your private blockchain with the main network. For the sake of illustration and testing, set mining difficulty to a low value. Also make sure you specify a unique nonce to start out with. The alloc field allows you to pre-populate accounts with Ether.

Now navigate to the directory where you created your genesis.json file and initialize the bootnode, a node through which your Ethereum client can join your private network and interact with other nodes connected to your private network.

cd /root/ucsfnet/data
geth --datadir=/root/ucsfnet/data init /root/ucsfnet/genesis.json
bootnode --genkey=boot.key
bootnode --nodekey=boot.key

Pay attention to the enode address that the previous commands results. You’ll need this for the next step.

Step 3: Connect to your externally reachable bootnode

The bootnode you created in the previous step is externally reachable, and in this step we’re going to connect to that node to create a one-node network. As you go through this tutorial and expand your knowledge of Ethereum networking, you’ll be able to add infinitely many nodes to your private network. The following commands create the geth.ipc file you’ll need to interact with your network in the subsequent steps. However after completing this tutorial, you’ll be able to skip this step the next time you connect to your network if you’re developing/testing and not using any networking protocols.

Open a new terminal window:

ssh root@

geth --datadir=/root/ucsfnet/data --bootnodes=enode://148f3....@

Replace 148f3…. above with the enode address from Step 2. Make sure to include the enode://  prefix.

Step 4: Create a new account and check your balance

Start by opening a new terminal window and connecting to your virtual server via SSH:

ssh root@
geth attach /root/ucsfnet/data/geth.ipc 
> eth.accounts

You’ll notice that there are no account addresses yet. Now you’ll create your first account. Be sure to replace “mypassword” with a strong password. Remember it and keep it safe; Ethereum is unforgiving in that there is no way to unlock your account if you forget your password!

> personal.newAccount("mypassword")
> web3.fromWei(eth.getBalance(eth.accounts[0]), "ether")

Remember your account’s address, which is prefixed by “0x”. Notice that your account has zero Ether, but don’t be discouraged. I’ll soon turn you into an Ethereum billionaire (but alas, the Ether you collect on your private network is only good on your private network).

Step 5: Mine on your private network

The purpose of mining here is two-fold. First you create Ether which you’ll need to power your transaction through gas (an Ethereum sub-unit). Second, mining incorporates your transactions into the blockchain. Open a new terminal window and connect to your private server:

ssh root@
geth --datadir=/root/ucsfnet/data --mine --minerthreads=1 --etherbase=0x...

The etherbase parameter specifies the address which should receive the Ether you generate by mining. This should contain your wallet address from Step 4. If you check your account balance again (Step 4), you’ll find that you quickly became a billionaire. Congratulations! Again, the Ether you generate on your private network is only good on your private network, but that’s OK. Your newfound knowledge of how to develop for Ethereum is more valuable than all the Ether in the world.

Step 6: Compile a simple contract

Unfortunately, the official Ethereum documentation has not been updated to reflect the fact that compiling using the solC compiler is no longer possible via RPC. This means that you will end up in a rabbit hole if you try to follow the instructions on the official Ethereum documentation and many other tutorials based on official documentation. There are 2 ways to compile contracts, and I encourage you do try both so you understand what’s going on under the hood.

First install the command-line compiler, solC:

sudo add-apt-repository ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install solc

Now open a new terminal window, connect to your server via SSH, and navigate to the directory where your source code will live:

cd /root/ucsfnet/source

And save this demo “greeter” contract in /root/ucsfnet/greeter.sol:

contract mortal {

/* Define variable owner of the type address*/
 address owner;

/* this function is executed at initialization and sets the owner of the contract */
 function mortal() { owner = msg.sender; }

/* Function to recover the funds on the contract */
 function kill() { if (msg.sender == owner) selfdestruct(owner); }

contract greeter is mortal {
 /* define variable greeting of the type string */
 string greeting;

/* this runs when the contract is executed */
 function greeter(string _greeting) public {
 greeting = "Hello, World!";

/* main function */
 function greet() constant returns (string) {
 return greeting;

Now compile this contract using solC:

solc --bin --abi  -o /root/ucsfnet/source /root/ucsfnet/source/greeter.sol 

In the above command, the bin and abi flags tell the solC compiler to generate Ethereum Virtual Machine (EVM) bytecode and an Application Binary Inferface (ABI) file, respectively. The o flag defines an output directory where these files will be created, and the last argument is the location of the contract (*.sol file) you want to compile.

Compiling a contract this way generates two files:

EVM file, indicated by the *.bin extension. This corresponds to the contract bytecode generated by the web-based compiler, which is easier to use and will be described below.

ABI file, indicated by an *.abi extension. An application binary interface is like an outline or template of your contract and helps you and others interact with the contract when it’s live on the blockchain.

Open both files using vim or your text editor of choice to see what they contain. Understanding what these files contain will make deploying and interacting with your contracts much clearer.

Alternatively, you can use the online compiler. This is easier because you can just copy-paste your Solidity code. However both methods are equivalent, and I’ll elaborate on this below. Copy and paste the code in greeter.sol (above) into the online compiler. Give it a second, and then click on the “Contract details…” link. Notice that the contents of the Bytecode field are equivalent to the greeter.bin file that you created using solC. Also notice that the Interface field is equivalent to the contents of the greeter.abi file you created when you compiled using the command-line solC compiler.

Step 7: Deploy a “Greeter” contract to your private network

Copy the contents of the Web3 deploy field in the online compiler. Paste them into a text editor on your computer and note the bolded changes I made:

var _greeting = 'UCSFnet lives!';

var browser_ballot_sol_greeterContract = web3.eth.contract([{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"greet","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"inputs":[{"name":"_greeting","type":"string"}],"payable":false,"type":"constructor"}]);

var browser_ballot_sol_greeter =



    from: web3.eth.accounts[0],

    data: '0x6060604052341561000f57600080fd5b6040516103dd3803806103dd833981016040528080518201919050505b5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b6040805190810160405280600d81526020017f48656c6c6f2c20576f726c642100000000000000000000000000000000000000815250600190805190602001906100b99291906100c1565b505b50610166565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061010257805160ff1916838001178555610130565b82800160010185558215610130579182015b8281111561012f578251825591602001919060010190610114565b5b50905061013d9190610141565b5090565b61016391905b8082111561015f576000816000905550600101610147565b5090565b90565b610268806101756000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806341c0e1b514610049578063cfae32171461005e575b600080fd5b341561005457600080fd5b61005c6100ed565b005b341561006957600080fd5b61007161017f565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100b25780820151818401525b602081019050610096565b50505050905090810190601f1680156100df5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561017c576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b5b565b610187610228565b60018054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561021d5780601f106101f25761010080835404028352916020019161021d565b820191906000526020600020905b81548152906001019060200180831161020057829003601f168201915b505050505090505b90565b6020604051908101604052806000815250905600a165627a7a7230582069d50e4318daa30d3f74bb817c3b0cb732c4ec6a493eb108266c548906c8b6d70029',

    gas: '1000000'

  }, function (e, contract){

   console.log(e, contract);

   if (typeof contract.address !== 'undefined') {

        console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);



Save the above as myContract.js.

Before we proceed, make sure your account balance is non-zero and that your account is unlocked. If your balance is too low, or if your account is locked, you won’t be able to deploy your contract. Follow the above steps to make sure you generate Ether by mining, and unlock your account as follows (replacing mypassword with the password you invented while creating your account):

Open new terminal window:

ssh root@ 
geth attach /root/ucsfnet/data/geth.ipc 
> web3.fromWei(eth.getBalance(eth.accounts[0]), "ether")
> personal.unlockAccount(eth.accounts[0], "mypassword")

If I’ve lost you, pay attention here because this is important. This is where the Ethereum documentation hasn’t been updated yet, creating confusion regarding how contracts are compiled. Note that the web3.eth.contract() function takes as an argument an ABI. This is the same as the greeter.abi file you created using the solC compiler! Also note that the data field is equivalent to the greeter.bin EVM bytecode, with the exception that it’s prefixed by “0x”. 

Time to deploy your contract:


You should see something like:

Contract mined! address: 0x4000737c8bd7bbe3dee190b6342ba1245f5452d1 transactionHash: 0x0a4c798467f9b40f2c4ec766657d0ec07c324659ea76fcc9c8ad28fc0a192319

Congratulations! Your contract lives at the following address on your private blockchain:


Make a note of this. You’ll need your contract’s address to interact with it (and allow others to do the same).

If this didn’t work, make sure you’re actively mining in another window so that your transaction is incorporated into the blockchain.

Step 8: Interacting with a contract

Again, this is another place where the Ethereum documentation hasn’t yet been updated and refers to deprecated functions. With your geth client fired up, try the following:

> var abi = '[{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"greet","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"inputs":[{"name":"_greeting","type":"string"}],"payable":false,"type":"constructor"}]'
> var abi = JSON.parse(abi)
> var contract = web3.eth.contract(abi)
> var c ="0x4000737c8bd7bbe3dee190b6342ba1245f5452d1")
> c.greet()

The first 3 inputs above define what the contract looks like via its ABI. The method takes as an argument the address where your contract lives.

You should see the following output:

UCSFnet lives! 

Big caveat: geth chokes if any of your inputs contain illegal characters. Id est:

“ (illegal) is not equal to  " (legal) 


‘ (illegal) is not equal to ' (legal) 


We covered a lot of ground here, from setting up your own Ethereum network to compiling your contract (and avoiding rabbit holes created by outdated documentation) to deploying the contract and interacting with it. Whew! If you ever get stuck and need to start over, just reboot your server and delete your blockchain (understanding the implications of what this means…):

rm -R /root/ucsfnet/data/geth/chaindata

“Wir werden von so manchen Despoten – etwa Institutionen, Überzeugungen oder auch Neurosen – beherrscht, die wir aus dem Weg räumen können, wenn wir sie analysieren und verstehen.”

We are ruled by so many despots — institutions, convictions, and neuroses — which we can clear away, if we analyze and understand them

— Isaiah Berlin

Portland smoke

Support my research!

If you find this information helpful, please consider supporting my research by donating Ether or Bitcoin. Equitable, transparent, high-quality healthcare for all!

ETH donations:  0x1b4Ba9c1811233805dC4f12Ca32F22cfD85DB212

BTC donations:  1Ef7oYi1zVpA4bw7hUcD8841wbdtCw4Xen

Suggested reading:

  • Nice low-level description of networking in Ethereum (uses the Python-based client): 

  • Setting up private network or local cluster

  • Connecting to the network

  • The Go Ethereum client

  • Official Ethereum command line tool documentation

11 thoughts on “How to create a private Ethereum network

  1. Hey, very noob here, I’m trying to reproduce using a Virtualbox VM.
    When I run step2, no ipc file is created. It says:

    No etherbase set and no accounts found as default
    INFO [07-26|10:47:40] Allocated cache and file handles database=/home/wout/amainet/data/geth/chaindata cache=16 handles=16
    INFO [07-26|10:47:40] Successfully wrote genesis state database=chaindata hash=f53b09…6e4acd
    INFO [07-26|10:47:40] Allocated cache and file handles database=/home/wout/amainet/data/geth/lightchaindata cache=16 handles=16
    INFO [07-26|10:47:40] Successfully wrote genesis state database=lightchaindata hash=f53b09…6e4acd

    If I just run geth it creates the .ethereum folder with an .ipc file in my home dir which I guess is not right either.

    1. I guess what’s missing is a command “geth –datadir /home/wout/amainet/data” which should be running in background

    2. Thank you for your feedback, Wouter. I’ve updated the tutorial by adding Step 3, which describes the processes of connecting to the bootnode and creating the geth.ipc file. It’s important to not confuse the geth.ipc file in your data directory with the default one in the home directory. That’s why it’s important to always specify the datadir flag. Cheers

  2. That was the solution, might want to include that in your post. Some more points:
    wouldn’t it be better to mention testrpc/truffle in this post? Following this post now requires a lot of copy paste work of addresses and so forth.

    Also, I first got an error because the mining wasn’t going fast enough, even with the current settings…I’m still too noob to understand how to boost it but might be a good adjustment too.

    Anyway, thanks a lot, I’ve spent 4 consecutive nights following tutorials that didn’t work, as well as crashes on Azure deployments. This was the first tutorial that actually worked.

    Any pointers on IDE for linux : Atom, Remix?

    1. Call me old school but I like good old command line and vim. No better way to really understand what’s going on and have total control. I didn’t like Azure or other 3rd party IDEs — you don’t know what’s going on under the hood and they’re expensive. Also when something goes wrong or the code base is changed, you’re left in the dark unless you’ve been studying the geth/pyethapp/code base.

  3. “The need for this tutorial arises from outdated Ethereum documentation, resulting in hair-pulling, heartburn, and insomnia.”

    Love it. So true. In fact so true squared. I’ve been there.

    I’m looking forwards to following your tutorial, but I saw that and had to comment.

  4. Hi, I am very curious about this. But sadly am a noob at this whole etherium/bitcoin investing. I noticed you did not have a “purpose” section above, you did have a “necessity” section; but I do not believe that answers my question as to the purpose of doing this and going through this process.
    Is the purpose to help create a local “false” etherium miner” to help us mine the actual etherium coins? What is the purpose in that context? Hope my question was clear and not too vague. Thanks for your answer.

  5. I encounter the problem at miner “Failed to store last header’s hash err=”leveldb/table: corruption on data-block (pos=291652): checksum mismatch, want=0xf1330ff3 got=0xcf893eec [file=001060.ldb]” at step 5
    What I am using is bellow:

    tony@ubuntu:~/geth$ geth version
    Version: 1.7.1-stable
    Git Commit: 05101641455a754936acc5ddff92f35f5e33181c
    Architecture: amd64
    Protocol Versions: [63 62]
    Network Id: 1
    Go Version: go1.9
    Operating System: linux

    Any advise? Many thanks!

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s