Register a stake pool
This is the second of two guides. It assumes you have already installed and synced a relay.
This is the adventurous path. Running a relay confirms your node can follow the chain; registering a stake pool lets it forge blocks — both ordinary Praos ranking blocks and Leios endorser blocks — and makes you a full participant in the Earth-phase work.
For the first few days of the testnet, registering a stake pool uses exactly the same method you use on Cardano today — the keys and certificates below. In the coming days the engineering team will release a node version that requires stake pools to additionally register a new BLS key. BLS keys are an essential part of Leios: they are what pools use to vote on and certify endorser blocks. When that release lands, this guide will add the extra registration step. Until then, the standard flow on this page is all you need.
cardano-cli and cardano-node on your PATHEvery command in this guide uses cardano-cli, and the final step runs
cardano-node directly — so you need both available.
- Installed with Nix? Run everything below from the project's dev
shell, which puts the tools on your
PATH:cd ~/leios/ouroboros-leios # the repo you clonednix develop # or `direnv allow` - Installed the prebuilt binaries? They are already on your
PATH— nothing extra to do.
What you need first
- Test ada from the faucet. It sends a fixed amount automatically (10,000 test ada) — far more than enough to cover the stake-pool deposit (500 ada), the stake-address deposit (2 ada), your pledge, and transaction fees.
- A public IP address and an open port so other nodes can reach your node.
- An accurate clock — a block producer must keep precise time.
Install and enable NTP:
sudo apt install -y chronysudo systemctl enable --now chrony
Keep the environment from the previous guide set in your shell. With
CARDANO_NODE_NETWORK_ID exported, every cardano-cli command targets
magic 164 automatically — no --testnet-magic flag needed:
export CARDANO_NODE_NETWORK_ID=164
export CARDANO_NODE_SOCKET_PATH=~/leios/relay/node.socket
The paths in this guide assume the prebuilt-binary layout from the
previous guide (socket and config under ~/leios/relay). If you ran the
Nix relay instead, point CARDANO_NODE_SOCKET_PATH — and the genesis
path in Step 4 — at your ./tmp-testnet working directory.
Work in a dedicated keys folder and back it up — these keys control your pool:
mkdir -p ~/leios/keys && cd ~/leios/keys
The commands below use the dijkstra era command group
(cardano-cli dijkstra ...), because the testnet is currently in the
Dijkstra era at the chain tip. Confirm with the era field of
cardano-cli query tip; if it ever reads something else, switch the era
word in these commands to match.
Step 1 — Generate payment and stake keys
# Payment key pair (holds funds)
cardano-cli dijkstra address key-gen \
--verification-key-file payment.vkey \
--signing-key-file payment.skey
# Stake key pair (controls delegation)
cardano-cli dijkstra stake-address key-gen \
--verification-key-file stake.vkey \
--signing-key-file stake.skey
Step 2 — Build your payment address and fund it
cardano-cli dijkstra address build \
--payment-verification-key-file payment.vkey \
--stake-verification-key-file stake.vkey \
--out-file payment.addr
cat payment.addr
Copy that address into the faucet to receive test ada. Confirm it arrived:
cardano-cli dijkstra query utxo --address "$(cat payment.addr)"
You should see one or more UTxO entries (a TxHash#TxIx and an amount).
Step 3 — Generate the node's operational keys
# Cold keys (your pool's identity — keep offline / backed up)
cardano-cli dijkstra node key-gen \
--cold-verification-key-file cold.vkey \
--cold-signing-key-file cold.skey \
--operational-certificate-issue-counter-file opcert.counter
# KES keys (hot keys, rotated periodically)
cardano-cli dijkstra node key-gen-KES \
--verification-key-file kes.vkey \
--signing-key-file kes.skey
# VRF keys (used to win block-production slots)
cardano-cli dijkstra node key-gen-VRF \
--verification-key-file vrf.vkey \
--signing-key-file vrf.skey
Step 4 — Issue the operational certificate
Compute the current KES period from the chain tip and the genesis parameter, then issue the certificate that binds your KES key to your cold key:
slotsPerKESPeriod=$(jq -r '.slotsPerKESPeriod' ~/leios/relay/shelley-genesis.json)
slotNo=$(cardano-cli query tip | jq -r '.slot')
kesPeriod=$(( slotNo / slotsPerKESPeriod ))
cardano-cli dijkstra node issue-op-cert \
--kes-verification-key-file kes.vkey \
--cold-signing-key-file cold.skey \
--operational-certificate-issue-counter-file opcert.counter \
--kes-period "$kesPeriod" \
--out-file opcert.cert
Step 5 — Register your stake address and pool
Two things go on-chain together: your stake address (a 2 ada deposit) and your pool (a 500 ada deposit). Build both certificates, then submit them in a single transaction.
build-raw and not build?Normally transaction build balances the transaction (fee and change) for
you. In the current release it does not support certificate
transactions in the Dijkstra era — it fails with Dijkstra cert path not yet implemented. The fix is expected in the next node release, after which
you can use the simpler transaction build. Until then, the steps below
use transaction build-raw, where you set the fee and change yourself.
Stake-address registration certificate:
cardano-cli dijkstra stake-address registration-certificate \
--stake-verification-key-file stake.vkey \
--key-reg-deposit-amt "$(cardano-cli dijkstra query gov-state | jq .currentPParams.stakeAddressDeposit)" \
--out-file stake-reg.cert
Pool registration certificate — replace <YOUR_PUBLIC_IP> with your node's
public IP (the address other nodes will use to reach it):
cardano-cli dijkstra stake-pool registration-certificate \
--cold-verification-key-file cold.vkey \
--vrf-verification-key-file vrf.vkey \
--pool-pledge 1000000000 \
--pool-cost 170000000 \
--pool-margin 0.05 \
--pool-reward-account-verification-key-file stake.vkey \
--pool-owner-stake-verification-key-file stake.vkey \
--pool-relay-ipv4 <YOUR_PUBLIC_IP> \
--pool-relay-port 3010 \
--out-file pool-reg.cert
--pool-pledge 1000000000 is 1000 test ada — a reasonable pledge for a testnet
pool. --pool-cost 170000000 (170 ada) and --pool-margin 0.05 (5%) are
typical values; adjust to taste. --pool-relay-port must match the port
your node listens on (3010 by default in this guide).
Submit both certificates in one transaction. With build-raw you choose
the input, fee, and change yourself. Pull your funded input straight from
query utxo (this assumes a single UTxO at the address — true right after
the faucet payment):
UTXO=$(cardano-cli dijkstra query utxo --address "$(cat payment.addr)")
TXIN=$(echo "$UTXO" | jq -r 'keys[0]')
FUNDS=$(echo "$UTXO" | jq -r '.[keys[0]].value.lovelace')
FEE=200000 # flat 0.2 ada — ample for this small cert tx
DEPOSITS=502000000 # 500 ada pool + 2 ada stake
CHANGE=$(( FUNDS - DEPOSITS - FEE ))
echo "TXIN=$TXIN CHANGE=$CHANGE"
build-raw doesn't auto-balance, so you supply the fee yourself. A
certificate transaction this size needs ~0.18 ada, so a flat 0.2 ada
(200000) always clears it — you overpay a negligible ~0.02 ada.
(Computing it with transaction calculate-min-fee against a draft tends to
come out a few hundred lovelace short here, and the node rejects it with
FeeTooSmallUTxO.) Once the next release fixes transaction build, it
computes the exact fee for you.
Build the transaction, then sign with three keys (payment, stake, cold) and submit:
cardano-cli dijkstra transaction build-raw \
--tx-in "$TXIN" \
--tx-out "$(cat payment.addr)+$CHANGE" \
--fee "$FEE" \
--certificate-file stake-reg.cert \
--certificate-file pool-reg.cert \
--out-file pool-reg-tx.raw
cardano-cli dijkstra transaction sign \
--tx-body-file pool-reg-tx.raw \
--signing-key-file payment.skey \
--signing-key-file stake.skey \
--signing-key-file cold.skey \
--out-file pool-reg-tx.signed
cardano-cli dijkstra transaction submit \
--tx-file pool-reg-tx.signed
Step 6 — Delegate your stake to your pool
Your pledge only counts once your own stake is delegated to your pool.
Build a delegation certificate and submit it in its own transaction. Your
UTxO set changed in Step 5, so the snippet re-queries it for the current
input. This transaction is signed by two keys (payment, stake) and pays no
deposit, so the change is simply funds - fee — using the same flat
FEE=200000:
cardano-cli dijkstra stake-address stake-delegation-certificate \
--stake-verification-key-file stake.vkey \
--cold-verification-key-file cold.vkey \
--out-file delegation.cert
UTXO=$(cardano-cli dijkstra query utxo --address "$(cat payment.addr)")
TXIN=$(echo "$UTXO" | jq -r 'keys[0]')
FUNDS=$(echo "$UTXO" | jq -r '.[keys[0]].value.lovelace')
FEE=200000
CHANGE=$(( FUNDS - FEE ))
cardano-cli dijkstra transaction build-raw \
--tx-in "$TXIN" \
--tx-out "$(cat payment.addr)+$CHANGE" \
--fee "$FEE" \
--certificate-file delegation.cert \
--out-file delegation-tx.raw
cardano-cli dijkstra transaction sign \
--tx-body-file delegation-tx.raw \
--signing-key-file payment.skey \
--signing-key-file stake.skey \
--out-file delegation-tx.signed
cardano-cli dijkstra transaction submit \
--tx-file delegation-tx.signed
Your pledge alone (1000 test ada) is far too little for the pool to be
selected to forge. The
faucet can also
delegate ~1,000,000 test ada to your pool, giving it meaningful active
stake. The faucet's delegate widget needs your bech32 pool id
(pool1…) — get it with:
cardano-cli dijkstra stake-pool id --output-bech32 --cold-verification-key-file cold.vkey
Step 7 — Verify your registration
Find your pool id, then confirm the pool is on-chain and your stake is delegated to it:
# Capture your pool id (from the cold key) and stake address
POOL_ID=$(cardano-cli dijkstra stake-pool id --cold-verification-key-file cold.vkey --output-format hex)
STAKE_ADDR=$(cardano-cli dijkstra stake-address build --stake-verification-key-file stake.vkey)
echo "pool id: $POOL_ID"
echo "stake address: $STAKE_ADDR"
# Is the pool registered on-chain?
cardano-cli dijkstra query pool-state --stake-pool-id "$POOL_ID"
# Did the delegation take effect?
cardano-cli dijkstra query stake-address-info --address "$STAKE_ADDR"
query pool-state should return your pool's parameters (pledge, cost,
margin, VRF). query stake-address-info should show a stakeDelegation
pointing at your pool id. If both look right, your pool is registered.
Step 8 — Restart your node as a block producer
Stop the relay and restart it with the KES key, VRF key, and operational
certificate so it can forge — this time launching cardano-node
directly (the nix run / run-node.sh wrappers run a non-producing
relay only). Run it from your relay's working directory, which holds the
config and database — ~/leios/relay if you used the prebuilt binaries,
or ./tmp-testnet if you used the Nix relay:
cardano-node run \
--config config.json \
--topology topology.json \
--database-path db \
--socket-path node.socket \
--host-addr 0.0.0.0 \
--port 3010 \
--shelley-kes-key ~/leios/keys/kes.skey \
--shelley-vrf-key ~/leios/keys/vrf.skey \
--shelley-operational-certificate ~/leios/keys/opcert.cert
Once your pool is registered and your node is forging, you are a block producer on the testnet. Block production begins after the stake snapshot takes effect — roughly two epochs after registration.
What to send back
The testnet is where the protocol practices in public, and what you see is part of the practice. If your node will not sync, a command here fails, a trace event looks wrong, or the chain behaves in a way you did not expect — that is exactly the signal the team wants.
When you report, include three things: the command or action you took,
what you expected, and what actually happened. Attach your node version
(cardano-node --version) and the relevant log lines.
- Discord: the Musashi Dōjō Discord — advice, guidance, and the place to raise issues, concerns, or bugs.
- Issues: Ouroboros Leios repository
- Design reference: CIP-0164