Developers Home»tutorials»Start a Relay Chain

Start a Relay Chain

Before you begin

In this tutorial you will start a Polkadot-like chain (the relay chain), use Cumulus to create your own parachain, and connect it to the relay chain all in a local testnet environment.

If you aren't familiar with the concept of parachains, please learn about them first before continuing:

If you are here without any former Substrate experience, you will likely not be able to understand or complete this tutorial. We assume you have completed these tutorials:

With those complete, let's dive in!

What you will be doing

1. Hardware and software requirements

2. Build your nodes

3. Parachain node template overview

4. Relay chain specification

5. Start your relay chain

6. Create a custom relay chain spec (optional)

Learning outcomes

  • Set up your parachain build environment
  • Start a relay chain
  • Customize a relay chain specification

Hardware and software requirements

Hardware requirements

Compiling this project is a resource intensive process! We suggest using a machine with no less than:

  • 8 GB of RAM (16 GB is suggested)
  • 4 CPU cores (8 cores are suggested)
  • 50 GB of free HDD/SSD space

Without the minimal RAM here, you are likely run out of memory resulting in a SIGKILL error during the compilation process, generally happens on building the polkadot-service crate. So be sure to monitor your RAM usage with tools like htop and look out as swap memory starting to be used.

Build with underpowered hardware

If you cannot find a machine with the minimums here, try the following solutions which trade longer build times for more limited memory usage.

  • Use less threads: cargo -j flag specifies the number of threads used to build. Try to use one less than the CPU cores your machine has.
  • Use cargo codegen units feature makes more optimized builds with less ram, but much longer compile times.
# set the number of cores/threads to compile (used to build cumulus/polkadot on rpi 3)
cargo build --release -j 1
# use less codegen units
RUSTFLAGS="-C codegen-units=1" cargo build --release

Software versioning

This tutorial has been tested on:

Exact Versions Matter

You must use the exact versions set forth in this tutorial to ensure that you do not run into conflicts.

At the moment, parachains are very tightly coupled with the relay chain codebase they are connecting to. To have the least amount of hiccups, be sure to use the corresponding tagged version of Polkadot and Parachain Template when working on this tutorial. So if you are using Polkadot v0.9.10, use the equivalent version of Parachain Template polkadot-v0.9.10.

We're doing our best to keep the Parachain Template and this tutorial updated presently with the latest release of Polkadot.

Join the chat

Please join the Parachain Technical matrix channel to report any issues you run into and get further support.

Build your nodes

In case you haven't, please follow the instructions to setup a local development environment for Substrate.

Building the relay chain node

Polkadot network will serve as our relay chain in this workshop. So clone the Polkadot repository and build the node:

# Clone the Polkadot Repository
git clone

# Switch into the Polkadot directory
cd polkadot

# Checkout the proper commit
git checkout v0.9.10

# Build the relay chain Node
cargo build --release

# Check if the help page prints to ensure the node is built correctly
./target/release/polkadot --help

After you start the compilation, it will take a while (30 mins to 60 mins) to complete. So go ahead and continue to read through the rest of the workshop during the wait 😉

If the help page is printed, you have succeeded in building a Polkadot node.

Building the parachain template

We will use the Substrate Parachain Template to launch our first parachain and make cross-chain asset transfers. The Parachain Template is similar but not identical to the Node Template. Later, we will use this Parachain Template as the starting point for developing our own parachains.

In a new terminal window:

# Clone the Parachain Template
git clone

# Switch into the Parachain Template directory
cd substrate-parachain-template

# Checkout the proper commit
git checkout polkadot-v0.9.10

# Build the parachain template collator
cargo build --release

# Check if the help page prints to ensure the node is built correctly
./target/release/parachain-collator --help

Again, this will take 30 to 60 mins to complete.

If the help page is printed, you have succeeded in building a Cumulus-based parachain collator.

Parachain node template overview

Substrate developers who are familiar with the Substrate Node Template will find the Substrate Parachain Template familiar. They have the same general structure featuring node, runtime, and pallets directories. Their runtimes are similar and feature many of the same pallets. Apart from a few new traits, the pallet-template itself is essentially identical. Many of the tutorials can be used with few modifications on the Parachain Template.

The similarities between these two templates should give you confidence that if you've built a Substrate chain, you will have no problem building a parachain!

Differences from the node template

There are, however, a few important differences between the two templates that are worth observing at the outset.

Parachain info pallet

Parachain Template runtime (runtime/Cargo.toml) has integrated parachain-info pallet in. This pallet is designed to inject information about the parachain's registration into its own runtime. Currently it just injects the para ID that the chain is registered at. This allows the runtime to know which cross-chain messages are intended for it.

register_validate_block! macro

Each parachain must supply a validate_block function, expressed as a Wasm blob, to the relay chain when registering. The Node Template does not provide this function, but the parachain template does, Thanks to cumulus, creating this function for a Substrate runtime is as simple as adding one line of code as shown at the bottom of the runtime:

  Runtime = Runtime,
  BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>,
  CheckInherents = CheckInherents,

No GRANDPA pallet

Many popular Substrate runtimes including the Node Template features a finality-related GRANDPA pallet and its associated GrandpaApi. These are both missing from the Parachain Template.

This is because parachains follow the finality of the relay chain rather than running their own finality gadget. This is fundamental to Polkadot's architecture and will not change.


The collator service (node/src/ is entirely different from the one of Node template. While you can find similarities, the structure of the service is much different. This new service is the primary change that cumulus provides.


When modifying an existing Substrate chain to use Cumulus, it is generally best to copy the service code from the Parachain Template.

Relay chain specification

You will need a chain specification (chain spec) for your relay chain network.

Minimal validators per collator

Always have one or more relay chain validator nodes running than the total connected parachains. For example, if you want to connect two parachains, run three or more relay chain validator nodes.

Whichever chain spec file you choose to use we will refer to the file simply as chain-spec.json in the instructions below. You will need to supply the proper path to the chain spec you are using.

Distribute your raw spec

If you intend to let others connect to your network, you should have the genesis Wasm and the associated chain spec for your network generated once and distributed to your peers. This stems from the non-deterministic issue in the way Wasm runtimes are compiled, at least for now.

Chain specs conventionally live in a /res folder that is published in your node's codebase for others to use. For example:

Pre-configured chain spec files

We have included three set of chain spec files in this tutorial that you can use without modification for a local test network:

Plain chain spec files are in a more human readable and modifiable format for your inspection. You will need to convert it to a SCALE encoded raw chain spec to use when starting your nodes. Jump to the conversion section to see how to do that.

The above raw chain specs were created according to the steps in the create your own chain spec section.

Start your relay chain

Before we can attach any cumulus-based parachains, we need to launch a relay chain for them to connect to. This section describes in detail how to start both nodes using the above two-validator raw chain spec as well as the general instructions for starting additional nodes.

Start the alice validator

# Start Relay `Alice` node
./target/release/polkadot \
--alice \
--validator \
--base-path /tmp/relay/alice \
--chain <path to spec json> \
--port 30333 \
--ws-port 9944

The port (port) and websocket port (ws-port) specified here are the defaults and thus those flags can be omitted. However we choose to leave them in to enforce the habit of checking their values. Once this node is launched, no other nodes on the same local machine can use these ports.

When the node starts you will see several log messages. Take note of the node's Peer ID in the logs. We will need it when connecting other nodes to it. It will look something like this:

🏷 Local node identity is: 12D3KooWGjsmVmZCM1jPtVNp6hRbbkGBK3LADYNniJAKJ19NUYiq

Start the bob validator

--bob \
--validator \
--base-path /tmp/relay-bob \
--chain <path to spec json> \
--bootnodes /ip4/<Alice IP>/tcp/30333/p2p/<Alice Peer ID> \
--port 30334 \
--ws-port 9945

Bob's command is perfectly analogous to Alice's. It differs from Alice's one by his own base path, his own validator key (--bob), and his own ports. Finally he adds a --bootnodes flag. This flag is not strictly necessary if you are running the entire network on a single local machine, but it is necessary when operating over the network.

Starting additional validators (optional)

If you are using the two-validator raw chain spec, you do not need to start additional nodes, but others may need to start more nodes. Again, this command is entirely analogous. You just need to make sure that nodes on the same local machine do not have conflicting ports or base paths.

--charlie \
--validator \
--base-path /tmp/relay-charlie \
--chain <path to spec json> \
--bootnodes /ip4/<Alice IP>/tcp/30333/p2p/<Alice Peer ID> \
--port 30335 \
--ws-port 9946

If you would like even more validators, or to customize the relay chain in some other ways, keep reading!

Create a custom relay chain spec (optional)

It is generally best to start from an existing raw chain specs to minimize chances of error. Once you are familiar with the overall flow, use the following steps to customize and generate your own chain specs.

chain spec testing with `rococo-local`

Your final chain spec filename must start with rococo or the node will not know what runtime logic to include.

Generate a Plain Chain Spec

# Create a base chain spec that we will modify
./target/release/build-spec --chain rococo-local --disable-default-bootnode > rococo-custom-plain.json

We have just generated a plain chain spec file for rococo-local network. That file contains most of the information we need already. Rococo is a permissioned chain, so we just need to add an authority and its session keys. The snippet below shows the relevant part of the generated spec file. All keys in the generated file belong to the usual well-known accounts used in other tutorials (Alice and Bob in the case of the rococo-custom-plain.json file).

"session": {
  "keys": [
      "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY",                           // <---- The Validator Authority (//Alice//stash)
        "grandpa": "5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu",              // <---- The GRANDPA ed25519 session key (//Alice)
        "babe": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",                 // <---- The sr25519 session keys (//Alice)
        "im_online": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
        "para_validator": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
        "para_assignment": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
        "authority_discovery": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
        "beefy": "KW39r9CJjAVzmkf9zQ4YDb2hqfAVGdRqn53eRqyruqpxAP5YL",               // <---- The BEEFY *encoded* ecdsa session keys (//Alice)
  // -- snip -- ADD MORE KEYS HERE, following the same format

As mentioned above, we have pre-configured:

Plain chain spec files are in a more human readable and modifiable format for our inspection. We will need to convert it to a SCALE encoded raw chain spec to pass to nodes on startup. Jump to the conversion section to see how to do that.

Adjust the Plain Chain Spec

Then we would adjust the plain chain spec file by adding new authority's AccountId and ValidatorId.

Direct edits vs. building specs

The addition of custom session keys in the plain chain spec discussed in this section typically is not needed for production chains, as they have been included in the accompanied chain spec files. The exercise below is done because we have made modifications to the plain chain spec. In production, most of the time you will want to include these in your to include them in the node binary directly. And then use the CLI to generate your custom plain chain spec.

In this case, both IDs are going to be the same and are generated from the "stash" account. You can generate your own or inspect the well-known development accounts.

The following commands demonstrate how the first part of the palletSession section inside the spec file can be reproduced. The second part is obtained similarly with //Bob and //Bob//stash.

Subkey tool vs. key command

All the keys and addresses needed can be generated and inspected using either:

  • The stand-alone subkey tool (v2.0.1 or above)
  • The node key subcommand, included in all substrate node CLIs

These commands are equivalent. The sole difference being that the key subcommand also includes methods to insert keys into your nodes.

Polkadot validator authority address for //Alice//stash (sr25519 cryptography):

# Replace `node` with any substrate based node binary, like `polkadot`
subkey inspect --scheme sr25519 --network substrate //Alice//stash


Secret Key URI `//Alice//stash` is account:
  Secret seed:       0x3c881bc4d45926680c64a7f9315eeda3dd287f8d598f3653d7c107799c5422b3
  Public key (hex):  0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f
  Public key (SS58): 5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY
  Account ID:        0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f
  SS58 Address:      5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY

Polkadot grandpa session key for //Alice (ed25519 cryptography):

subkey inspect --scheme ed25519 --network substrate //Alice


Secret Key URI `//Alice` is account:
  Secret seed:       0xabf8e5bdbe30c65656c0a3cbd181ff8a56294a69dfedd27982aace4a76909115
  Public key (hex):  0x88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee
  Public key (SS58): 5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu
  Account ID:        0x88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee
  SS58 Address:      5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu

Polkadot address for //Alice (sr25519 cryptography). This is used in all but the beefy key sections of the chain spec after the grandpa key.

subkey inspect --scheme sr25519 --network substrate //Alice


Secret Key URI `//Alice` is account:
  Secret seed:       0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a
  Public key (hex):  0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
  Public key (SS58): 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
  Account ID:        0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
  SS58 Address:      5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY

And finally the encoded SS58 ecdsa BEEFY key:

subkey inspect --scheme ecdsa --network substrate //Alice


Secret Key URI `//Alice` is account:
  Secret seed:       0xcb6df9de1efca7a3998a8ead4e02159d5fa99c3e0d4fd6432667390bb4726854
  Public key (hex):  0x020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1
  Public key (SS58): KW39r9CJjAVzmkf9zQ4YDb2hqfAVGdRqn53eRqyruqpxAP5YL
  Account ID:        0x01e552298e47454041ea31273b4b630c64c104e4514aa3643490b8aaca9cf8ed
  SS58 Address:      5C7C2Z5sWbytvHpuLTvzKunnnRwQxft1jiqrLD5rhucQ5S9X

Now that you have all the keys needed, append them in the palletSession section of you plain chain spec. You can either create new IDs or use other well known accounts following this same process.

A note on SS58 encoding of key vs. address

Notice the BEEFY key is the Public key (SS58) and it's different from the SS58 Address in the case of ECDSA keys. BEEFY is a component built for the feature bridging Substrate to other blockchain networks.

In the case of sr25519 and ed25519, the account ID matches its public key, hence SS58 encoded account-id address is the same as SS58 public key encoding. In case of ECDSA, we apply blake2 algorithm to the public key to get the address (due to the size difference between 33 vs 32 bytes), so the SS58 encoding is different.

Default serialization / deserialization implementation for public keys is using SS58 encoding, hence every time we use public keys in encoded form we are going to need it's SS58 encoding. A notable case is chain spec JSON file and encoding of session keys (most importantly BEEFY).

Convert plain to raw chain spec

Now that you've modified your plain chain spec, you can generate the final raw chain spec by:

./target/release/build-spec --chain rococo-custom-plain.json --raw --disable-default-bootnode > rococo-custom.json

You may get the output warning: Took active validators from set with wrong size. The resulting chain-spec.json will still be perfectly usable, you can ignore this warning for now.

Start the custom relay chain and insert your own keys

If your custom chain spec includes self-generated keys, see the Substrate private network tutorial for details on inserting these keys into your various nodes.

Next steps

Now that we have a layer-0 blockchain ready, we can now start connecting layer-1 parachains to it! Into the next section we dive!

Last edit: on

Was This Tutorial Helpful?
Help us improve