Set up a Cardano Stake Pool on testnet
This document is intended for those within the Midnight community looking to set up a Cardano Stake Pool on the testnet.
Overview
Cardano Stake Pool Operators are crucial for block production on Partner Chains like the Midnight Testnet, which is paired with Cardano's Preview Testnet. This document refers to these as Preview and Testnet, respectively.
Environment setup
This workshop is designed for execution on a single machine where virtual machines are managed using Multipass. However, in a production environment, you might opt for dedicated bare metal servers or cloud services.
Minimum system requirements
Following this exercise can be done on a local workstation or dedicated servers.
Local workstation

This will be divided amongst 2 local virtual machines.
| Component | Requirement |
|---|---|
| CPU | At least 4 cores |
| Storage | Minimum of 40 GB |
| Memory | At least 8 GB |
Provider

Instance 1/2
| Component | Requirement |
|---|---|
| CPU | At least 2 cores |
| Storage | Minimum of 40 GB |
| Memory | At least 6 GB |
Instance 2/2
| Component | Requirement |
|---|---|
| CPU | At least 2 cores |
| Storage | Minimum of 40 GB |
| Memory | At least 6 GB |
Optimal Production Setup
This tutorial focuses on a simplified setup and does not cover a full production Cardano Stake Pool Operator (SPO) configuration. In a production environment, an optimal Cardano SPO setup would typically include a dedicated instance for the block producer node and multiple relay nodes deployed across diverse geographical locations. This distributed architecture enhances network resilience, security, and performance by ensuring redundancy and minimizing latency.

- For official recommended system requirements for mainnet and production. Visit the Cardano docs: https://developers.cardano.org/docs/operate-a-stake-pool/hardware-requirements/.
Mac/ ARM compatibility:
- It is strongly recommended to use systems with AMD or Intel architecture due to better support from Cardano tools. There is limited pre-built binary support for ARM and M-Chip architectures:
- If you use ARM or M-Chip architectures, you might need to compile the software from source or use trusted releases.
- An optional workaround for ARM and M-Chip users is provided in this documentation.
Windows compatibility:
- Windows users need Bash-compatible tools like GitBash for script execution.
Prerequisites
- System Administration: Familiarity with managing system resources and configurations.
- Networking: Understanding of network topologies and configurations pertinent to blockchain node operations.
- Command-Line Interface (CLI): Proficiency in using command-line tools, as much of the setup will involve CLI interactions.
Step 01 - Creating virtual machines using Multipass
Introduction to Multipass
Multipass is utilized for launching Ubuntu virtual machines with ease. To install Multipass, one should visit the Multipass website and download the installer. For Linux users, installation can also be done via a snap package:
Installation on Linux:
Multipass uses KVM (Kernel-based Virtual Machine) as the default hypervisor for managing Ubuntu virtual machine instances. Therefore, it’s important to confirm KVM is enabled.
- Ensure KVM is enabled:
lsmod | grep kvm #check if KVM is enabled
- Output may return:
lsmod | grep kvm
kvm_intel 487424 0
kvm 1404928 1 kvm_intel
irqbypass 12288 1 kvm
- Install Snap if not already present:
sudo apt install snapd
- Install Multipass:
sudo snap install multipass --classic
- Verify installation:
multipass --version # display current version
multipass --help # display usage and commands
which multipass # verify installation path
Launch virtual machines
- To create two virtual machines, use the following script:
#!/bin/bash
# Launch multipass instances
echo "✨ Creating cute little instances! ✨"
multipass launch -n cn1 -c 2 -m 4gb -d 40gb
multipass launch -n cn2 -c 2 -m 4gb -d 40gb
echo "🐾 Instances are born! 🐾"
instances=("cn1" "cn2")
# Function to check if architecture is ARM
is_arm_architecture() {
if uname -m | grep -q "arm" || uname -m | grep -q "aarch64"; then
return 0 # True (ARM detected)
else
return 1 # False (not ARM)
fi
}
for instance in "${instances[@]}"; do
echo "🚀 Running commands on $instance... 🌟"
multipass exec "$instance" -- bash -c '
# Always run the standard setup first
echo "🐼 Running standard setup for all architectures... 🐼"
sudo apt update -y && sudo apt upgrade -y
mkdir -p "$HOME/tmp" && cd "$HOME/tmp"
sudo apt -y install curl
echo "🐸 Downloading guild-deploy script..."
curl -sS -o guild-deploy.sh https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/guild-deploy.sh
chmod 755 guild-deploy.sh
./guild-deploy.sh -b master -n preview -t cnode -s pdlcowx
. "${HOME}/.bashrc"
cd ~/git || mkdir -p ~/git && cd ~/git
echo "🦋 Cloning cardano-node repository..."
git clone https://github.com/intersectmbo/cardano-node || (cd cardano-node && git fetch --tags --recurse-submodules --all && git pull)
cd cardano-node
git checkout $(curl -sLf https://api.github.com/repos/intersectmbo/cardano-node/releases/latest | jq -r .tag_name)
$CNODE_HOME/scripts/cabal-build-all.sh
# Check architecture and run additional ARM-specific steps if needed
if uname -m | grep -q "arm" || uname -m | grep -q "aarch64"; then
# ARM-specific additional commands
echo "🐙 Detected ARM architecture, running additional ARM-specific setup... 🐙"
ARCHIVE_URL="https://github.com/armada-alliance/cardano-node-binaries/raw/refs/heads/main/static-binaries/cardano-10_1_4-aarch64-static-musl-ghc_966.tar.zst"
ADDRESS_URL="https://github.com/armada-alliance/cardano-node-binaries/raw/refs/heads/main/miscellaneous/cardano-address.zip"
DEST_DIR="$HOME/.local/bin"
mkdir -p "$DEST_DIR"
echo "🐳 Downloading cardano-node archive..."
curl -L "$ARCHIVE_URL" -o cardano-10_1_4.tar.zst
if [ $? -ne 0 ]; then
echo "😿 Failed to download the cardano-node archive."
exit 1
fi
echo "🦄 Downloading cardano-address archive..."
curl -L "$ADDRESS_URL" -o cardano-address.zip
if [ $? -ne 0 ]; then
echo "😿 Failed to download the cardano-address archive."
exit 1
fi
if ! command -v zstd &> /dev/null; then
sudo apt-get update -y
sudo apt-get install -y zstd unzip
elif ! command -v unzip &> /dev/null; then
sudo apt-get install -y unzip
fi
echo "🐰 Extracting cardano-node archive..."
zstd -d -c cardano-10_1_4.tar.zst | tar -x -C "$DEST_DIR" --strip-components=1
if [ $? -ne 0 ]; then
echo "😿 Failed to extract the cardano-node archive."
exit 1
fi
echo "🦊 Extracting cardano-address archive..."
unzip -d "$DEST_DIR" cardano-address.zip
if [ $? -ne 0 ]; then
echo "😿 Failed to extract the cardano-address archive."
exit 1
fi
rm cardano-10_1_4.tar.zst cardano-address.zip
echo "🌈 Additional ARM archives downloaded and extracted successfully to $DEST_DIR"
fi
'
echo "🎉 Finished running commands on $instance! 🐾✨"
done
This script sets up two VMs named cn1 and cn2 (representing "cardano-node-1" and "cardano-node-2") with 2 CPU cores, 4 GB of memory, and 20 GB of disk storage for each instance.
Listing instances
- List all running instances to verify setup:
multipass list
The output might look like:
Name State IPv4 Image
cn1 Running x.x.x.x Ubuntu 24.04 LTS
cn2 Running x.x.x.x Ubuntu 24.04 LTS
Note the IP addresses for future reference. They will be needed when modifying node configuration files.
Graphical Interface
- Multipass also provides a GUI. Users can find and manage virtual machines by searching for "Multipass" in their application list.
Step 02 - Configure and start relay rode (cn1)
Access relay node instance
multipass shell cn1
Initial setup verification
- Upon installation, the SPO toolkit should have set up the
$CNODE_HOMEvariable to/opt/cardano/cnode, where Cardano node files are stored. - Verify setup:
- echo
$CNODE_HOMEshould return/opt/cardano/cnode. - which cardano-node should return
/home/ubuntu/.local/bin/cardano-node.
- echo
Environment variables
The SPO toolkit provides default paths and variables in $CNODE_HOME/scripts/env. Key variables include:
CNODEBIN: "/home/ubuntu/.local/bin/cardano-node"
CCLI: "/home/ubuntu/.local/bin/cardano-cli"
CNODE_PORT: 6000
POOL_NAME: "GUILD"
These can be customized (if desired) by overriding the environment variables, e.g., export CNODE_PORT=6001.
Test start the relay node
- Navigate to the scripts directory:
cd "${CNODE_HOME}"/scripts
- Start the node interactively in shell:
./cnode.sh
- Expected output should conclude with
Listening on http://127.0.0.1:12798. - Press Ctrl + C to stop the node.
Node monitoring
gLiveView displays realtime information on the Cardano node including, but not limited to synchronization status. The node is working correctly if it is syncing and propagating blocks.
- Start the node iteratively in shell again:
cd "${CNODE_HOME}"/scripts
./cnode.sh
- Open a new shell and use gLiveView for real-time monitoring:
cd $CNODE_HOME/scripts
./gLiveView.sh
- Example gLiveView output:
> Cardano Node - (Relay - Preview) : 10.1.3 [36871ba0] <
┌───────────────────────────────┬────────────┬─────────────────────────┐
│ Uptime: 02:18:07 │ Port: 6000 │ Koios gLiveView v1.30.4 │
│-------------------------------└────────────┴─────────────────────────┤
│ Epoch 807 [82.1%], 04:17:09 remaining │
│ ▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▖▖▖▖▖▖▖▖▖▖▖▖▖ │
│ │
│ Block : 2856308 Tip (ref) : 69795771 Forks : 4 │
│ Slot : 69795764 Tip (diff) : 7 :) Total Tx : 0 │