How to use the slides - Full screen (new tab)
Slides Content
--- title: Zombienet description: Zombienet workshop duration: 1 hour ---


What is Zombienet?

Zombienet is an integration testing tool that allows users to spawn and test ephemeral substrate based networks.

Why Zombienet?

Integration tests are always complex:

  • Setup Configuration
  • Port management
  • Ready state off all artifacts
  • Observability
  • Leaking resources


Friction to resolve

  • Config flexibility
  • Local environment
  • Maintenance
  • CI friendly
  • Scaling
  • Test-runner



Hassle free setup
  • Toml / json
  • Nice defaults
  • Templating lang.
Multiple envs
  • Local
  • k8s
  • podman
  • Custom assertions

  • Intuitive D.S.L

  • Templating lang.




  • Custom chain-specs
  • Custom command
  • Port-mapping
  • Parachains registration


  • Custom D.S.L
  • Multiple assertions
  • Extensible
  • Custom reporting

Zombienet Options

  • As binary (releases)
  • As library (@zombienet)
  • As container (published in docker hub)
  • From source (zombienet repo)


  • As binary: Binaries for Linux and MacOS are available in each release in Github.
  • npm packages: cli, orchestrator, utils
  • image: docker.io/paritytech/zombienet code is available in GitHub with the instructions on how to build and run Zombienet. (https://github.com/paritytech/zombienet)


Download Zombienet

# macOS
curl -L https://github.com/paritytech/zombienet/releases/download/v1.3.63/zombienet-macos
-o ./zombienet

# linux
curl -L https://github.com/paritytech/zombienet/releases/download/v1.3.63/zombienet-linux
-o ./zombienet

# make executable
chmod +x zombienet

Let’s spawn a new network!


But first, try manually…

  • Create chain-spec (parachain)
parachain-template-node build-spec --chain local \
--disable-default-bootnode > /tmp/para.json

  • Create chain-spec (relay chain)
polkadot build-spec --chain rococo-local \
 --disable-default-bootnode > /tmp/relay.json


Tutorials https://docs.substrate.io/tutorials/build-a-parachain/


Add keys*

When not using --alice or --bob, you need to provide additional aura and grandpa keys and inject them into the keystore! (per node)

./target/release/polkadot \
key insert --base-path /tmp/node01 \
  --chain /tmp/relay.json \
  --scheme Sr25519 \
  --suri <your-secret-seed> \
  --password-interactive \
  --key-type aura

./target/release/polkadot key insert \
  --base-path /tmp/node01 \
  --chain /tmp/relay.json \
  --scheme Ed25519 \
  --suri <your-secret-key> \
  --password-interactive \
  --key-type gran


This step is optional if you use the dev accounts (e.g. alice, bob, charlie, dave, etc)


  • Start relay chain nodes
# create nodes dirs
  mkdir -p /tmp/relay/{alice,bob}

  ./target/release/polkadot \
  --alice \
  --validator \
  --base-path /tmp/relay/alice \
  --chain /tmp/relay.json \
  --port 30333 \
  --ws-port 9944

  ./target/release/polkadot \
  --bob \
  --validator \
  --base-path /tmp/relay/bob \
  --chain /tmp/relay.json \
  --port 30334 \
  --ws-port 9945


Why do we need to use different ports for Alice and Bob?


  • Start collator
# create nodes dirs
mkdir -p /tmp/para/alice

parachain-template-node \
--alice \
--collator \
--force-authoring \
--chain /tmp/para.json \
--base-path /tmp/para/alice \
--port 40333 \
--ws-port 8844 \
-- \
--execution wasm \
--chain /tmp/relay.json \
--port 30343 \
--ws-port 9977


  • Register ParaId on relay chain
  1. Modify parachain chain-spec and create raw format
  2. Generate genesis wasm and state
  3. Register parachain using sudo call

parachain-template-node build-spec --chain /tmp/para-raw.json \
--disable-default-bootnode --raw > /tmp/para-raw.json

parachain-template-node export-genesis-wasm --chain /tmp/para-raw.json \

parachain-template-node export-genesis-state --chain /tmp/para-raw.json \


Follow the connect a local parachain to launch your own network.

Non-trivial chore

  • Error prone.
  • Multiple commands.
  • Port management.
  • Multiple process.
Zombienet allow you to set everything in just 1 file.


Zombienet network definition

Zombienet allow to define your network with a simple configuration file.




# examples/0001-small-network.toml

default_image = "docker.io/parity/polkadot:latest"
default_command = "polkadot"
chain = "rococo-local"

  name = "sub"

  name = "zero"

id = 1001
cumulus_based = true

  name = "collator01"
  image = "docker.io/parity/polkadot-parachain:latest"
  command = "polkadot-parachain"




Spawn the network

./zombienet spawn examples/0001-small-network.toml


Try to launch a network with 2 parachains.


Make the network config dynamic

The network definition supports using nunjucks templating language (similar to tera). Where {{variables}} are replaced with env vars and you can use all the built-in features.

default_image = "{{ZOMBIENET_INTEGRATION_IMG}}"
default_command = "polkadot"


Make the network config dynamic


Zombienet providers allow to spawn and test networks with in different environments.



  • Used internally, integrated with the Grafana stack.
  • You need to provide your infra stack.


  • Automatically spawn and wire an instance of Grafana stack.
  • Attach a jaeger instance if enabled in the network definition.


  • Allow to attach to a running Grafana stack. (wip)


Meet the Test-runner

Zombienet’s built-in test-runner allows users to use a simple D.S.L. to easily and intuitively write tests with a set of natural language expressions to make assertions.


Built-in assertions

  • Prometheus: Query the exposed metrics/histograms and assert on their values.

  • Chain: Query/subscribe chain's storage/events.

  • Custom scripts: Run custom js scripts or bash scripts (inside the pod).

  • Node's logs: Match regex/glob patterns in the node's logs.

  • Integrations: Zombienet supports multiple integrations, like jaeger spans, polkadot introspector and the backchannel.


Description: Small Network Paras
Network: ./0002-small-network-paras.toml
Creds: config # Only used with k8s

# well known functions
validator: is up # check all the validators in the group
validator-0: parachain 1000 is registered within 225 seconds
validator-0: parachain 1001 is registered within 225 seconds

# ensure parachains are producing blocks
validator-0: parachain 1000 block height is at least 5 within 300 seconds
validator-0: parachain 1001 block height is at least 5 within 300 seconds

# metrics
validator-0: reports node_roles is 4
validator-0: reports block height is at least 2 within 15 seconds

# logs (patterns are transformed to regex)
validator-1: log line matches glob "*rted #1*" within 10 seconds
validator-1: log line matches "Imported #[0-9]+" within 10 seconds

# system events (patterns are transformed to regex)
validator-2: system event contains "A candidate was included" within 10 seconds
validator-2: system event matches glob "*was backed*" within 10 seconds

# custom scripts
validator-0: js-script ./custom.js with "alice" within 200 seconds
validator-0: run ./custom.sh within 200 seconds


First three lines are the header

Each line represents an assertion

Each assertion is executed sequentially

Assertions on a group check each node

within keyword allows to keep-trying until time expires

DSL extension

Learning a new DSL can be tedious, but if you are using vscode we develop an extension that can help you to write test easily.


Show the extension link https://github.com/paritytech/zombienet-vscode-extension

Demo time

./zombienet -p native test examples/0002-small-network-paras.zndsl


Zombienet allow users to use the custom-js assertion to extend and run custom tests.



# custom scripts
validator-0: js-script ./custom.js with "alice" within 200 seconds
async function run(nodeName, networkInfo, args) {
  const { wsUri, userDefinedTypes } = networkInfo.nodesByName[nodeName];
  const api = await zombie.connect(wsUri, userDefinedTypes);
  const validator = await api.query.session.validators();
  return validator.length;

module.exports = { run };


Zombienet will load your script and call the run function.

Passing the node name, network info and an array of arguments from the assertion

Your function have access to the zombie object exposing utilities like connect, ApiPromise, Keyring, etc *

The assertions can validate the return value or the completions of your script.

*similar to the way that scripts are written in PolkadotJS apps - developer page (https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frpc.polkadot.io#/js)

More extensibility

Zombienet also allow users to use as a library to create their own interactions with the running network.


As a Library

  • @zombienet/orchestrator module expose the start function as entrypoint.

  • Returning a network instance, with all the information about the running topology.

  • You can also use the test function passing a callback to run your test.

  • @zombienet/utils module expose misc utils functions like readNetworkConfig.


import {start} from "@zombienet/orchestrator";
import { readNetworkConfig } from "@zombienet/utils";


// can be toml or json
const launchConfig = readNetworkConfig("../examples/0001-small-network.toml");

( async () => {
    const network = await start(ZOMBIENET_CREDENTIALS, launchConfig, {
        spawnConcurrency: 5,

    // write your own test, `network` will have all the network info

The road ahead...

🚧 🚧 Zombienet v2 (a.k.a SDK) is currently under construction 🚧 🚧

The SDK will provide a set of building blocks that users can combine to spawn and interact with the network and also a fluent API for crafting different topologies and assertions for the running network.


SDK repo: https://github.com/paritytech/zombienet-sdk

Acknowledgement & Contributions

Zombienet take inspiration and some patterns from polkadot-launch and SimNet.

We encourage everyone to test it, provide feedback, ask question and contribute.



  • Launch a network with two validators and one parachain.

  • Add a test to ensure:

    • block producing
    • peers number
    • node's role

Additional Resources!

Check speaker notes (click "s" πŸ˜‰)