How to run a private network of the NEO blockchain

Chris Hager
Proof of Working
Published in
8 min readNov 6, 2017

Running a private network of the NEO blockchain is an integral part of working with the blockchain, in particular for writing and testing dApps and smart contracts. A private network is a complete NEO blockchain for yourself, isolated from the public networks. You can spin it up quickly, claim the initial 100 million NEO, and experiment with all aspects of it.

This post is a step-by-step guide on setting up a private chain with Docker and Python on Mac, Linux and Windows, and an introduction to neo-python and neo-privatenet-docker.

Update 2017–11–20: There is now a turnkey Docker image with a pre-build private network and a wallet with 100m NEO and 16.6k GAS available here: If you are unsure what to use, use this ready-to-go image instead of building it yourself! You can even easily run it with neoscan.

Note for Windows users: You’ll need to use the Linux subsystem because LevelDB does not easily work natively on Windows.

NEO itself and the native tooling is primarily developed with C# using Windows. The official NEO documentation on private chains gives a good overview how to setup a private network with a Windows toolchain and 4 Windows virtual machines as consensus nodes.

Thanks to the monumental efforts of the City of Zion community (CoZ), running a private chain is possible on any platform with Docker and Python, with minimal system requirements. The two specific projects we are going to use are neo-python and neo-privatenet-docker:

  • neo-python — allows us to run a full NEO node and to interact with the blockchain with Python 3.
  • neo-privatenet-docker — allows us to run a whole NEO blockchain with 4 consensus nodes in a single, lightweight Docker container

At this point I also want to mention neo-js, a NEO node implementation in JavaScript, with several features in the pipeline. neo-js is not quite as mature as neo-python, but already supports all RPC calls, and you can run a full local node, saving the blockchain data into a MongoDB database.

Setting up a private NEO chain, step-by-step

The steps are the following: Docker setup → neo-privatenet-dockerneo-python → connect to the private net and create a wallet → claim the initial 100,000,000 NEO.

Docker Setup

Run the private chain Docker container

  • Clone neo-privatenet-docker, then build and run the Docker container with the four initial consensus nodes:
$ git clone
$ cd neo-privatenet-docker
$ ./
$ ./

This will take a while. It creates a new Docker image with 4 consensus nodes, creates a wallet, transfers the initial 100m NEO to this wallet (by creating a transaction signed by 3 of the 4 consensus nodes, and waits for the blockchain to confirm the transaction), waits a moment and finally claims an initial amount of 40 GAS. The final output looks similar to this:

All done!
- WIF key: L1nqvvVGGesAQ5vLyyR21Q2gVt4ifw8ZrKGJa58tv9xP7hGa2SMx
- Wallet file: /tmp/wallet
- Wallet pwd: coz
Shutting down. This may take a bit...
Copying wallet file and wif key out of Docker container...--------------------All done! You now have 2 files in the current directory:neo-privnet.wallet .. a wallet you can use with neo-python (pwd: coz)
neo-privnet.wif ..... a wif private key you can import into other clients
  • At this point, the Docker container running the consensus nodes is running in the background. You can see it with $ docker ps:
$ docker ps
6f7ed0d4ae3a neo-privnet "/bin/bash /opt/pr..." 2 hours ago Up 2 hours>20333-20336/tcp neo-privnet
  • If you encounter an error like Get unauthorized: incorrect username or password, you may need to login to Docker hub $ docker login with your user-id (not email).
  • When the Docker container stops or restarts, all state is deleted (the whole ‘old’ blockchain will be gone), and you should also remove Chains/privnet from neo-python and any privnet wallets you created.


Now we can connect to the private network with neo-python, and either use the wallet that is produced in the step above, or import the WIF key into a custom wallet.

$ python -p
NEO cli. Type 'help' to get started
  • The number of blocks should be increasing continuously (eg. ‘Progress 0/1’)
  • Open the wallet:
neo> open wallet neo-privnet.wallet
password> coz
neo> wallet rebuild
  • At this point, you have 100m NEO in your wallet and you can do as you please. Check the balance with neo-python:
neo> wallet
Script hash b'i\xd5r\xef\\\xf2=\xb32\xb7\xffe\xa4\t@6\xdbM\x1b\xb7' <class 'bytes'>
Wallet {
"percent_synced": 100,
"path": "privnet",
"addresses": [
"votes": [],
"balances": {
"c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b": "100000000.0"
"version": 0,
"is_watch_only": false,
"frozen": false,
"script_hash": "ARRUJLMrTipDfvzUGHNJ3qAAcQRSdtUcEX"
"public_keys": [
"Public Key": "02c017615119d81f4b3ab7ddb016e22e3313cacb6ab5cf2ef38addd752ac1b3d9f",
"Address": "ARRUJLMrTipDfvzUGHNJ3qAAcQRSdtUcEX"
"synced_balances": [
"[NEO]: 100000000.0 "
"height": 402

At this point you now have a private NEO chain up and running and full control over the initial funds. Happy hacking!

How to restart and reset the private network

The Docker container does not persist any state. If it stops or restarts, all state is lost and the whole blockchain will be newly created at the next start.

It’s important to remove the old chain files from neo-python (Chains/privnet), else you’ll run into problems running the old database against another chain.

neo-python$ rm -rf Chains/privnet/

You can manually restart the private-net container by running ./ This stops the neo-privnet container if it is running, and starts a fresh one:

$ ./
Stopping container named neo-privnet
Removing container named neo-privnet
Starting container...

Claiming the initial GAS

neo-python does not yet support claiming GAS. You can claim it with the official NEO tools as described in the NEO private chain docs beginning with step 5.

Docker Tips & Tricks

Here are a few often-used Docker commands:

  • docker ps lists all running instances, docker ps -a also all stopped ones - docs
  • docker images lists all your local images from which a container can start from - docs
  • docker exec -it {container-id} /bin/bash opens a bash interface into the running container - docs
  • docker stop {container-id} stops the container - docs
  • docker rm -f {container-id} stops and deletes the container - docs
  • docker run to start a new container - docs

NEO TestNet

The NEO project also provides a running TestNet, but this should be only used in the final stages of development (for a variety of reasons).


neo-python is an amazing project, a community effort to re-implement the original C# NEO project in Python! It has come a long way and already supports the following functionality:

  • Running a Python based NEO P2P node
  • Interactive CLI for configuring node as well as inspecting and interacting with the blockchain
  • Executing smart contracts on the blockchain in a Python virtual machine
  • Very basic Wallet functionality (not fully tested, please do not use on mainnet)

neo-python has been forked from a “very” preliminary Python SDK, but development really started in July 2017, with constant contributions since then:

As of November 3, 2017, neo-python has 18 contributors, and is spearheaded by localhuman.

If you want to work with the NEO blockchain on a platform other than Windows, you’ll most certainly interact with neo-python in one way or the other. It’s an integral part of the cross-platform development toolchain, actively developed, and just overall a great project.

I just want to send a big thank-you to all the individuals who contributed to making this possible! ♥️ I’m excited about the bright future of this project and it’s further development.


Let’s take a closer look at neo-privatenet-docker, which allows us to easily run a private NEO blockchain within an Ubuntu Docker image. A private chain requires at least 4 consensus nodes, which are all running inside this single Docker container. The main contributors are hal0x2328 and phetter.

Let’s start with the Dockerfile:

FROM ubuntu:16.04ENV DEBIAN_FRONTEND noninteractiveRUN apt-get update && apt-get upgrade -y
RUN apt-get install -y apt-utils
RUN apt-get install -y mininet netcat curl wget unzip less python screen
RUN apt-get install -y ca-certificates apt-transport-https
RUN apt-get install -y libleveldb-dev sqlite3 libsqlite3-dev
RUN apt-get install -y expect
RUN curl | gpg --dearmor > /etc/apt/trusted.gpg.d/microsoft.gpg
RUN echo "deb [arch=amd64] xenial main" > /etc/apt/sources.list.d/dotnetdev.list
RUN apt-get update && apt-get install -y dotnet-sdk-2.0.0RUN wget -O /opt/
  • The image is based on Ubuntu 16:04
  • Various system utilities are installed, including LevelDB, a fast key-value storage from Google, and sqlite3, a self-contained, serverless, zero-configuration, transactional SQL database engine.
  • The Microsoft apt repositories are setup, and the dotnet-sdk-2.0.0 is installed. Remember, the original NEO project is implemented in C# and runs on .NET Core.
  • The the neo-cli Ubuntu release is downloaded

That’s pretty much the whole setup. After this, neo-cli is extracted 4 times (once for each consensus node), and the private chain startup files are copied:

RUN unzip -d /opt/node1 /opt/
RUN unzip -d /opt/node2 /opt/
RUN unzip -d /opt/node3 /opt/
RUN unzip -d /opt/node4 /opt/
ADD ./scripts/ /opt/
ADD ./scripts/ /opt/

When you run the Docker container with, it starts the container with 4 open ports (20333-20336, mapped to the host), and invokes the script:

docker run -d --name neo-privnet -p 20333-20336:20333-20336/tcp -h neo-privnet neo-privnet /bin/bash /opt/ /opt/ in turn starts 4 NEO nodes by executing four times:

#!/usr/bin/expect -f
set dnpath [lindex $argv 0]
set wallet [lindex $argv 1]
set password [lindex $argv 2]
set timeout -1
cd $dnpath
spawn dotnet neo-cli.dll
expect "neo>"
send "open wallet $wallet\n"
expect "password:"
send "$password\n"
expect "neo>"
send "start consensus\n"
expect "OnStart"

This code sets a few variables from the arguments (dnpath, wallet, password), runs dotnet neo-cli.dll, opens the initial node wallet and calls start consensus in order for the nodes to start working. At this point, blocks in the private chain are being created.

That’s pretty much it! And even though it doesn’t look like much code, it’s a life-saver for getting started with NEO blockchain development and running a private chain, because it makes it just so easy and accessible. Again a big thank-you to the developers driving this project!

I would suggest that you try it out right now! Just follow the getting started guide above and you will have your own private NEO chain running in no time.

Feel free to join the NEO Slack and say hi, it’s an open and welcoming community! You can also find links to various other resources on the official NEO homepage at

If you have feedback or comments regarding this post, please reach out to the author via @metachris.