Guide: How to build a Cardano Stake Pool

On Ubuntu/Debian, this guide will illustrate how to install and configure a Cardano stake pool from source code.

As of July 28, 2020, this guide is written for mainnet with release v.1.18.0 ๐Ÿ˜

โ€‹๐Ÿ 0. Prerequisites

โ€‹๐Ÿง™โ™‚ Skills for stake pool operators

As a stake pool operator for Cardano, you will typically have the following abilities:

  • operational knowledge of how to set up, run and maintain a Cardano node continuously

  • a commitment to maintain your node 24/7/365

  • system operation skills

  • server administration skills (operational and maintenance).

  • experience of development and operations (DevOps) would be very useful

โ€‹๐ŸŽ— Minimum Hardware Requirements

  • Operating system: 64-bit Linux (i.e. Ubuntu 20.04 LTS)

  • Processor: 6 core CPU

  • Memory: 16GB RAM

  • Storage: 100GB SSD

  • Internet: 24/7 broadband internet connection with speeds at least 10 Mbps.

  • Data Plan: at least 1GB per hour. 720GB per month.

  • Power: 24/7 electrical power

  • ADA balance: at least 1000 ADA

  • Operating system: 64-bit Linux (i.e. Ubuntu 20.04 LTS)

  • Processor: 12 core or higher CPU

  • Memory: 32GB+ RAM

  • Storage: 2TB SSD with RAID

  • Internet: Multiple 24/7 broadband internet connections with speeds at least 100 Mbps

  • Data Plan: Unlimited

  • Power: Redundant 24/7 electrical power with UPS

  • ADA balance: more pledge is better, to be determined by a0, the pledge influence factor

For instructions on installing Ubuntu, refer to the following:

If you are rebuilding or reusing an existing cardano-node installation, refer to section 15.2 on how to reset the installation.โ€‹

โ€‹๐Ÿญ 1. Install Cabal and GHC

Press Ctrl+Alt+T. This will launch a terminal window.

First, update packages and install Ubuntu dependencies.

sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install git make tmux rsync htop curl build-essential pkg-config libffi-dev libgmp-dev libssl-dev libtinfo-dev libsystemd-dev zlib1g-dev make g++ tmux git jq wget libncursesw5 libtool autoconf -y

Install Libsodium.

mkdir ~/git
cd ~/git
git clone https://github.com/input-output-hk/libsodium
cd libsodium
git checkout 66f017f1
./autogen.sh
./configure
make
sudo make install

Install Cabal.

cd
wget https://downloads.haskell.org/~cabal/cabal-install-3.2.0.0/cabal-install-3.2.0.0-x86_64-unknown-linux.tar.xz
tar -xf cabal-install-3.2.0.0-x86_64-unknown-linux.tar.xz
rm cabal-install-3.2.0.0-x86_64-unknown-linux.tar.xz cabal.sig
mkdir -p ~/.local/bin
mv cabal ~/.local/bin/

Install GHC.

wget https://downloads.haskell.org/~ghc/8.6.5/ghc-8.6.5-x86_64-deb9-linux.tar.xz
tar -xf ghc-8.6.5-x86_64-deb9-linux.tar.xz
rm ghc-8.6.5-x86_64-deb9-linux.tar.xz
cd ghc-8.6.5
./configure
sudo make install

Update PATH to include Cabal and GHC and add exports. Your node's location will be in $NODE_HOME. The cluster configuration is set by $NODE_CONFIG, $NODE_URL and $NODE_BUILD_NUM.

echo PATH="~/.local/bin:$PATH" >> ~/.bashrc
echo export LD_LIBRARY_PATH="/usr/local/lib:$LD_LIBRARY_PATH" >> ~/.bashrc
echo export NODE_HOME=$HOME/cardano-my-node >> ~/.bashrc
echo export NODE_CONFIG=mainnet>> ~/.bashrc
echo export NODE_URL=cardano-mainnet >> ~/.bashrc
echo export NODE_BUILD_NUM=$(curl https://hydra.iohk.io/job/Cardano/iohk-nix/cardano-deployment/latest-finished/download/1/index.html | grep -e "build" | sed 's/.*build\/\([0-9]*\)\/download.*/\1/g') >> ~/.bashrc
echo export NETWORK_IDENTIFIER=\"--mainnet\" >> ~/.bashrc
source ~/.bashrc

Update cabal and verify the correct versions were installed successfully.

cabal update
cabal -V
ghc -V

Cabal library should be version 3.2.0.0 and GHC should be version 8.6.5

โ€‹๐Ÿ— 2. Build the node from source code

Download source code and switch to the latest tag. In this case, use tags/1.18.0

cd ~/git
git clone https://github.com/input-output-hk/cardano-node.git
cd cardano-node
git fetch --all
git checkout tags/1.18.0

Update the cabal config, project settings, and reset build folder.

echo -e "package cardano-crypto-praos\n flags: -external-libsodium-vrf" > cabal.project.local
sed -i $HOME/.cabal/config -e "s/overwrite-policy:/overwrite-policy: always/g"
rm -rf $HOME/git/cardano-node/dist-newstyle/build/x86_64-linux/ghc-8.6.5

Build the cardano-node from source code.

cabal build cardano-cli cardano-node

Building process may take a few minutes up to a few hours depending on your computer's processing power.

Copy cardano-cli and cardano-node files into bin directory.

sudo cp $(find ~/git/cardano-node/dist-newstyle/build -type f -name "cardano-cli") /usr/local/bin/cardano-cli
sudo cp $(find ~/git/cardano-node/dist-newstyle/build -type f -name "cardano-node") /usr/local/bin/cardano-node

Verify your cardano-cli and cardano-node are the expected versions.

cardano-node version
cardano-cli version

โ€‹๐Ÿ“ 3. Configure the node

Here you'll grab the config.json, genesis.json, and topology.json files needed to configure your node.

mkdir $NODE_HOME
cd $NODE_HOME
wget -N https://hydra.iohk.io/build/${NODE_BUILD_NUM}/download/1/${NODE_CONFIG}-byron-genesis.json
wget -N https://hydra.iohk.io/build/${NODE_BUILD_NUM}/download/1/${NODE_CONFIG}-topology.json
wget -N https://hydra.iohk.io/build/${NODE_BUILD_NUM}/download/1/${NODE_CONFIG}-shelley-genesis.json
wget -N https://hydra.iohk.io/build/${NODE_BUILD_NUM}/download/1/${NODE_CONFIG}-config.json

Run the following to modify config.json and

  • update ViewMode to "LiveView"

  • update TraceBlockFetchDecisions to "true"

sed -i ${NODE_CONFIG}-config.json \
-e "s/SimpleView/LiveView/g" \
-e "s/TraceBlockFetchDecisions\": false/TraceBlockFetchDecisions\": true/g"

Update .bashrc shell variables.

echo export CARDANO_NODE_SOCKET_PATH="$NODE_HOME/db/socket" >> ~/.bashrc
source ~/.bashrc

โ€‹๐Ÿ”ฎ 3.1 Configure the block-producer node and the relay nodes

A block producing node will be configured with various key-pairs needed for block generation (cold keys, KES hot keys and VRF hot keys). It can only connect to its relay nodes.

A relay node will not be in possession of any keys and will therefore be unable to produce blocks. It will be connected to its block-producing node, other relays and external nodes.

Setup the directory structure and copy the essential json files to each directory.

mkdir relaynode1
mkdir relaynode2
cp *.json relaynode1
cp *.json relaynode2

Configure topology.json file so that

  • only relay nodes connect to the public internet and your block-producing node

  • the block-producing node can only connect to your relay nodes

Update relaynode1 with the following. Simply copy/paste.

cat > $NODE_HOME/relaynode1/${NODE_CONFIG}-topology.json << EOF
{
"Producers": [
{
"addr": "127.0.0.1",
"port": 3000,
"valency": 2
},
{
"addr": "127.0.0.1",
"port": 3002,
"valency": 2
},
{
"addr": "relays-new.${NODE_URL}.iohk.io",
"port": 3001,
"valency": 2
}
]
}
EOF

Update relaynode2 with the following. Simply copy/paste.

cat > $NODE_HOME/relaynode2/${NODE_CONFIG}-topology.json << EOF
{
"Producers": [
{
"addr": "127.0.0.1",
"port": 3000,
"valency": 2
},
{
"addr": "127.0.0.1",
"port": 3001,
"valency": 2
},
{
"addr": "relays-new.${NODE_URL}.iohk.io",
"port": 3001,
"valency": 2
}
]
}
EOF

Update the block-producer node with the following. Simply copy/paste.

cat > $NODE_HOME/${NODE_CONFIG}-topology.json << EOF
{
"Producers": [
{
"addr": "127.0.0.1",
"port": 3001,
"valency": 2
},
{
"addr": "127.0.0.1",
"port": 3002,
"valency": 2
}
]
}
EOF

Valency tells the node how many connections to keep open. Only DNS addresses are affected. If value is 0, the address is ignored.

โ€‹โœจ Port Forwarding Tip: You'll need to forward ports 3001 and 3002 to your computer. Check with https://canyouseeme.org/โ€‹

โ€‹๐Ÿค– 4. Create startup scripts

The startup script contains all the variables needed to run a cardano-node such as directory, port, db path, config file, and topology file.

For your block-producing node:

cat > $NODE_HOME/startBlockProducingNode.sh << EOF
DIRECTORY=\$NODE_HOME
PORT=3000
HOSTADDR=0.0.0.0
TOPOLOGY=\${DIRECTORY}/${NODE_CONFIG}-topology.json
DB_PATH=\${DIRECTORY}/db
SOCKET_PATH=\${DIRECTORY}/db/socket
CONFIG=\${DIRECTORY}/${NODE_CONFIG}-config.json
cardano-node run --topology \${TOPOLOGY} --database-path \${DB_PATH} --socket-path \${SOCKET_PATH} --host-addr \${HOSTADDR} --port \${PORT} --config \${CONFIG}
EOF

For your relaynode1:

cat > $NODE_HOME/relaynode1/startRelayNode1.sh << EOF
DIRECTORY=\$NODE_HOME/relaynode1
PORT=3001
HOSTADDR=0.0.0.0
TOPOLOGY=\${DIRECTORY}/${NODE_CONFIG}-topology.json
DB_PATH=\${DIRECTORY}/db
SOCKET_PATH=\${DIRECTORY}/db/socket
CONFIG=\${DIRECTORY}/${NODE_CONFIG}-config.json
cardano-node run --topology \${TOPOLOGY} --database-path \${DB_PATH} --socket-path \${SOCKET_PATH} --host-addr \${HOSTADDR} --port \${PORT} --config \${CONFIG}
EOF

For your relaynode2:

cat > $NODE_HOME/relaynode2/startRelayNode2.sh << EOF
DIRECTORY=\$NODE_HOME/relaynode2
PORT=3002
HOSTADDR=0.0.0.0
TOPOLOGY=\${DIRECTORY}/${NODE_CONFIG}-topology.json
DB_PATH=\${DIRECTORY}/db
SOCKET_PATH=\${DIRECTORY}/db/socket
CONFIG=\${DIRECTORY}/${NODE_CONFIG}-config.json
cardano-node run --topology \${TOPOLOGY} --database-path \${DB_PATH} --socket-path \${SOCKET_PATH} --host-addr \${HOSTADDR} --port \${PORT} --config \${CONFIG}
EOF

The startStakePool.sh script will automatically start your relays and block-producing node.

cat > $NODE_HOME/startStakePool.sh << EOF
#!/bin/bash
SESSION=$(whoami)
tmux has-session -t \$SESSION 2>/dev/null
if [ \$? != 0 ]; then
# tmux attach-session -t \$SESSION
tmux new-session -s \$SESSION -n window -d
tmux split-window -v
tmux split-window -h
tmux select-pane -t \$SESSION:window.0
tmux split-window -h
tmux send-keys -t \$SESSION:window.0 $NODE_HOME/startBlockProducingNode.sh Enter
tmux send-keys -t \$SESSION:window.1 htop Enter
tmux send-keys -t \$SESSION:window.2 $NODE_HOME/relaynode1/startRelayNode1.sh Enter
tmux send-keys -t \$SESSION:window.3 $NODE_HOME/relaynode2/startRelayNode2.sh Enter
echo Stakepool started. \"tmux a\" to view.
fi
EOF

The stopStakePool.sh script will automatically stop your relays and block-producing node.

cat > $NODE_HOME/stopStakePool.sh << EOF
#!/bin/bash
SESSION=$(whoami)
tmux has-session -t \$SESSION 2>/dev/null
if [ \$? != 0 ]; then
echo Stakepool not running.
else
echo Stopped stakepool.
tmux kill-session -t \$SESSION
fi
EOF

โ€‹โœ… 5. Start the node

Press Ctrl+Alt+T. This will launch a terminal window.

Add execute permissions to the script, start your stake pool, and begin syncing the blockchain!

cd $NODE_HOME
chmod +x startBlockProducingNode.sh
chmod +x relaynode1/startRelayNode1.sh
chmod +x relaynode2/startRelayNode2.sh
chmod +x startStakePool.sh
chmod +x stopStakePool.sh
./startStakePool.sh

Your stake pool is running in a tmux terminal session now. To attach to the terminal, run the following.

tmux a

Smash the Maximize Window icon for best viewing effect.

To detach from a tmux session,

Press Ctrl+b+d.

โ€‹โœจ Tips for using tmux with the [start|stop]StakePool.sh scripts.

  • Ctrl + b + arrow key to navigate around panes

  • Ctrl + b + z to zoom

Congratulations! Your node is running successfully now. Let it sync up.

โ€‹โš™ 6. Generate block-producer keys

The block-producer node requires you to create 3 keys as defined in the Shelley ledger specs:

  • stake pool cold key

  • stake pool hot key (KES key)

  • stake pool VRF key

First, make a KES key pair.

cd $NODE_HOME
cardano-cli shelley node key-gen-KES \
--verification-key-file kes.vkey \
--signing-key-file kes.skey

KES (key evolving signature) keys are created to secure your stake pool against hackers who might compromise your keys. On mainnet, these will be regenerated every 90 days.

Cold keys should be generated and stored on an unconnected air-gapped offline machine. Copy cardano-cli binary over and run the node key-gen commands. The cold keys are the files stored in ~/cold-keys.

Make a directory to store your cold keys

mkdir ~/cold-keys
pushd ~/cold-keys

Make a set of cold keys and create the cold counter file.

cardano-cli shelley node key-gen \
--cold-verification-key-file node.vkey \
--cold-signing-key-file node.skey \
--operational-certificate-issue-counter node.counter

Be sure to back up your all your keys to another secure storage device. Make multiple copies.

Determine the number of slots per KES period from the genesis file.

pushd +1
slotsPerKESPeriod=$(cat $NODE_HOME/${NODE_CONFIG}-shelley-genesis.json | jq -r '.slotsPerKESPeriod')
echo slotsPerKESPeriod: ${slotsPerKESPeriod}

Before continuing, your node must be fully synchronized to the blockchain. Otherwise, you won't calculate the latest KES period. Your node is synchronized when the epoch and slot# is equal to that found on a block explorer such as https://pooltool.io/โ€‹

slotNo=$(cardano-cli shelley query tip $NETWORK_IDENTIFIER | jq -r '.slotNo')
echo slotNo: ${slotNo}

Find the kesPeriod by dividing the slot tip number by the slotsPerKESPeriod.

kesPeriod=$((${slotNo} / ${slotsPerKESPeriod}))
echo kesPeriod: ${kesPeriod}

With this calculation, you can generate a operational certificate for your pool.

Stake pool operators must provide an operational certificate to verify that the pool has the authority to run. The certificate includes the operatorโ€™s signature, and includes key information about the pool (addresses, keys, etc.). Operational certificates represent the link between the operatorโ€™s offline key and their operational key.

cardano-cli shelley node issue-op-cert \
--kes-verification-key-file kes.vkey \
--cold-signing-key-file ~/cold-keys/node.skey \
--operational-certificate-issue-counter ~/cold-keys/node.counter \
--kes-period $kesPeriod \
--out-file node.cert

Make a VRF key pair.

cardano-cli shelley node key-gen-VRF \
--verification-key-file vrf.vkey \
--signing-key-file vrf.skey

Open a new terminal window with Ctrl+Alt+T and stop your stake pool by running the following:

cd $NODE_HOME
./stopStakePool.sh

Update your startup script with the new KES, VRF and Operation Certificate.

cat > $NODE_HOME/startBlockProducingNode.sh << EOF
DIRECTORY=\$NODE_HOME
PORT=3000
HOSTADDR=0.0.0.0
TOPOLOGY=\${DIRECTORY}/${NODE_CONFIG}-topology.json
DB_PATH=\${DIRECTORY}/db
SOCKET_PATH=\${DIRECTORY}/db/socket
CONFIG=\${DIRECTORY}/${NODE_CONFIG}-config.json
KES=\${DIRECTORY}/kes.skey
VRF=\${DIRECTORY}/vrf.skey
CERT=\${DIRECTORY}/node.cert
cardano-node run --topology \${TOPOLOGY} --database-path \${DB_PATH} --socket-path \${SOCKET_PATH} --host-addr \${HOSTADDR} --port \${PORT} --config \${CONFIG} --shelley-kes-key \${KES} --shelley-vrf-key \${VRF} --shelley-operational-certificate \${CERT}
EOF

To operate a stake pool, two sets of keys are needed: they KES key (hot) and the cold key. Cold keys generate new hot keys periodically.

Now start your stake pool.

cd $NODE_HOME
./startStakePool.sh
tmux a

โ€‹๐Ÿ” 7. Setup payment and staking keys

First, obtain the protocol-parameters.

Wait for the block-producing node to start syncing before continuing if you get this error message.

cardano-cli: Network.Socket.connect: : does not exist (No such file or directory)

cardano-cli shelley query protocol-parameters \
$NETWORK_IDENTIFIER \
--out-file params.json

Payment keys are used to send and receive payments and staking keys are used to manage stake delegations.

There are two ways to create your payment and stake key pair. Pick the one that best suits your needs.

โ€‹๐Ÿ”ฅ Critical Operational Security Advice: payment and stake keys must be generated and used to build transactions in an cold environment. In other words, an air-gapped offline machine. Copy cardano-cli binary over to your offline machine and run the CLI method or mnemonic method. The only steps performed online in a hot environment are those steps that require live data. Namely the follow type of steps:

  • querying the current slot tip

  • querying the balance of an address

  • submitting a transaction

CLI Method
Mnemonic Method
CLI Method

Create a new payment key pair: payment.skey & payment.vkey

cardano-cli shelley address key-gen \
--verification-key-file payment.vkey \
--signing-key-file payment.skey

Create a new stake address key pair: stake.skey & stake.vkey

cardano-cli shelley stake-address key-gen \
--verification-key-file stake.vkey \
--signing-key-file stake.skey

Create your stake address from the stake address verification key and store it in stake.addr

cardano-cli shelley stake-address build \
--staking-verification-key-file stake.vkey \
--out-file stake.addr \
--mainnet

Build a payment address for the payment key payment.vkey which will delegate to the stake address, stake.vkey

cardano-cli shelley address build \
--payment-verification-key-file payment.vkey \
--staking-verification-key-file stake.vkey \
--out-file payment.addr \
--mainnet
Mnemonic Method

Credits to ilap for creating this process.

Benefits: Track and control pool rewards from any wallet (Daedalus, Yoroi or any other wallet) that support stakings.

Create a 15-word or 24-word length shelley compatible mnemonic with Daedalus or Yoroi on a offline machine preferred.

Download cardano-wallet.

cd $NODE_HOME
wget https://hydra.iohk.io/build/3662127/download/1/cardano-wallet-shelley-2020.7.28-linux64.tar.gz

Verify the legitimacy of cardano-wallet by checking the sha256 hash found in the Details button.โ€‹

echo "f75e5b2b4cc5f373d6b1c1235818bcab696d86232cb2c5905b2d91b4805bae84 *cardano-wallet-shelley-2020.7.28-linux64.tar.gz" | shasum -a 256 --check

Example valid output:

cardano-wallet-shelley-2020.7.28-linux64.tar.gz: OK

Only proceed if the sha256 check passes with OK!

Extract the wallet files and cleanup.

tar -xvf cardano-wallet-shelley-2020.7.28-linux64.tar.gz
rm cardano-wallet-shelley-2020.7.28-linux64.tar.gz

CreateextractPoolStakingKeys.sh script.

cat > extractPoolStakingKeys.sh << HERE
#!/bin/bash
โ€‹
CADDR=\${CADDR:=\$( which cardano-address )}
[[ -z "\$CADDR" ]] && ( echo "cardano-address cannot be found, exiting..." >&2 ; exit 127 )
โ€‹
CCLI=\${CCLI:=\$( which cardano-cli )}
[[ -z "\$CCLI" ]] && ( echo "cardano-cli cannot be found, exiting..." >&2 ; exit 127 )
โ€‹
OUT_DIR="\$1"
[[ -e "\$OUT_DIR" ]] && {
echo "The \"\$OUT_DIR\" is already exist delete and run again." >&2
exit 127
} || mkdir -p "\$OUT_DIR" && pushd "\$OUT_DIR" >/dev/null
โ€‹
shift
MNEMONIC="\$*"
โ€‹
# Generate the master key from mnemonics and derive the stake account keys
# as extended private and public keys (xpub, xprv)
echo "\$MNEMONIC" |\
"\$CADDR" key from-recovery-phrase Shelley > root.prv
โ€‹
cat root.prv |\
"\$CADDR" key child 1852H/1815H/0H/2/0 > stake.xprv
โ€‹
cat root.prv |\
"\$CADDR" key child 1852H/1815H/0H/0/0 > payment.xprv
โ€‹
TESTNET=0
MAINNET=1
NETWORK=\$MAINNET
โ€‹
cat payment.xprv |\
"\$CADDR" key public | tee payment.xpub |\
"\$CADDR" address payment --network-tag \$NETWORK |\
"\$CADDR" address delegation \$(cat stake.xprv | "\$CADDR" key public | tee stake.xpub) |\
tee base.addr_candidate |\
"\$CADDR" address inspect
echo "Generated from 1852H/1815H/0H/{0,2}/0"
cat base.addr_candidate
echo
โ€‹
# XPrv/XPub conversion to normal private and public key, keep in mind the
# keypars are not a valind Ed25519 signing keypairs.
TESTNET_MAGIC="--testnet-magic 42"
MAINNET_MAGIC="--mainnet"
MAGIC="\$MAINNET_MAGIC"
โ€‹
SESKEY=\$( cat stake.xprv | bech32 | cut -b -128 )\$( cat stake.xpub | bech32)
PESKEY=\$( cat payment.xprv | bech32 | cut -b -128 )\$( cat payment.xpub | bech32)
โ€‹
cat << EOF > stake.skey
{
"type": "StakeExtendedSigningKeyShelley_ed25519_bip32",
"description": "",
"cborHex": "5880\$SESKEY"
}
EOF
โ€‹
cat << EOF > payment.skey
{
"type": "PaymentExtendedSigningKeyShelley_ed25519_bip32",
"description": "Payment Signing Key",
"cborHex": "5880\$PESKEY"
}
EOF
โ€‹
"\$CCLI" shelley key verification-key --signing-key-file stake.skey --verification-key-file stake.evkey
"\$CCLI" shelley key verification-key --signing-key-file payment.skey --verification-key-file payment.evkey
โ€‹
"\$CCLI" shelley key non-extended-key --extended-verification-key-file payment.evkey --verification-key-file payment.vkey
"\$CCLI" shelley key non-extended-key --extended-verification-key-file stake.evkey --verification-key-file stake.vkey
โ€‹
โ€‹
"\$CCLI" shelley stake-address build --stake-verification-key-file stake.vkey \$MAGIC > stake.addr
"\$CCLI" shelley address build --payment-verification-key-file payment.vkey \$MAGIC > payment.addr
"\$CCLI" shelley address build \
--payment-verification-key-file payment.vkey \
--stake-verification-key-file stake.vkey \
\$MAGIC > base.addr
โ€‹
echo "Important the base.addr and the base.addr_candidate must be the same"
diff base.addr base.addr_candidate
popd >/dev/null
HERE

Add permissions and update PATH.

chmod +x extractPoolStakingKeys.sh
echo PATH="$(pwd)/cardano-wallet-shelley-2020.7.28:$PATH" >> ~/.bashrc
source ~/.bashrc

Extract your keys. Update the command with your mnemonic phrase.

./extractPoolStakingKeys.sh extractedPoolKeys/ <15|24-word length mnemonic>

Important the base.addr and the base.addr_candidate must be the same. Review the screen output. Base.addr is the payment address to be funded.

Your new staking keys are in the folder extractedPoolKeys/

Now move payment/stake key pair over to your $NODE_HOME for use with your stake pool.

cd extractedPoolKeys/
cp stake.vkey stake.skey stake.addr payment.vkey payment.skey base.addr $NODE_HOME
cd $NODE_HOME
#Rename to base.addr file to payment.addr
mv base.addr payment.addr

Clear the bash history in order to protect your mnemonic phrase and remove the cardano-wallet files.

history -c && history -w
rm -rf $NODE_HOME/cardano-wallet-shelley-2020.7.28

Finally close all your terminal windows and open new ones with zero history.

Awesome. Now you can track your pool rewards in your wallet.

Next step is to fund your payment address.

Mainnet
Release Candidate
Shelley Testnet
Mainnet

Payment address can be funded from

  • your Daedalus / Yoroi wallet

  • if you were part of the ITN, you can convert your keys.

Run the following to find your payment address.

cat payment.addr
Release Candidate

Payment address can be funded from

  • โ€‹Public testnet faucetโ€‹

  • your Byron mainnet funds based on a snapshot from 07/20 00:00 UTC.

  • if you were part of the ITN, you can convert your address as specified above.

Run the following to find your payment address.

cat payment.addr
Shelley Testnet

Visit the faucet to request funds to your payment.addr

Run the following to find your address.

cat payment.addr

Paste this address and fill out the captcha.

The Shelly Testnet Faucet can deliver up to 100,000 fADA every 24 hours.

After funding your account, check your payment address balance.

Before continuing, your nodes must be fully synchronized to the blockchain. Otherwise, you won't see your funds.

cardano-cli shelley query utxo \
--address $(cat payment.addr) \
$NETWORK_IDENTIFIER

You should see output similar to this. This is your unspent transaction output (UXTO).

TxHash TxIx Lovelace
----------------------------------------------------------------------------------------
100322a39d02c2ead.... 0 1000000000

โ€‹๐Ÿ‘ฉ๐Ÿ’ป 8. Register your stake address

Create a certificate, stake.cert, using the stake.vkey

cardano-cli shelley stake-address registration-certificate \
--staking-verification-key-file stake.vkey \
--out-file stake.cert

You need to find the tip of the blockchain to set the ttl parameter properly.

currentSlot=$(cardano-cli shelley query tip $NETWORK_IDENTIFIER | jq -r '.slotNo')
echo Current Slot: $currentSlot

Find your balance and UTXOs.

cardano-cli shelley query utxo \
--address $(cat payment.addr) \
$NETWORK_IDENTIFIER > fullUtxo.out
โ€‹
tail -n +3 fullUtxo.out | sort -k3 -nr > balance.out
โ€‹
cat balance.out
โ€‹
tx_in=""
total_balance=0
while read -r utxo; do
in_addr=$(awk '{ print $1 }' <<< "${utxo}")
idx=$(awk '{ print $2 }' <<< "${utxo}")
utxo_balance=$(awk '{ print $3 }' <<< "${utxo}")
total_balance=$((${total_balance}+${utxo_balance}))
echo TxHash: ${in_addr}#${idx}
echo ADA: ${utxo_balance}
tx_in="${tx_in} --tx-in ${in_addr}#${idx}"
done < balance.out
txcnt=$(cat balance.out | wc -l)
echo Total ADA balance: ${total_balance}
echo Number of UTXOs: ${txcnt}

Find the keyDeposit value.

keyDeposit=$(cat $NODE_HOME/params.json | jq -r '.keyDeposit')
echo keyDeposit: $keyDeposit

Registration of a stake address certificate (keyDeposit) costs 2000000 lovelace.

Run the build-raw transaction command

The ttl value must be greater than the current tip. In this example, we use current slot + 10000.

cardano-cli shelley transaction build-raw \
${tx_in} \
--tx-out $(cat payment.addr)+0 \
--ttl $(( ${currentSlot} + 10000)) \
--fee 0 \
--out-file tx.tmp \
--certificate stake.cert

Calculate the current minimum fee:

fee=$(cardano-cli shelley transaction calculate-min-fee \
--tx-body-file tx.tmp \
--tx-in-count ${txcnt} \
--tx-out-count 1 \
$NETWORK_IDENTIFIER \
--witness-count 2 \
--byron-witness-count 0 \
--protocol-params-file params.json | awk '{ print $1 }')
echo fee: $fee

Ensure your balance is greater than cost of fee + keyDeposit or this will not work.

Calculate your change output.

txOut=$((${total_balance}-${keyDeposit}-${fee}))
echo Change Output: ${txOut}

Build your transaction which will register your stake address.

cardano-cli shelley transaction build-raw \
${tx_in} \
--tx-out $(cat payment.addr)+${txOut} \
--ttl $(( ${currentSlot} + 10000)) \
--fee ${fee} \
--certificate-file stake.cert \
--out-file tx.raw

Sign the transaction with both the payment and stake secret keys.

cardano-cli shelley transaction sign \
--tx-body-file tx.raw \
--signing-key-file payment.skey \
--signing-key-file stake.skey \
$NETWORK_IDENTIFIER \
--out-file tx.signed

Send the signed transaction.

cardano-cli shelley transaction submit \
--tx-file tx.signed \
$NETWORK_IDENTIFIER

โ€‹๐Ÿ“„ 9. Register your stake pool

Create your pool's metadata with a JSON file. Update with your pool information.

ticker must be between 3-5 characters in length.

description cannot exceed 255 characters in length.

cat > poolMetaData.json << EOF
{
"name": "MyPoolName",
"description": "My pool description",
"ticker": "MPN",
"homepage": "https://myadapoolnamerocks.com"
}
EOF

Calculate the hash of your metadata file.

cardano-cli shelley stake-pool metadata-hash --pool-metadata-file poolMetaData.json > poolMetaDataHash.txt

Now upload your poolMetaData.json to your website or a public website such as https://pages.github.com/โ€‹

Refer to the following quick guide if you need help hosting your metadata on github.com

Find the minimum pool cost.

minPoolCost=$(cat $NODE_HOME/params.json | jq -r .minPoolCost)
echo minPoolCost: ${minPoolCost}

minPoolCost is 340000000 lovelace or 340 ADA. Therefore, your --pool-cost must be at a minimum this amount.

Create a registration certificate for your stake pool. Update with your metadata URL and relay IP address.

metadata-url must be no longer than 64 characters.

cardano-cli shelley stake-pool registration-certificate \
--cold-verification-key-file ~/cold-keys/node.vkey \
--vrf-verification-key-file vrf.vkey \
--pool-pledge 100000000 \
--pool-cost 345000000 \
--pool-margin 0.15 \
--pool-reward-account-verification-key-file stake.vkey \
--pool-owner-stake-verification-key-file stake.vkey \
$NETWORK_IDENTIFIER \
--pool-relay-port 3001 \
--pool-relay-ipv4 <your relay IP address> \
--metadata-url <url where you uploaded poolMetaData.json> \
--metadata-hash $(cat poolMetaDataHash.txt) \
--out-file pool.cert

Here we are pledging 100 ADA with a fixed pool cost of 345 ADA and a pool margin of 15%.

Pledge stake to your stake pool.

cardano-cli shelley stake-address delegation-certificate \
--staking-verification-key-file stake.vkey \
--cold-verification-key-file ~/cold-keys/node.vkey \
--out-file deleg.cert

This creates a delegation certificate which delegates funds from all stake addresses associated with key stake.vkey to the pool belonging to cold key node.vkey

A stake pool owner's promise to fund their own pool is called Pledge.

  • Your balance needs to be greater than the pledge amount.

  • You pledge funds are not moved anywhere. In this guide's example, the pledge remains in the stake pool's owner keys, specifically payment.addr

  • Failing to fulfill pledge will result in missed block minting opportunities and your delegators would miss rewards.

  • Your pledge is not locked up. You are free to transfer your funds.

You need to find the tip of the blockchain to set the ttl parameter properly.

currentSlot=$(cardano-cli shelley query tip $NETWORK_IDENTIFIER | jq -r '.slotNo')
echo Current Slot: $currentSlot

Find your balance and UTXOs.

cardano-cli shelley query utxo \
--address $(cat payment.addr) \
$NETWORK_IDENTIFIER > fullUtxo.out
โ€‹
tail -n +3 fullUtxo.out | sort -k3 -nr > balance.out
โ€‹
cat balance.out
โ€‹
tx_in=""
total_balance=0
while read -r utxo; do
in_addr=$(awk '{ print $1 }' <<< "${utxo}")
idx=$(awk '{ print $2 }' <<< "${utxo}")
utxo_balance=$(awk '{ print $3 }' <<< "${utxo}")
total_balance=$((${total_balance}+${utxo_balance}))
echo TxHash: ${in_addr}#${idx}
echo ADA: ${utxo_balance}
tx_in="${tx_in} --tx-in ${in_addr}#${idx}"
done < balance.out
txcnt=$(cat balance.out | wc -l)
echo Total ADA balance: ${total_balance}
echo Number of UTXOs: ${txcnt}

Find the deposit fee for a pool.

poolDeposit=$(cat $NODE_HOME/params.json | jq -r '.poolDeposit')
echo poolDeposit: $poolDeposit

Run the build-raw transaction command.

The ttl value must be greater than the current tip. In this example, we use current slot + 10000.

cardano-cli shelley transaction build-raw \
${tx_in} \
--tx-out $(cat payment.addr)+$(( ${total_balance} - ${poolDeposit})) \
--ttl $(( ${currentSlot} + 10000)) \
--fee 0 \
--certificate-file pool.cert \
--certificate-file deleg.cert \
--out-file tx.tmp

Calculate the minimum fee:

fee=$(cardano-cli shelley transaction calculate-min-fee \
--tx-body-file tx.tmp \
--tx-in-count ${txcnt} \
--tx-out-count 1 \
$NETWORK_IDENTIFIER \
--witness-count 3 \
--byron-witness-count 0 \
--protocol-params-file params.json | awk '{ print $1 }')
echo fee: $fee

Ensure your balance is greater than cost of fee + minPoolCost or this will not work.

Calculate your change output.

txOut=$((${total_balance}-${poolDeposit}-${fee}))
echo txOut: ${txOut}

Build the transaction.

cardano-cli shelley transaction build-raw \
${tx_in} \
--tx-out $(cat payment.addr)+${txOut} \
--ttl $(( ${currentSlot} + 10000)) \
--fee ${fee} \
--certificate-file pool.cert \
--certificate-file deleg.cert \
--out-file tx.raw

Sign the transaction.

cardano-cli shelley transaction sign \
--tx-body-file tx.raw \
--signing-key-file payment.skey \
--signing-key-file ~/cold-keys/node.skey \
--signing-key-file stake.skey \
$NETWORK_IDENTIFIER \
--out-file tx.signed

Send the transaction.

cardano-cli shelley transaction submit \
--tx-file tx.signed \
$NETWORK_IDENTIFIER

โ€‹๐Ÿฃ 10. Locate your Stake pool ID and verify everything is working

Your stake pool ID can be computed with:

cardano-cli shelley stake-pool id --verification-key-file ~/cold-keys/node.vkey > stakepoolid.txt
cat stakepoolid.txt

Now that you have your stake pool ID, verify it's included in the blockchain.

cardano-cli shelley query ledger-state $NETWORK_IDENTIFIER | grep publicKey | grep $(cat stakepoolid.txt)

A non-empty string return means you're registered! ๐Ÿ‘

With your stake pool ID, now you can find your data on block explorers such as https://pooltool.io/โ€‹

โ€‹โš™ 11. Configure your topology files

Shelley testnet has been launched without peer-to-peer (p2p) node discovery so that means we will need to manually add trusted nodes in order to configure our topology. This is a critical step as skipping this step will result in your minted blocks being orphaned by the rest of the network.

There are two ways to configure your topology files.

  • topologyUpdate.sh method is automated and works after 4 hours.

  • Pooltool.io method gives you control over who your nodes connect to.

topologyUpdater.sh Method
Pooltool.io Method
topologyUpdater.sh Method

โ€‹๐Ÿš€ Publishing your Relay Node with topologyUpdater.sh

Credits to GROWPOOL for this addition and credits to CNTOOLS Guild OPS on creating this process.

Create the topologyUpdater.sh script which publishes your node information to a topology fetch list.

cat > $NODE_HOME/topologyUpdater.sh << EOF
#!/bin/bash
# shellcheck disable=SC2086,SC2034
USERNAME=$(whoami)
CNODE_PORT=3001 # must match your relay node port as set in the startup command
CNODE_HOSTNAME="CHANGE ME" # optional. must resolve to the IP you are requesting from
CNODE_BIN="/usr/local/bin"
CNODE_HOME=$NODE_HOME
CNODE_LOG_DIR="\${CNODE_HOME}/logs"
GENESIS_JSON="\${CNODE_HOME}/${NODE_CONFIG}-shelley-genesis.json"
NETWORKID=\$(jq -r .networkId \$GENESIS_JSON)
CNODE_VALENCY=1 # optional for multi-IP hostnames
NWMAGIC=\$(jq -r .networkMagic < \$GENESIS_JSON)
[[ "\${NETWORKID}" = "Mainnet" ]] && HASH_IDENTIFIER="--mainnet" || HASH_IDENTIFIER="--testnet-magic \${NWMAGIC}"
[[ "\${NWMAGIC}" = "764824073" ]] && NETWORK_IDENTIFIER="--mainnet" || NETWORK_IDENTIFIER="--testnet-magic \${NWMAGIC}"
export PATH="\${CNODE_BIN}:\${PATH}"
export CARDANO_NODE_SOCKET_PATH="\${CNODE_HOME}/db/socket"
blockNo=\$(cardano-cli shelley query tip \${NETWORK_IDENTIFIER} | jq -r .blockNo )
# Note:
# if you run your node in IPv4/IPv6 dual stack network configuration and want announced the
# IPv4 address only please add the -4 parameter to the curl command below (curl -4 -s ...)
if [ "\${CNODE_HOSTNAME}" != "CHANGE ME" ]; then
T_HOSTNAME="&hostname=\${CNODE_HOSTNAME}"
else
T_HOSTNAME=''
fi
โ€‹
if [ ! -d \${CNODE_LOG_DIR} ]; then
mkdir -p \${CNODE_LOG_DIR};
fi
curl -s "https://api.clio.one/htopology/v1/?port=\${CNODE_PORT}&blockNo=\${blockNo}&valency=\${CNODE_VALENCY}&magic=\${NWMAGIC}\${T_HOSTNAME}" | tee -a \$CNODE_LOG_DIR/topologyUpdater_lastresult.json
EOF

Add permissions and run the updater script.

cd $NODE_HOME
chmod +x topologyUpdater.sh
./topologyUpdater.sh

When the topologyUpdater.sh runs successfully, you will see

{ "resultcode": "201", "datetime":"2020-07-28 01:23:45", "clientIp": "1.2.3.4", "iptype": 4, "msg": "nice to meet you" }

Every time the script runs and updates your IP, a log is created in $NODE_HOME/logs

Add a crontab job to automatically run topologyUpdater.sh every hour on the 22nd minute. You can change the 22 value to your own preference.

cat > $NODE_HOME/crontab-fragment.txt << EOF
22 * * * * ${NODE_HOME}/topologyUpdater.sh
EOF
crontab -l | cat - crontab-fragment.txt >crontab.txt && crontab crontab.txt
rm crontab-fragment.txt

After four hours and four updates, your node IP will be registered in the topology fetch list.

โ€‹๐Ÿคนโ™€ Update your relay node topology files

Complete this section after four hours when your relay node IP is properly registered.

Create relay-topology_pull.sh script which fetches your relay node buddies and updates your topology file.

cat > $NODE_HOME/relay-topology_pull.sh << EOF
#!/bin/bash
BLOCKPRODUCING_IP=127.0.0.1
BLOCKPRODUCING_PORT=3000
RELAYNODE1_IP=127.0.0.1
RELAYNODE1_PORT=3001
RELAYNODE2_IP=127.0.0.1
RELAYNODE2_PORT=3002
curl -s -o $NODE_HOME/relaynode1/${NODE_CONFIG}-topology.json "https://api.clio.one/htopology/v1/fetch/?max=20&customPeers=\${BLOCKPRODUCING_IP}:\${BLOCKPRODUCING_PORT}:2|\${RELAYNODE1_IP}:\${RELAYNODE1_PORT}|relays-new.${NODE_URL}.iohk.io:3001:2|\${RELAYNODE2_IP}:\${RELAYNODE2_PORT}"
cp $NODE_HOME/relaynode1/${NODE_CONFIG}-topology.json $NODE_HOME/relaynode2/${NODE_CONFIG}-topology.json
EOF

Add permissions and pull new topology files.

chmod +x relay-topology_pull.sh
./relay-topology_pull.sh

The new topology takes after after restarting your stake pool.

./stopStakePool.sh
./startStakePool.sh
tmux a

Don't forget to restart your nodes after every time you fetch the topology!

Pooltool.io Method
  1. Create an account and login

  2. Search for your stakepool id

  3. Click โžก Pool Details > Manage > CLAIM THIS POOL

  4. Fill in your pool name and pool URL if you have one.

  5. Fill in your Private Nodes and Your Relays as follows.

You can find your public IP with https://www.whatismyip.com/ or

curl http://ifconfig.me/ip

Add requests for nodes or "buddies" to each of your relay nodes. Make sure you include the IOHK node and your private nodes.

IOHK's node address is:

relays-new.cardano-mainnet.iohk.io

IOHK's node port is:

3001

For example, on relaynode1's buddies you should add requests for

  • your private BlockProducingNode

  • your private RelayNode2

  • IOHK's node

  • and any other buddy/friendly nodes your can find or know

For example, on relaynode2's buddies you should add requests for

  • your private BlockProducingNode

  • your private RelayNode1

  • IOHK's node

  • and any other buddy/friendly nodes your can find or know

A relay node connection is not established until there is a request and an approval.

For relaynode1, create a get_buddies.sh script to update your topology.json file.

cat > $NODE_HOME/relaynode1/get_buddies.sh << EOF
#!/usr/bin/env bash
โ€‹
# YOU CAN PASS THESE STRINGS AS ENVIRONMENTAL VARIABLES, OR EDIT THEM IN THE SCRIPT HERE
if [ -z "\$PT_MY_POOL_ID" ]; then
## CHANGE THESE TO SUIT YOUR POOL TO YOUR POOL ID AS ON THE EXPLORER
PT_MY_POOL_ID="XXXXXXXX"
fi
โ€‹
if [ -z "\$PT_MY_API_KEY" ]; then
## GET THIS FROM YOUR ACCOUNT PROFILE PAGE ON POOLTOOL WEBSITE
PT_MY_API_KEY="XXXXXXXX"
fi
โ€‹
if [ -z "\$PT_MY_NODE_ID" ]; then
## GET THIS FROM YOUR POOL MANAGE TAB ON POOLTOOL WEBSITE
PT_MY_NODE_ID="XXXXXXXX"
fi
โ€‹
if [ -z "\$PT_TOPOLOGY_FILE" ]; then
## SET THIS TO THE LOCATION OF YOUR TOPOLOGY FILE THAT YOUR NODE USES
PT_TOPOLOGY_FILE="$NODE_HOME/relaynode1/${NODE_CONFIG}-topology.json"
fi
โ€‹
JSON="\$(jq -n --compact-output --arg MY_API_KEY "\$PT_MY_API_KEY" --arg MY_POOL_ID "\$PT_MY_POOL_ID" --arg MY_NODE_ID "\$PT_MY_NODE_ID" '{apiKey: \$MY_API_KEY, nodeId: \$MY_NODE_ID, poolId: \$MY_POOL_ID}')"
echo "Packet Sent: \$JSON"
RESPONSE="\$(curl -s -H "Accept: application/json" -H "Content-Type:application/json" -X POST --data "\$JSON" "https://api.pooltool.io/v0/getbuddies")"
SUCCESS="\$(echo \$RESPONSE | jq '.success')"
if [ \$SUCCESS ]; then
echo "Success"
echo \$RESPONSE | jq '. | {Producers: .message}' > \$PT_TOPOLOGY_FILE
echo "Topology saved to \$PT_TOPOLOGY_FILE. Note topology will only take effect next time you restart your node"
else
echo "Failure "
echo \$RESPONSE | jq '.message'
fi
EOF

For relaynode2, create a get_buddies.sh script to update your topology.json file.

cat > $NODE_HOME/relaynode2/get_buddies.sh << EOF
#!/usr/bin/env bash
โ€‹
# YOU CAN PASS THESE STRINGS AS ENVIRONMENTAL VARIABLES, OR EDIT THEM IN THE SCRIPT HERE
if [ -z "\$PT_MY_POOL_ID" ]; then
## CHANGE THESE TO SUIT YOUR POOL TO YOUR POOL ID AS ON THE EXPLORER
PT_MY_POOL_ID="XXXXXXXX"
fi
โ€‹
if [ -z "\$PT_MY_API_KEY" ]; then
## GET THIS FROM YOUR ACCOUNT PROFILE PAGE ON POOLTOOL WEBSITE
PT_MY_API_KEY="XXXXXXXX"
fi
โ€‹
if [ -z "\$PT_MY_NODE_ID" ]; then
## GET THIS FROM YOUR POOL MANAGE TAB ON POOLTOOL WEBSITE
PT_MY_NODE_ID="XXXXXXXX"
fi
โ€‹
if [ -z "\$PT_TOPOLOGY_FILE" ]; then
## SET THIS TO THE LOCATION OF YOUR TOPOLOGY FILE THAT YOUR NODE USES
PT_TOPOLOGY_FILE="$NODE_HOME/relaynode2/${NODE_CONFIG}-topology.json"
fi
โ€‹
JSON="\$(jq -n --compact-output --arg MY_API_KEY "\$PT_MY_API_KEY" --arg MY_POOL_ID "\$PT_MY_POOL_ID" --arg MY_NODE_ID "\$PT_MY_NODE_ID" '{apiKey: \$MY_API_KEY, nodeId: \$MY_NODE_ID, poolId: \$MY_POOL_ID}')"
echo "Packet Sent: \$JSON"
RESPONSE="\$(curl -s -H "Accept: application/json" -H "Content-Type:application/json" -X POST --data "\$JSON" "https://api.pooltool.io/v0/getbuddies")"
SUCCESS="\$(echo \$RESPONSE | jq '.success')"
if [ \$SUCCESS ]; then
echo "Success"
echo \$RESPONSE | jq '. | {Producers: .message}' > \$PT_TOPOLOGY_FILE
echo "Topology saved to \$PT_TOPOLOGY_FILE. Note topology will only take effect next time you restart your node"
else
echo "Failure "
echo \$RESPONSE | jq '.message'
fi
EOF

For each of your relay nodes, update the following variables from pooltool.io into your get_buddies.sh file

  • PT_MY_POOL_ID

  • PT_MY_API_KEY

  • PT_MY_NODE_ID

Update your get_buddies.sh scripts with this information.

Use nano to edit your files.

nano $NODE_HOME/relaynode1/get_buddies.sh

nano $NODE_HOME/relaynode2/get_buddies.sh

Add execute permissions to these scripts. Run the scripts to update your topology files.

cd $NODE_HOME
chmod +x relaynode1/get_buddies.sh
chmod +x relaynode2/get_buddies.sh
./relaynode1/get_buddies.sh
./relaynode2/get_buddies.sh

Stop and then restart your stakepool in order for the new topology settings to take effect.

./stopStakePool.sh
./startStakePool.sh
tmux a

As your REQUESTS are approved, you must re-run the get_buddies.sh script to pull the latest topology data. Restart your relay nodes afterwards.

โ€‹๐Ÿ”ฅ Critical step: In order to be a functional stake pool ready to mint blocks, you must see the TXs processed number increasing. If not, review your topology file and ensure your relay buddies are well connected and ideally, minted some blocks.

โ€‹๐Ÿ›‘ Critical Reminder: The only stake pool keys and certs that are required to run a stake pool are those required by the block producer. Namely, the following three files.

KES=\${DIRECTORY}/kes.skey
VRF=\${DIRECTORY}/vrf.skey
CERT=\${DIRECTORY}/node.cert

All other keys must remain offline in your cold environment.

Congratulations! Your stake pool is registered and ready to produce blocks.

โ€‹๐ŸŽ‡ 12. Checking Stake pool Rewards

After the epoch is over and assuming you successfully minted blocks, check with this:

cardano-cli shelley query stake-address-info \
--address $(cat stake.addr) \
$NETWORK_IDENTIFIER

โ€‹๐Ÿ”ฎ 13. Setup Prometheus and Grafana Dashboard

Prometheus is a monitoring platform that collects metrics from monitored targets by scraping metrics HTTP endpoints on these targets. Official documentation is available here. Grafana is a dashboard used to visualize the collected data.

๐Ÿฃ 13.1 Installation

Install prometheus and prometheus node exporter.

sudo apt-get install -y prometheus prometheus-alertmanager prometheus-node-exporter

Install grafana.

wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add -
echo "deb https://packages.grafana.com/oss/deb stable main" > grafana.list
sudo mv grafana.list /etc/apt/sources.list.d/grafana.list
sudo apt-get update && sudo apt-get install -y grafana

Change the default port of Grafana from 3000 to 30000

Port 3000 is used by the block-producing node.

cd /etc/grafana
sudo sed -i grafana.ini -e "s/;http_port = 3000/http_port = 30000/g"

Enable services so they start automatically.

sudo systemctl enable grafana-server.service
sudo systemctl enable prometheus.service
sudo systemctl enable prometheus-node-exporter.service

Update prometheus.yml located in /etc/prometheus/prometheus.yml

cat > prometheus.yml << EOF
global:
scrape_interval: 15s # By default, scrape targets every 15 seconds.
โ€‹
# Attach these labels to any time series or alerts when communicating with
# external systems (federation, remote storage, Alertmanager).
external_labels:
monitor: 'codelab-monitor'
โ€‹
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label job=<job_name> to any timeseries scraped from this config.
- job_name: 'prometheus'
โ€‹
static_configs:
- targets: ['localhost:9100']
- targets: ['localhost:12700']
labels:
alias: 'block-producing-node'
type: 'cardano-node'
- targets: ['localhost:12701']
labels:
alias: 'relaynode1'
type: 'cardano-node'
- targets: ['localhost:12702']
labels:
alias: 'relaynode2'
type: 'cardano-node'
EOF
sudo mv prometheus.yml /etc/prometheus/prometheus.yml

Finally, restart the services.

sudo systemctl restart grafana-server.service
sudo systemctl restart prometheus.service
sudo systemctl restart prometheus-node-exporter.service

Verify that the services are running properly:

sudo systemctl status grafana-server.service prometheus.service prometheus-node-exporter.service

Update ${NODE_CONFIG}-config.json config files with new hasEKG and hasPrometheus ports.

cd $NODE_HOME
sed -i ${NODE_CONFIG}-config.json -e "s/ 12798/ 12700/g" -e "s/hasEKG\": 12788/hasEKG\": 12600/g"
sed -i relaynode1/${NODE_CONFIG}-config.json -e "s/ 12798/ 12701/g" -e "s/hasEKG\": 12788/hasEKG\": 12601/g"
sed -i relaynode2/${NODE_CONFIG}-config.json -e "s/ 12798/ 12702/g" -e "s/hasEKG\": 12788/hasEKG\": 12602/g"

Stop and restart your stake pool.

cd $NODE_HOME
./stopStakePool.sh
./startStakePool.sh
tmux a

โ€‹๐Ÿ“ถ 13.2 Setting up Grafana Dashboards

  1. Open http://localhost:30000 in your browser

  2. Login with admin / admin

  3. Change password

  4. Click the configuration gear icon, then Add data Source

  5. Select Prometheus

  6. Set Name to "prometheus" . โœจ Lower case matters.

  7. Set URL to http://localhost:9090

  8. Click Save & Test

  9. Click Create + icon > Import

  10. Add dashboard by importing id: 11074

  11. Click the Load button.

  12. Set Prometheus data source as "prometheus"

  13. Click the Import button.

Grafana dashboard ID 11074 is an excellent overall systems health visualizer.

Grafana system health dashboard

Import a Cardano-Node dashboard

  1. Click Create + icon > Import

  2. Add dashboard by importing via panel json. Copy the json from below.

  3. Click the Import button.

{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"id": 1,
"links": [],
"panels": [
{
"datasource": "prometheus",
"description": "",
"fieldConfig": {
"defaults": {
"custom": {},
"decimals": 2,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "purple",
"value": null
}
]
},
"unit": "d"
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 0,
"y": 0
},
"id": 18,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"mean"
],
"fields": "",
"values": false
}
},
"pluginVersion": "7.0.3",
"targets": [
{
"expr": "(cardano_node_Forge_metrics_remainingKESPeriods_int * 6 / 24 / 6)",
"instant": true,
"interval": "",
"legendFormat": "Days till renew",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Key evolution renew left",
"type": "stat"
},
{
"datasource": "prometheus",
"fieldConfig": {
"defaults": {
"custom": {},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red",
"value": null
},
{
"color": "#EAB839",
"value": 12
},
{
"color": "green",
"value": 24
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 5,
"x": 6,
"y": 0
},
"id": 12,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"mean"
],
"fields": "",
"values": false
}
},
"pluginVersion": "7.0.3",
"targets": [
{
"expr": "cardano_node_Forge_metrics_remainingKESPeriods_int",
"instant": true,
"interval": "",
"legendFormat": "KES Remaining",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "KES remaining",
"type": "stat"
},
{
"datasource": "prometheus",
"description": "",
"fieldConfig": {
"defaults": {
"custom": {
"align": null
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "yellow",
"value": 460
},
{
"color": "red",
"value": 500
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 11,
"y": 0
},
"id": 2,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"mean"
],
"fields": "",
"values": false
}
},
"pluginVersion": "7.0.3",
"targets": [
{
"expr": "cardano_node_Forge_metrics_operationalCertificateExpiryKESPeriod_int",
"format": "time_series",
"instant": true,
"interval": "",
"legendFormat": "KES Expiry",
"refId": "A"
},
{
"expr": "cardano_node_Forge_metrics_currentKESPeriod_int",
"instant": true,
"interval": "",
"legendFormat": "KES current",
"refId": "B"
}
],
"timeFrom": null,
"timeShift": null,
"title": "KES Perioden",
"type": "stat"
},
{
"datasource": "prometheus",
"description": "",
"fieldConfig": {
"defaults": {
"custom": {
"align": null
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 0,
"y": 5
},
"id": 10,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"mean"
],
"fields": "",
"values": false
}
},
"pluginVersion": "7.0.3",
"targets": [
{
"expr": "cardano_node_ChainDB_metrics_slotNum_int",
"instant": true,
"interval": "",
"legendFormat":