Instantly share code, notes, and snippets.

Embed
What would you like to do?
Setup private Ethereum network with Docker

Setup private Ethereum network with Docker

Ethereum is a decentralized platform that runs smart contracts: applications that run exactly as programmed without any possibility of downtime, censorship, fraud or third party interference.

This is a step-by-step guide, how to setup private Ethereum network. 

We’ll set up a network and create two simple JSON-RPC clients in order to communicate with our Ethereum nodes.

Private blockchains: a fully private blockchain is a blockchain where write permissions are kept centralized to one organization. Read permissions may be public or restricted to an arbitrary extent. Likely applications include database management, auditing, etc internal to a single company, and so public readability may not be necessary in many cases at all, though in other cases public auditability is desired.

To get the difference between Public and Private networks read this article by V. Buterin the author of Ethereum.

I assume you have got hand-on experience with Docker, also you’re knowing Ruby or Nodejs a little. 

You can ask why docker? I don't want to install geth locally.

Copy the source code from my Github repository.

Let’s get started from the Docker container, here is the Dockerfile for our ethereum nodes, we’ll use the geth (go-ethereum):

FROM ubuntu:16.04

LABEL version="1.0"
LABEL maintainer="shindu666@gmail.com"

ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install --yes software-properties-common
RUN add-apt-repository ppa:ethereum/ethereum
RUN apt-get update && apt-get install --yes geth

RUN adduser --disabled-login --gecos "" eth_user

COPY eth_common /home/eth_user/eth_common
RUN chown -R eth_user:eth_user /home/eth_user/eth_common
USER eth_user
WORKDIR /home/eth_user

RUN geth init eth_common/genesis.json

ENTRYPOINT bash

In order to create private network we need the genesis file which must be on both nodes(look in the eth_common/).

To allow nodes to talk to each other, we must create the network between containers, with the docker network it’s pretty easy:

docker network create ETH

Now let’s build two containers:

docker build -t node_one .
# Output omitted
# ...
docker build -t node_two .
# Output omitted
# ....

OK, containers are builded:

docker run --rm -it -p 8545:8545 --net=ETH node_one
eth_user@node_one:~$ ls -a
.  ..  .bash_logout  .bashrc  .ethereum  .profile  eth_common
eth_user@node_one:~$

First one are ready(don’t close terminal), now second one, open an another terminal and run:

docker run --rm -it -p 8546:8546 --net=ETH node_two
eth_user@node_two:~$ ls -a
.  ..  .bash_logout  .bashrc  .ethereum  .profile  eth_common
eth_user@node_two:~$

Second one is also ready.

Note about docker run command line options:

  • -p 8545:8545 in the node_one expose the default RPC port of geth.
  • -p 8546:8546 in the node_two expose port, which will be used later in geth.
  • — net=ETH is a custom docker network, to allow containers communicate each other(because we’re building the cluster).

Inspect docker network(remember IPv4Address, we’ll use it later), run command docker network inspect ETH:

[
    {
        # ..... omitted ....
        "Containers": {
            "50e624e9481765443216eebaa4d0d7ae1dda3497f64eb55d6160632c7b7d0cce": {
                "Name": "cocky_mccarthy",
                "EndpointID": "15a34c3bf61f85cd1e705bf72470e2ac46d370159db59d07715e428518533bba",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            },
            "54d6fecd407b586bd4d0e20422923dd7355a691ce4de8a2050d649d7c9318526": {
                "Name": "nostalgic_raman",
                "EndpointID": "64fdd19a2f50e5fd184afa839b271c122a22d8b965ea7c72240624804c73cf3b",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            }
        },
        # ..... omitted ....
    }
]

At this point we have two docker containers connected each other. Need to generate coinbase account, in order to mine ether we must provide an address to receive reward for found blocks.

node_one:

eth_user@node_one:~$ ./eth_common/setup_account 
Address: {9b40d576bfaa7781e0826a4024b6300566113ce1}
eth_user@node_one:~$

node_two:

eth_user@node_two:~$ ./eth_common/setup_account 
Address: {648a4b24cf769da5467bf9b008dace89d9f65a80}
eth_user@node_two:~$

Here we’re using setup_account shell script, which has been copied early to the docker container.  Time to start minning.

node_one:

eth_user@node_one:~$ geth --identity="NODE_ONE" --networkid="500" --verbosity=1 --mine --minerthreads=1 --rpc --rpcaddr 0.0.0.0 console
Welcome to the Geth JavaScript console!
instance: Geth/NODE_ONE/v1.7.2-stable-1db4ecdc/linux-amd64/go1.9
coinbase: 0x9b40d576bfaa7781e0826a4024b6300566113ce1
at block: 0 (Thu, 01 Jan 1970 00:00:00 UTC)
 datadir: /home/eth_user/.ethereum
 modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
>

node_two:

eth_user@node_two:~$ geth --identity="NODE_TWO" --networkid="500" --verbosity=1 --mine --minerthreads=1 --rpc --rpcport=8546 --rpcaddr 0.0.0.0 console
Welcome to the Geth JavaScript console!
instance: Geth/NODE_TWO/v1.7.2-stable-1db4ecdc/linux-amd64/go1.9
coinbase: 0x648a4b24cf769da5467bf9b008dace89d9f65a80
at block: 0 (Thu, 01 Jan 1970 00:00:00 UTC)
 datadir: /home/eth_user/.ethereum
 modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
>

Note about the command line options to geth:

  •  — identity must be unique identifier of the node
  • — networkid must be the same on the both nodes
  • —verbosity there are many levels 0=silent, 1=error, 2=warn, 3=info, 4=core, 5=debug, 6=detail (default: 3)
  • — mine enable minning
  • — rpc enable RPC
  • — rpcport=8546 change the (default: 8545) RPC port in order to reach container from the host machine

The documentation for all other command line options you can find here

Let’s run some command and check the state of our nodes.

node_one:

> eth.hashrate
0
> eth.blockNumber
0

If both commands returns zero, don’t worry, the DAG is generated. Wait some time and check again, you must see the result(your output might be different):

> eth.hashrate
23458
> eth.blockNumber
20

Let’s check peers, our nodes must detect each other:

> admin.peers
[]

Peers are empty, in order to add peers we must provide a full enode url to the admin.addPeer() method, to get the enode url run command:

node_one:

> admin.nodeInfo.enode
"enode://2468b878bb87****072fae1362fd3448@[::]:30303"

I omit a most part of the enode, your enode url will be other, except the last part [::]:30303.

  • [::]  — means localhost, that’s the enode url of node_one.

Let’s get the second enode from another container: node_two:

> admin.nodeInfo.enode
"enode://4f3c1f87914a68255f9b736aa**ac754d7558ba@[::]:30303"

Ok, at this point we're having enode urls of both containers, let’s add its to each other. In order to do that, we need container IP address, which we have from the output of the docker network inspect ETH command, IPv4Address.

node_one:

#
# enode from node_two with IP address of node_two container
> enode = "enode://4f3c1f87914a68255f9b736aa**ac754d7558ba@172.18.0.3:30303"
> admin.addPeer(enode)
> admin.peers
[{
    caps: ["eth/63"],
    id: "4f3c1f87914a68255f9b736aa**ac754d7558ba",
    name: "Geth/NODE_TWO/v1.7.2-stable-1db4ecdc/linux-amd64/go1.9",
    network: {
      localAddress: "172.18.0.2:41760",
      remoteAddress: "172.18.0.3:30303"
    },
    protocols: {
      eth: {
        difficulty: 70077493,
        head: "0x2d1a50604ea00163728a9ff80824c66105dcfb129a66d218f709ce6bab285249",
        version: 63
      }
    }
}]

node_two:

# enode from node_one with IP address of node_one container
enode = "enode://2468b878bb87****072fae1362fd3448@172.18.0.2:30303
> admin.addPeer(enode)
> admin.peers
[{
    caps: ["eth/63"],
    id: "2468b878bb87****072fae1362fd3448",
    name: "Geth/NODE_ONE/v1.7.2-stable-1db4ecdc/linux-amd64/go1.9",
    network: {
      localAddress: "172.18.0.3:30303",
      remoteAddress: "172.18.0.2:41760"
    },
    protocols: {
      eth: {
        difficulty: 76191910,
        head: "0x1eca2ed57825c8840eca54cf807228f15473e120f65fbaaf6e3b5b79d6d5203d",
        version: 63
      }
    }
}]

That’s good, nodes are seeing each other. Let’s check the block number it must be the same on both nodes(+- 1–2 blocks, because the mining is going fast, your output might be different):

node_one:

> eth.blockNumber
750
> eth.hashrate
60757

node_two:

> eth.blockNumber
753
> eth.hashrate
61494

Don’t close docker containers it’s time to create a script to run commands in our nodes by JSON-RPC.

Here is an example of Ruby script:

#!/usr/bin/env ruby
require "bundler/setup"
require 'json-rpc-client'
EM.run {
  EventMachine.add_periodic_timer(5) { # 5 seconds timeout
    node_one = JsonRpcClient.new('http://localhost:8545/') 
    node_two = JsonRpcClient.new('http://localhost:8546/')
    [
      { name: "NODE_ONE", node: node_one },
      { name: "NODE_TWO", node: node_two } 
    ].each do |node|
      rpc_coinbase = node[:node].eth_coinbase
      rpc_blockNumber = node[:node].eth_blockNumber
      rpc_coinbase.callback do |result|
        puts "#{node[:name]} coinbase: #{result}"
      end
      rpc_coinbase.errback do |error|
        puts error
      end
      rpc_blockNumber.callback do |result|
        puts "#{node[:name]} blocknumber: #{Integer(result)}"
      end
      rpc_blockNumber.errback do |error|
        puts error
      end
    end
  }
}

And a Nodejs example:

var rpc = require('node-json-rpc');
 
var nodeOne = {
  port: 8545,
  host: 'localhost',
  path: '/',
  strict: true
};
var nodeTwo = {
  port: 8546,
  host: 'localhost',
  path: '/',
  strict: true
};
 
var clientNodeOne = new rpc.Client(nodeOne);
var clientNodeTwo = new rpc.Client(nodeTwo);
 
setInterval(()=> {
  // Coinbase
  clientNodeOne.call({"jsonrpc": "2.0", "method": "eth_coinbase", "params": [], "id": 0 },
    function(err, res) {
      if (err) console.log(err)
      console.log("NODE ONE coinbase: " + res.result )
    })
   // blockNumber
  clientNodeTwo.call({"jsonrpc": "2.0", "method": "eth_blockNumber", "params": [], "id": 1 },
    function(err, res) {
      if (err) console.log(err)
      console.log("NODE TWO block number: " + parseInt(res.result, 16) )
    })
  
  // Coinbase
  clientNodeOne.call({"jsonrpc": "2.0", "method": "eth_coinbase", "params": [], "id": 0 },
    function(err, res) {
      if (err) console.log(err)
      console.log("NODE ONE coinbase: " + res.result )
    })
  
  // blockNumber
  clientNodeTwo.call({"jsonrpc": "2.0", "method": "eth_blockNumber", "params": [], "id": 1 },
    function(err, res) {
      if (err) console.log(err)
      console.log("NODE TWO block number: " + parseInt(res.result, 16) )
    })
}, 5000) // 5 seconds timeout

Now run scripts and check out the results(note about 5 seconds timeout):

$> node index.js 
NODE ONE coinbase: 0x718cb4a34cac0fa4e9a37f249340078896b4dfa0
NODE ONE block number: 1467
NODE TWO coinbase: 0x648a4b24cf769da5467bf9b008dace89d9f65a80
NODE TWO block number: 1467
$> ruby client.rb 
NODE_ONE blocknumber: 1533
NODE_TWO coinbase: 0x648a4b24cf769da5467bf9b008dace89d9f65a80
NODE_TWO blocknumber: 1533
NODE_ONE coinbase: 0x718cb4a34cac0fa4e9a37f249340078896b4dfa0

We have builded private ethereum network cluster with the script writen on Ruby and Nodejs, these scripts able to communicate with our cluster using JSON-RPC protocol. It’s a good start to create your blockchain project based on Ethereum. 

References:

Disclaimer

❗️ you use the howto at your own risk..

P.S. forgive me my bad English, it’s not my native language.

Donation

Are you like this tutorial? Buy me a beer and I'll write more tutorials like this one:

BTC - 19SYMA2hqRZHRSL4di35Uf7jV87KBKc9bf ETH - 0xD7cc10f0d70Fd8f9fB83D4eF9250Fc9201981e3a

@robertlayton

This comment has been minimized.

robertlayton commented Dec 27, 2017

An awesome tutorial, covering things that are otherwise hard to piece together from other sources. Thanks!

@panshul007

This comment has been minimized.

panshul007 commented Jan 16, 2018

This is excellent guide. Thanks for the efforts for putting things together. One question: why are you making a custom image instead of using the ethereum official docker image: https://github.com/ethereum/go-ethereum/wiki/Running-in-Docker?

@cdrum

This comment has been minimized.

cdrum commented Jan 18, 2018

@panshul007 my guess is because the default docker image runs a node on the public net. These instructions are for building a private net.

@MaximeAubanel

This comment has been minimized.

MaximeAubanel commented Feb 2, 2018

Hello, I don't understand why we need to manually add peers with admin.addPeer(enode). I mean, they are supposed to be already in the same network, why do I need to link them between each other/with the first container ?

@civilbots

This comment has been minimized.

civilbots commented Feb 12, 2018

Thanks a Ton for sharing your work. I'm sure that it saved lot of my time. 👍

@nikitha12

This comment has been minimized.

nikitha12 commented Mar 7, 2018

I am getting this error when i run the index.js script. My geth instances are up but the blocks are a lot of time to sync.
image
Please help me out with this. Thanks in advanced

@toicodedoec

This comment has been minimized.

toicodedoec commented Mar 23, 2018

How can i "geth attach"?
This CLI is always fail with [not found geth.ipc file]

@0xngoubou

This comment has been minimized.

0xngoubou commented May 3, 2018

eth.hashrate alway = 0, pls tell me why?
(I run it on server google)

@kingpinXD

This comment has been minimized.

kingpinXD commented Jul 4, 2018

After creating the network how can I deploy smart contracts to it ?

@kingpinXD

This comment has been minimized.

kingpinXD commented Jul 5, 2018

I dont get hte point behind building two images ,cant we just make two containers out of the same image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment