diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3f2f3cc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+stage0-spec.yml
+stage0-deployment
+stage1-spec.yml
+stage1-deployment
+
+stage1-genesis
+output
diff --git a/stack-orchestrator/compose/docker-compose-fixturenet-laconicd.yml b/stack-orchestrator/compose/docker-compose-fixturenet-laconicd.yml
index a47d3bc..d4bc53a 100644
--- a/stack-orchestrator/compose/docker-compose-fixturenet-laconicd.yml
+++ b/stack-orchestrator/compose/docker-compose-fixturenet-laconicd.yml
@@ -2,12 +2,14 @@ services:
   laconicd:
     restart: unless-stopped
     image: cerc/laconic2d:local
-    command: ["sh", "/docker-entrypoint-scripts.d/create-fixturenet.sh"]
+    command: ["bash", "/docker-entrypoint-scripts.d/create-fixturenet.sh"]
     environment:
       TEST_AUCTION_ENABLED: ${TEST_AUCTION_ENABLED:-false}
       TEST_REGISTRY_EXPIRY: ${TEST_REGISTRY_EXPIRY:-false}
+      ONBOARDING_ENABLED: ${ONBOARDING_ENABLED:-false}
     volumes:
       - laconicd-data:/root/.laconicd
+      - genesis-config:/var/tmp
       - ../config/fixturenet-laconicd/create-fixturenet.sh:/docker-entrypoint-scripts.d/create-fixturenet.sh
     ports:
       - "6060"
@@ -25,3 +27,4 @@ services:
 
 volumes:
   laconicd-data:
+  genesis-config:
diff --git a/stack-orchestrator/config/fixturenet-laconicd/create-fixturenet.sh b/stack-orchestrator/config/fixturenet-laconicd/create-fixturenet.sh
index 4b9225e..d9de757 100644
--- a/stack-orchestrator/config/fixturenet-laconicd/create-fixturenet.sh
+++ b/stack-orchestrator/config/fixturenet-laconicd/create-fixturenet.sh
@@ -6,6 +6,8 @@ MONIKER="localtestnet"
 KEYRING="test"
 LOGLEVEL="${LOGLEVEL:-info}"
 
+input_genesis_file=/var/tmp/genesis.json
+
 if [ "$1" == "clean" ] || [ ! -d "$HOME/.laconicd/data/blockstore.db" ]; then
   # validate dependencies are installed
   command -v jq > /dev/null 2>&1 || {
@@ -29,6 +31,11 @@ if [ "$1" == "clean" ] || [ ! -d "$HOME/.laconicd/data/blockstore.db" ]; then
   # Set moniker and chain-id for Ethermint (Moniker can be anything, chain-id must be an integer)
   laconicd init $MONIKER --chain-id $CHAINID --default-denom photon
 
+  if [ -f ${input_genesis_file} ]; then
+    # Use provided genesis config
+    cp $input_genesis_file $HOME/.laconicd/config/genesis.json
+  fi
+
   update_genesis() {
     jq "$1" $HOME/.laconicd/config/genesis.json > $HOME/.laconicd/config/tmp_genesis.json &&
       mv $HOME/.laconicd/config/tmp_genesis.json $HOME/.laconicd/config/genesis.json
@@ -52,6 +59,12 @@ if [ "$1" == "clean" ] || [ ! -d "$HOME/.laconicd/data/blockstore.db" ]; then
     update_genesis '.app_state["registry"]["params"]["authority_auction_reveals_duration"]="60s"'
   fi
 
+  if [[ "$ONBOARDING_ENABLED" == "true" ]]; then
+    echo "Enabling validator onboarding."
+
+    update_genesis '.app_state["onboarding"]["params"]["onboarding_enabled"]=true'
+  fi
+
   # increase block time (?)
   update_genesis '.consensus["params"]["block"]["time_iota_ms"]="1000"'
 
diff --git a/stack-orchestrator/stacks/fixturenet-laconicd/README.md b/stack-orchestrator/stacks/fixturenet-laconicd/README.md
index 891835f..356950d 100644
--- a/stack-orchestrator/stacks/fixturenet-laconicd/README.md
+++ b/stack-orchestrator/stacks/fixturenet-laconicd/README.md
@@ -51,7 +51,7 @@ Instructions for running a laconicd fixturenet along with registry CLI and conso
 * Edit `network` in the spec file to map container ports to host ports as required:
 
   ```bash
-  # testnet-laconicd-spec.yml
+  # fixturenet-laconicd-spec.yml
   ...
   network:
     ports:
@@ -74,7 +74,7 @@ Instructions for running a laconicd fixturenet along with registry CLI and conso
 * Create deployment from the spec files:
 
   ```bash
-  laconic-so --stack ~/cerc/fixturenet-laconicd-stack/stack-orchestrator/stacks/fixturenet-laconicd deploy create --spec-file  fixturenet-laconicd-spec.yml --deployment-dir fixturenet-laconicd-deployment
+  laconic-so --stack ~/cerc/fixturenet-laconicd-stack/stack-orchestrator/stacks/fixturenet-laconicd deploy create --spec-file fixturenet-laconicd-spec.yml --deployment-dir fixturenet-laconicd-deployment
 
   laconic-so --stack ~/cerc/testnet-laconicd-stack/stack-orchestrator/stacks/laconic-console deploy create --spec-file laconic-console-spec.yml --deployment-dir laconic-console-deployment
 
@@ -82,8 +82,25 @@ Instructions for running a laconicd fixturenet along with registry CLI and conso
   cp fixturenet-laconicd-deployment/deployment.yml laconic-console-deployment/deployment.yml
   ```
 
+* (Optional) Copy over an existing genesis file (`.json`) to data directory in deployment (`fixturenet-laconicd-deployment/data/genesis-config`):
+
+  ```bash
+  # Example
+  cp genesis.json fixturenet-laconicd-deployment/data/genesis-config/genesis.json
+  ```
+
 ## Configuration
 
+* Inside the `fixturenet-laconicd-deployment` deployment directory, open `config.env` file and set the following env variable:
+
+  ```bash
+  # Optional
+
+  # Set to true to enable adding participants functionality of the onboarding module
+  # (default: false)
+  ONBOARDING_ENABLED=
+  ```
+
 * Inside the `laconic-console-deployment` deployment directory, open `config.env` file and set following env variables:
 
   ```bash
diff --git a/stack-orchestrator/stacks/fixturenet-laconicd/scripts/generate-stage1-genesis.sh b/stack-orchestrator/stacks/fixturenet-laconicd/scripts/generate-stage1-genesis.sh
new file mode 100755
index 0000000..5e220d7
--- /dev/null
+++ b/stack-orchestrator/stacks/fixturenet-laconicd/scripts/generate-stage1-genesis.sh
@@ -0,0 +1,84 @@
+#!/bin/bash
+
+# Exit on error
+set -e
+
+# Prerequisite: nitro-rpc-client package installed globally
+# https://github.com/cerc-io/go-nitro/blob/main/packages/nitro-rpc-client/readme.md
+
+# Check args
+if [ "$#" -ne 1 ]; then
+  echo "Usage: $0 <stage0-deployment-dir-absolute>"
+  exit 1
+fi
+
+STAGE0_DEPLOYMENT_DIR="$1"
+STAGE1_GENESIS_DIR=stage1-genesis
+
+# Create a temporary target directory
+mkdir -p $STAGE1_GENESIS_DIR
+
+# --------
+
+# Fetch ETH account holdings from the bridge node
+
+# Trust locally-trusted development certificates created by mkcert
+export NODE_EXTRA_CA_CERTS="$(mkcert -CAROOT)/rootCA.pem"
+
+# Run the nitro-rpc-client command and process the output with jq
+eth_account_holdings=$(nitro-rpc-client get-all-ledger-channels -p 4006 | jq -s '[.[] | {nitro_address: .Balance.Them, balance: .Balance.TheirBalance}]')
+
+echo "Fetched Ethereum account holdings"
+
+# --------
+
+# Export onboarding module state from stage0 laconicd chain
+
+onboarding_state_file="$STAGE1_GENESIS_DIR/stage0-onboarding-state.json"
+docker run -it \
+  -v ${STAGE0_DEPLOYMENT_DIR}/data/laconicd-data:/root/stage0-deployment/.laconicd \
+  cerc/laconic2d:local bash -c "laconicd export --home /root/stage0-deployment/.laconicd" \
+  | jq .app_state.onboarding > "$onboarding_state_file"
+
+stage0_participants=$(cat "$onboarding_state_file" | jq .participants)
+
+echo "Exported onboarding module state from stage 0 chain"
+
+# --------
+
+# For each participant, using ETH account holdings, figure out balance allocation for stage1
+stage1_allocations_file="$STAGE1_GENESIS_DIR/stage1-allocations.json"
+jq -n --argjson holdings "$eth_account_holdings" --argjson participants "$stage0_participants" '
+[
+  $holdings[] |
+  . as $holding |
+  $participants[] |
+  select((.nitro_address | ascii_downcase) == ($holding.nitro_address | ascii_downcase)) |
+  {
+    cosmos_address: .cosmos_address,
+    balance: $holding.balance
+  }
+]' > $stage1_allocations_file
+
+echo "Calculated allocations for stage 1 chain"
+
+# --------
+
+# Run a script with cerc/laconic2d:local to generate the genesis file
+# with onboarding module state and given allocations
+docker run -it \
+  -v ./stage1-genesis:/root/.laconicd \
+  -v ./scripts:/scripts \
+  cerc/laconic2d:local bash -c "/scripts/genesis.sh"
+
+# Copy over the genesis file to output folder
+OUTPUT_DIR=output
+mkdir -p $OUTPUT_DIR
+cp ./stage1-genesis/config/genesis.json $OUTPUT_DIR/genesis.json
+
+echo "Genesis file for stage1 written to $OUTPUT_DIR/genesis.json"
+
+# --------
+
+# Clean up
+echo "Please remove the temporary data directory: sudo rm -rf stage1-genesis"
diff --git a/stack-orchestrator/stacks/fixturenet-laconicd/scripts/genesis.sh b/stack-orchestrator/stacks/fixturenet-laconicd/scripts/genesis.sh
new file mode 100755
index 0000000..e45326c
--- /dev/null
+++ b/stack-orchestrator/stacks/fixturenet-laconicd/scripts/genesis.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+# Exit on error
+set -e
+
+# Note: Needs to be run in a docker container with image cerc/laconic2d:local
+
+CHAINID="laconic_9000-1"
+STAGE1_MONIKER=localtestnet-stage-1
+NODE_HOME="/root/.laconicd"
+
+onboarding_state_file="$NODE_HOME/stage0-onboarding-state.json"
+stage1_allocations_file="$NODE_HOME/stage1-allocations.json"
+stage1_genesis_file="$NODE_HOME/config/genesis.json"
+
+laconicd init $STAGE1_MONIKER --chain-id $CHAINID --default-denom photon
+
+# Update onboarding module state with participants from stage0
+participants=$(jq -c '.participants' $onboarding_state_file)
+jq --argjson p "$participants" '.app_state.onboarding.participants = $p' "$stage1_genesis_file" > tmp.$$.json && mv tmp.$$.json "$stage1_genesis_file"
+
+# Disable bank module transfers
+jq '.app_state["bank"]["params"]["default_send_enabled"]=false' "$stage1_genesis_file" > tmp.$$.json && mv tmp.$$.json "$stage1_genesis_file"
+
+# Read and assign allocations
+jq -c '.[]' "$stage1_allocations_file" | while IFS= read -r line; do
+  cosmos_address=$(jq -r '.cosmos_address' <<< "$line")
+  balance=$(jq -r '.balance' <<< "$line")
+
+  # Add a genesis account for participant's laconic address with required balance
+  laconicd genesis add-genesis-account $cosmos_address ${balance}photon
+done
+
+# Ensure that resulting genesis file is valid
+laconicd genesis validate