Skip to main content

Set up a network

In this topic, you'll learn how to set up your own blockchain network by studying a concrete example. You can set up a private network for development and testing. It provides an easy way to develop and test your dapps. You might also want to create a private network for learning and understanding the Chromia blockchain network.

The Postchain Management Console (PMC) creates and interacts with the network. This guide uses an example project that lets you quickly start a local network and manage multiple providers. You can expand the network by adding more providers, nodes, and clusters.

The network provided by this example contains four providers and three nodes. In a public Chromia network, a provider is represented by a person or an organization. However, in this case, the providers are represented by different key pairs on your machine that are specified in the PMC config. It means that to run commands by a specific provider; the command must be executed with the PMC config of that provider.

The first provider is the initializer of the network. The first node in the network is called the genesis node and hosts the management chain (BC0). When the network is initialized, the initial provider can add more providers to the network. Providers can belong to different provider tiers, which define what changes they can make to the network.

Some actions on the network need consensus among the providers in the network. A proposal requests such actions from a provider. The system node tier providers then vote on the proposals to reach a consensus.

Get example

Download directory1-example-0.2.8-dist.tar.gz from here.


This sample contains:

├── app
│   ├── config.yml
│   └── src
│   └── main.rell
├── chromia-cli-0.16.0-dist.tar
├── config
│   ├──
│   ├──
│   ├──
│   ├──
│   ├──
│   ├──
│   ├──
│   ├──
│   ├──
│   ├──
│   ├──
│   ├──
│   └──
├── icmf
│   ├── receiver
│   └── sender
├── directory-chain-1.35.0-sources.tar.gz
├── docs
│   ├──
│   ├──
│   ├──
│   ├──
│   ├──
│   ├──
│   └── single-provider.mdx
├── management-console-3.21.0-dist.tar.gz
├── provider
│   ├── alpha
│   ├── beta
│   ├── delta
│   └── gamma
└── scripts
├── find-ip
└── wipe-db
  • app is the city tracker, which can be deployed to the network.
  • config contains node configurations for four nodes. By default, they're pointing to docker.db.- properties which uses host.docker.internal:5432. Update this path to the actual database that you are using.
  • provider contains configurations for four providers. They're all pointing toward node 0-3, respectively. Change this using the pmc config command.
  • scripts contain utility scripts.
  • unpacks all tarballs and generate blockchain configurations.

We recommend installing direnv to access all scripts in the scripts folder and the management console directly from the path.


  • Docker
  • direnv (this isn't mandatory, but the examples assume that all scripts are available on this path)

If you are on a Mac, it's recommended to install coreutils to ensure that the scripts in this distribution is working correctly

brew install coreutils


Run the script scripts/ to initiate this project.


If you want, you can manually specify which IP you have and which blockchain to use:

IP=<my-ip> BLOCKCHAIN=manager scripts/

The script unpacks PMC, the deployment tool, and BC0 sources. It also generates the blockchain configurations for the build and app/build folders.

In the configuration file directory1/rell/chromia.yml for BC0, we've:

initial_provider: x"03ECD350EEBC617CBBFBEF0A1B7AE553A748021FD65C7C50C5ABB4CA16D4EA5B05"

that's configured to the provider alpha (found in the providers/alpha directory). alpha is the initial provider of the network.


You must have a Postgres server running to be able to run nodes. You can start a Postgres server using Docker.

$ docker run --name postgres -e POSTGRES_INITDB_ARGS="--lc-collate=C.UTF-8 --lc-ctype=C.UTF-8 --encoding=UTF-8" -e POSTGRES_PASSWORD=postchain -e POSTGRES_USER=postchain -p 5433:5432 postgres

Configure config/ to point to the Postgres instance by replacing {DOCKER_HOST} with the Docker host.


On Mac, the Docker host is host.docker.internal, whereas on Linux/Windows, it's typically

You have now set up a Docker container with a Postgres instance and generated the needed configuration files to set up a network.

Starting over

If you want to restart and give up your progress, you must wipe the database. You might never want to do this in a production environment, but it can be helpful when practicing or testing. You can do this in two ways:

  1. Delete the Postgres container and all its data
  2. Wipe the database schema associated with the node in question
$ scripts/wipe-db config/

Starting network and managing multiple providers

In this section, we set up and use a few nodes and providers with different roles.

The providers are called alpha, beta, gamma, and delta. Their configurations are available in the provider folder. alpha$ means that the command is executed from the provider/alpha folder.


As previously mentioned, a provider is a unique keypair in a PMC config. It's already done for the given providers, but if you want to create a new provider, you must use PMC to create a new keypair (pmc keygen).

The goal of the providers in this example is to illustrate the different roles a provider can have on the network.

alpha - system provider and owner of node 0
beta - system provider and owner of node 1
gamma - node provider and owner of node 2
delta - dapp provider who does not own a node

Initializing the network

Start the genesis node and two other nodes.


All nodes have the management chain deployed. Each node has a node config inside the /config folder. The config files contain unique keypairs for the node and database schema. Each config includes common properties such as database name, password, and information to the genesis node to connect to the existing network. The genesis node is the node that gets started first below.

$ docker run --name postchain0 \
--mount type=bind,source="$(pwd)/config",target=/config,readonly \
--mount type=bind,source="$(pwd)/directory1/rell/build",target=/build,readonly \
-p 9870:9870/tcp \
-p 7740:7740/tcp \ \
run-node --node-config /config/ --chain-id 0 --blockchain-config /build/manager.xml
$ docker run --name postchain1 \
--mount type=bind,source="$(pwd)/config",target=/config,readonly \
--mount type=bind,source="$(pwd)/directory1/rell/build",target=/build,readonly \
-p 9871:9870/tcp \
-p 7741:7740/tcp \ \
run-node --node-config /config/ --chain-id 0 --blockchain-config /build/manager.xml
$ docker run --name postchain2 \
--mount type=bind,source="$(pwd)/config",target=/config,readonly \
--mount type=bind,source="$(pwd)/directory1/rell/build",target=/build,readonly \
-p 9872:9870/tcp \
-p 7742:7740/tcp \ \
run-node --node-config /config/ --chain-id 0 --blockchain-config /build/manager.xml

alpha then initializes the network on node 0.


The pmc network initialize command creates the resources needed to start the D1 management chain. Furthermore, an initial provider is created with the key pair from the PMC config, and a genesis node is registered pointing to the node api-url stated in the PMC config.

alpha$ pmc network initialize --anchoring-config directory1/rell/build/cluster_anchoring.xml

Verify that the network is initialized by noting non-zero values from

alpha$ pmc network summary

Inviting providers

alpha then invites beta and gamma as node providers

alpha$ pmc provider register --enable --pubkey $(pmc config --get pubkey --file ../beta/.pmc/config) -sp
alpha$ pmc provider register --disable --pubkey $(pmc config --get pubkey --file ../gamma/.pmc/config) -np

A provider is only enabled if it fulfills certain conditions.

  • SP enables NP and DP
  • NP enables DP
  • SP proposes SP

In this case, alpha is the only SP that can enable beta immediately. If a second SP is added, both alpha and beta must reach a consensus in voting.

Verify the states of the providers

alpha$ pmc providers

Adding a node to the system cluster

beta has the right to add a node to the system cluster as a newly joined system provider. It can do that by registering the node to the network.


When registering a new node, the PMC config needs to have an api-url pointing to a D1 in the network. When the node is added, this api-url can be changed back to the node hosted by the provider.

beta$ pmc config --set api.url=http://localhost:7740 # points to the rest api of node 0
beta$ pmc node add --pubkey 035676109c54b9a16d271abeb4954316a40a32bcce023ac14c8e26e958aa68fba9 --host $(find-ip) --port 9871 --api-url http://localhost:7741 --cluster system
beta$ pmc config --set api.url=http://localhost:7741 # Change back to point to node 1

Verify that the node is indeed added to the cluster (may take more than 10 seconds since this has a delay of 5 blocks)

beta$ pmc cluster info --name system

Promoting a System Provider through voting

Now that we've two system providers, we can promote gamma to become a system provider through voting.

Making a proposal

A system provider can do some operations directly on the network, such as creating a cluster, but some require consensus. Promoting an NP to SP is one of them:

beta$ pmc provider promote --pubkey $(pmc config --get pubkey --file ../gamma/.pmc/config) --system

Verify that gamma is still not a system provider:

beta$ pmc provider info --pubkey $(pmc config --get pubkey --file ../gamma/.pmc/config)


alpha must now vote on the proposal to accept it. To view which proposals are present and show more information about them, use the following:

alpha$ pmc proposals
alpha$ pmc proposal info # shows latest proposal, use --id to see info about a specific

alpha can now vote on the proposal:

alpha$ pmc proposal vote --id <id> --accept # or --reject

Verify that gamma is promoted to a system node and let gamma add its node:

alpha$ pmc provider info --pubkey $(pmc config --get pubkey --file ../gamma/.pmc/config)
gamma$ pmc config --set api.url=http://localhost:7740 # points to the rest api of node 0
gamma$ pmc node add --pubkey 03f811d3e806e6d093a4bcce49c145ba78f9a4b2fbd167753ecab2a13530b081f8 --host $(find-ip) --port 9872 --api-url http://localhost:7742 --cluster system
gamma$ pmc config --set api.url=http://localhost:7742 # Change back to point to node 1

You should now be able to see that the system cluster has three nodes and providers:

gamma$ pmc cluster info --name system

Launch a blockchain

You can deploy your dapp directly to the system container, but in this tutorial, we'll create a new container to contain the resource usage of the dapp.

To create a new container, use the following:

$ pmc container add --name cities --cluster system --pubkeys $(pmc config --get pubkey --file provider/alpha/.pmc/config)

and see whether it was added using:

$ pmc containers

Launch a new blockchain using the pmc blockchain add command. After running the setup script, you can find a test blockchain configuration in the app-out folder.

$ CITY_DAPP_BRID=$(pmc blockchain add --quiet --name city_tracker --container cities --blockchain-config app/build/city.xml)

In this example project, PMC is used to deploy a dapp. The Chromia CLI is currently not supported in this private network due to incompatibility between software, but it'll be supported soon. When deploying a dapp to the official network, a dapp developer needs to use the Chromia CLI instead.

To verify whether your blockchain is running, use the following:

$ pmc blockchains

Convenient voting

Here, we provide a script to automate voting in your local network. It can be helpful if you have several proposals to vote on and want to avoid manually switching between keypairs. The script iterates over each sub-directory in the provider that you provide as input. If no provider sub-directory is provided, the script iterates in each sub-directory to provider that exists. Furthermore, you give a proposal ID that it should vote on.

To use the script, create a file called in the scripts directory. Paste the following script in the file:



while getopts "i:p:" opt; do
case $opt in
i) id="$OPTARG";;
p) IFS=',' read -ra providers <<< "$OPTARG";;
*) echo "Usage: $0 [-i <proposal ID>] [-p <provider1>,<provider2>,...]" >&2
exit 1;;

if [[ -z $id ]]; then
echo "Error: proposal ID is required." >&2
exit 1

if [[ ${#providers[@]} -eq 0 ]]; then
providers=( $(find provider -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) )

for dir in "${providers[@]}"; do
if [[ -d $full_dir ]]; then
echo "Voting as provider '$dir':"
(cd $full_dir; pmc proposal vote --id $id --accept)
echo "Error: provider directory $full_dir does not exist." >&2

Make the script executable by running the following:

chmod +x scripts/

First, use the script to navigate in a terminal to the project's root directory. Run the script with the following command:

scripts/ -i <proposal ID> -p <provider1>,<provider2>,...

Make sure that the providers that you are trying to vote from exist as a sub-directory in provider with an existing key pair.

Usage examples

This command votes on the proposal with ID 123 with provider alpha, beta, and gamma:

scripts/ -i 123 -p alpha,beta,gamma

This command votes on the proposal with ID 123 with each provider (that's, sub-directory) that exists in the directory provider:

scripts/ -i 123