Run a Stake Pool Guided mode

Run a Stake Pool31 steps

Where are you?

The guide starts where you stand. You can always step back.

O-1 Your pool at a glance

Relays face the world, the block producer hides behind them, the cold keys never go online.

Your pool at a glance Cardano network Relay 1public · port 6000 Relay 2other country Relay 3other provider firewall Block producer private · relays only VRF · KES · certificate Air-gapped machine cold keys · never online USB stick only Your pool at a glance Cardano network Relay 1port 6000 Relay 22nd country Relay 32nd provider firewall Block producer private · relays only VRF · KES · certificate USB stick only Air-gapped machine cold keys · signs never online
Your pool at a glance: the Cardano network reaches three public relays; only the relays reach the private block producer behind a firewall; the cold keys live on an air-gapped machine that is connected by USB stick only.

Every code block below names the machine it runs on. A testnet run first costs nothing: use --testnet-magic 2 (Preview) instead of --mainnet.

O-2 Watch out for attackers

A pool holds real ADA and real keys. These six tricks are how pools get robbed.

  • Fake downloads

    Look-alike sites and repositories ship node binaries with a backdoor.

    Download only from github.com/IntersectMBO and check the SHA-256 before you run anything.

  • Fake support

    “Admins” write first in Discord, Telegram or X and ask for keys, a seed phrase or remote access.

    Nobody genuine ever asks. Never share a .skey file, a seed phrase or a screen.

  • Keys on a server

    A cold key on an online machine means a stolen pool and stolen rewards.

    Cold, payment and stake keys exist only on the air-gapped machine and its backups.

  • Password guessing

    Bots try SSH passwords on every server, day and night.

    Keys only, no root login, another port, fail2ban.

  • Copy-paste traps

    Clipboard malware swaps addresses; web pages hide extra commands in what you copy.

    Paste into an editor first, read every command, check every address after pasting.

  • Exposed block producer

    A block producer reachable from the internet can be flooded or attacked.

    Firewall: port 6000 only from your relays. Its IP is never published.

Important Never paste a key, a seed phrase or a .skey file into any chat, form or AI — ours included. A genuine helper never needs one.

O-3 How BRIAN runs it

The setup behind BRIAN since August 2024 — our recommendations, from practice.

  • Three relays, three countriesFrance, United Kingdom, United States — one data centre going dark does not stop the pool.
  • Hosted at ContaboRented servers with a static IP; the relays run there.
  • Private block producerReachable only from the three relays, its address published nowhere.
  • Cold keys offlineCreated and kept on an air-gapped machine; every signature happens there.
  • Mithril signerHelps certify the snapshots other nodes sync from.
  • Monitored dailyPeers, sync, KES and blocks checked every day; updates promptly, relays first.

See it live on the Pool Info board.

O-4 Before you start

Checked versions, and two settings every online command uses.

Every command was run against cardano-node 11.1.3 and cardano-cli 11.2.3 on 3 October 2026. All start with cardano-cli latest (the Conway era).

Block producer or relay
echo 'export CARDANO_NODE_SOCKET_PATH=/run/cardano/node.socket' >> ~/.bashrc
echo 'export CARDANO_NODE_NETWORK_ID=mainnet' >> ~/.bashrc
source ~/.bashrc

Caution Not financial advice. Read each command before you run it.

I-1 What you need

Three kinds of machine and at least 505 ADA.

  • 2+relays
  • 1block producer
  • 1air-gapped computer
  • 24 GBRAM with swap
  • 300 GBdisk
  • 505 ₳deposits + fees

Each node: Ubuntu LTS (64-bit), 2+ cores, a static IP, 10 Mbps. Plus your pledge in ADA. Deposits can change by governance vote — Part III reads them from the chain.

I-2 Harden every server

Own user, key-only SSH, updates, fail2ban.

All nodes
sudo useradd -m -s /bin/bash cardano-op
sudo passwd cardano-op
sudo usermod -aG sudo cardano-op
sudo passwd -l root
Your own computer
ssh-keygen -t ed25519 -C "stake-pool-ops"
ssh-copy-id -i ~/.ssh/id_ed25519.pub cardano-op@<server-ip>

In /etc/ssh/sshd_config:

File
Port 2222
PubkeyAuthentication yes
PasswordAuthentication no
PermitRootLogin no
PermitEmptyPasswords no
X11Forwarding no
KbdInteractiveAuthentication no
MaxAuthTries 3
LoginGraceTime 30
All nodes
sudo sshd -t && sudo systemctl reload ssh
sudo apt-get update -y && sudo apt-get upgrade -y && sudo apt-get autoremove -y
sudo apt-get install -y unattended-upgrades fail2ban jq
sudo dpkg-reconfigure -plow unattended-upgrades
sudo systemctl enable --now fail2ban

Important Keep your session open and log in from a second terminal on port 2222 before you close it.

Check ssh -p 2222 cardano-op@<server-ip> works with your key; a password login is refused.

I-3 Firewall

Relays open to the world, the block producer only to your relays.

Relay
  • 6000 · Cardanoanyone
  • 2222 · SSHyour keys only
  • everything elsedropped
Block producer
  • 6000 · Cardanoyour relays only
  • 2222 · SSHyour IP only
  • everything elsedropped
Which ports open to whom.

Relay — /etc/nftables.conf:

File
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;

        ct state established,related accept
        iifname "lo" accept
        ip protocol icmp accept
        ip6 nexthdr icmpv6 accept

        tcp dport 2222 accept
        tcp dport 6000 accept
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
    }

    chain output {
        type filter hook output priority 0; policy accept;
    }
}

Block producer — /etc/nftables.conf:

File
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;

        ct state established,related accept
        iifname "lo" accept
        ip protocol icmp accept
        ip6 nexthdr icmpv6 accept

        ip saddr <your-management-ip> tcp dport 2222 accept
        ip saddr { <relay-1-ip>, <relay-2-ip>, <relay-3-ip> } tcp dport 6000 accept
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
    }

    chain output {
        type filter hook output priority 0; policy accept;
    }
}
All nodes
sudo apt-get install -y nftables
sudo systemctl enable nftables
sudo nft -f /etc/nftables.conf
sudo nft list ruleset

Check From another server, nc -zv <block-producer-ip> 6000 times out; from a relay it connects.

I-4 Time sync

Blocks come in one-second slots; a drifting clock misses them.

All nodes
sudo apt-get install -y chrony
sudo systemctl enable --now chrony
chronyc tracking

Check System time is off by a few milliseconds at most.

I-5 Install cardano-node — and check it

Official release, verified by its SHA-256 before it runs.

All nodes
VERSION=11.1.3
cd ~
wget https://github.com/IntersectMBO/cardano-node/releases/download/${VERSION}/cardano-node-${VERSION}-linux-amd64.tar.gz
wget https://github.com/IntersectMBO/cardano-node/releases/download/${VERSION}/cardano-node-${VERSION}-sha256sums.txt
sha256sum --ignore-missing -c cardano-node-${VERSION}-sha256sums.txt
mkdir -p ~/.local
tar -xzf cardano-node-${VERSION}-linux-amd64.tar.gz -C ~/.local/
sudo install -m 755 ~/.local/bin/cardano-node ~/.local/bin/cardano-cli /usr/local/bin/
cardano-node --version
cardano-cli --version

Check sha256sum prints cardano-node-11.1.3-linux-amd64.tar.gz: OK. Anything else: stop and delete the file.

Newer release? Take it from IntersectMBO/cardano-node only. The archive also holds the network configuration files.

II-1 Folders and configuration

A system user for the node, config in /etc/cardano, data in /var/lib/cardano.

All nodes
sudo useradd -r -m -d /var/lib/cardano -s /sbin/nologin cardano
sudo mkdir -p /etc/cardano /var/lib/cardano/db
sudo cp ~/.local/share/mainnet/*.json /etc/cardano/
sudo chown -R cardano:cardano /etc/cardano /var/lib/cardano
sudo usermod -aG cardano $USER

Log out and in again. The block producer shares no peers:

Block producer
sudo jq '.PeerSharing = false' /etc/cardano/config.json > /tmp/config.json
sudo install -o cardano -g cardano -m 644 /tmp/config.json /etc/cardano/config.json

II-2 Download the chain with Mithril

A certified snapshot: hours instead of days.

All nodes
curl --proto '=https' --tlsv1.2 -sSfL \
  https://raw.githubusercontent.com/IntersectMBO/mithril/refs/heads/main/mithril-install.sh \
  | sh -s -- -c mithril-client -d latest -p $HOME/.local/bin

export AGGREGATOR_ENDPOINT=https://aggregator.release-mainnet.api.mithril.network/aggregator
export GENESIS_VERIFICATION_KEY=$(wget -q -O - \
  https://raw.githubusercontent.com/IntersectMBO/mithril/main/mithril-infra/configuration/release-mainnet/genesis.vkey)
export ANCILLARY_VERIFICATION_KEY=$(wget -q -O - \
  https://raw.githubusercontent.com/IntersectMBO/mithril/main/mithril-infra/configuration/release-mainnet/ancillary.vkey)

mkdir -p ~/mithril && cd ~/mithril
~/.local/bin/mithril-client cardano-db download latest --include-ancillary
sudo rm -rf /var/lib/cardano/db
sudo mv ~/mithril/db /var/lib/cardano/db
sudo chown -R cardano:cardano /var/lib/cardano/db

Note The client checks the snapshot's certificate chain against the genesis key — a tampered snapshot fails. GitHub rate limit? Take mithril-client from the Mithril releases.

II-3 Topology

Relays talk to the network and your block producer; the block producer only to your relays.

Relay — /etc/cardano/topology.json:

File
{
  "bootstrapPeers": [
    { "address": "backbone.cardano.iog.io", "port": 3001 },
    { "address": "backbone.mainnet.cardanofoundation.org", "port": 3001 }
  ],
  "localRoots": [
    {
      "accessPoints": [
        { "address": "<block-producer-ip>", "port": 6000 }
      ],
      "advertise": false,
      "trustable": true,
      "valency": 1
    }
  ],
  "peerSnapshotFile": "peer-snapshot.json",
  "publicRoots": [
    { "accessPoints": [], "advertise": false }
  ],
  "useLedgerAfterSlot": 194140785
}

Block producer — /etc/cardano/topology.json:

File
{
  "bootstrapPeers": [],
  "localRoots": [
    {
      "accessPoints": [
        { "address": "<relay-1-ip>", "port": 6000 },
        { "address": "<relay-2-ip>", "port": 6000 },
        { "address": "<relay-3-ip>", "port": 6000 }
      ],
      "advertise": false,
      "trustable": true,
      "valency": 3
    }
  ],
  "publicRoots": [],
  "useLedgerAfterSlot": -1
}

Take useLedgerAfterSlot for the relays from your release's topology file. Reload without restart:

Block producer or relay
sudo systemctl reload cardano-node

II-4 The air-gapped machine

A computer that never connects — for the cold keys and every signature.

A spare laptop with fresh Ubuntu. After installing: Wi-Fi and Bluetooth off for good, no cable. Bring the checked node archive on a USB stick:

Air-gapped machine
cd /media/$USER/<usb-stick>
sha256sum --ignore-missing -c cardano-node-11.1.3-sha256sums.txt
mkdir -p ~/cardano && tar -xzf cardano-node-11.1.3-linux-amd64.tar.gz -C ~/cardano
sudo install -m 755 ~/cardano/bin/cardano-cli /usr/local/bin/
cardano-cli --version
mkdir -p ~/cold-keys && chmod 700 ~/cold-keys

Important Two encrypted backups of ~/cold-keys, in two places, restore-tested. Whoever holds these files owns the pool.

II-5 Run the node as a service

systemd starts it at boot and restarts it when it stops.

/etc/systemd/system/cardano-node.service on a relay:

File
[Unit]
Description=Cardano Node
Wants=network-online.target
After=network-online.target

[Service]
User=cardano
Group=cardano
Type=simple
WorkingDirectory=/var/lib/cardano
ExecStart=/usr/local/bin/cardano-node run \
  --config        /etc/cardano/config.json \
  --topology      /etc/cardano/topology.json \
  --database-path /var/lib/cardano/db \
  --socket-path   /run/cardano/node.socket \
  --host-addr     0.0.0.0 \
  --port          6000
ExecReload=pkill -HUP cardano-node
KillSignal=SIGINT
RestartKillSignal=SIGINT
TimeoutStopSec=300
LimitNOFILE=131072
Restart=always
RestartSec=5
SyslogIdentifier=cardano-node
RuntimeDirectory=cardano
RuntimeDirectoryMode=0750

[Install]
WantedBy=multi-user.target

On the block producer, ExecStart also names the pool files (from Part III, step 3):

File
ExecStart=/usr/local/bin/cardano-node run \
  --config        /etc/cardano/config.json \
  --topology      /etc/cardano/topology.json \
  --database-path /var/lib/cardano/db \
  --socket-path   /run/cardano/node.socket \
  --host-addr     0.0.0.0 \
  --port          6000 \
  --shelley-kes-key                 /var/lib/cardano/keys/kes.skey \
  --shelley-vrf-key                 /var/lib/cardano/keys/vrf.skey \
  --shelley-operational-certificate /var/lib/cardano/keys/node.cert
All nodes
sudo systemctl daemon-reload
sudo systemctl enable --now cardano-node
journalctl -fu cardano-node

Check cardano-cli latest query tip shows "syncProgress": "100.00" before you go on.

Is your node fully synced?

Registration needs a node at 100 percent.

III-1 Payment and stake keys

The wallet that pays the deposits and receives the rewards.

  1. 1Buildsynced nodetx.raw
  2. 2Signair-gapped machinetx.signed
  3. 3Submitsynced nodeon chain
Build online, sign offline, submit online.
Air-gapped machine
cd ~/cold-keys
cardano-cli latest address key-gen \
  --verification-key-file payment.vkey \
  --signing-key-file payment.skey
cardano-cli latest stake-address key-gen \
  --verification-key-file stake.vkey \
  --signing-key-file stake.skey
cardano-cli latest address build \
  --payment-verification-key-file payment.vkey \
  --stake-verification-key-file stake.vkey \
  --mainnet \
  --out-file payment.addr
cardano-cli latest stake-address build \
  --stake-verification-key-file stake.vkey \
  --mainnet \
  --out-file stake.addr

Copy payment.addr, stake.addr and the .vkey files to a node. Send a small test amount, then at least 505 ADA plus your pledge:

Block producer or relay
cardano-cli latest query utxo --address $(cat payment.addr) --output-json

Check The UTxO list shows your amount. Compare the address character by character with the one on the air-gapped machine.

III-2 Register the stake address

2 ADA deposit — build, sign, submit.

Block producer or relay
cardano-cli latest query protocol-parameters --out-file protocol.json
cardano-cli latest stake-address registration-certificate \
  --stake-verification-key-file stake.vkey \
  --key-reg-deposit-amt $(jq '.stakeAddressDeposit' protocol.json) \
  --out-file stake.cert
cardano-cli latest transaction build \
  --tx-in $(cardano-cli latest query utxo --address $(cat payment.addr) --output-json | jq -r 'keys[0]') \
  --change-address $(cat payment.addr) \
  --certificate-file stake.cert \
  --witness-override 2 \
  --out-file tx.raw
Air-gapped machine
cardano-cli latest transaction sign \
  --tx-body-file tx.raw \
  --signing-key-file payment.skey \
  --signing-key-file stake.skey \
  --mainnet \
  --out-file tx.signed
Block producer or relay
cardano-cli latest transaction submit --tx-file tx.signed

Note keys[0] spends the first UTxO. Too small? Name another one in --tx-in.

Check Transaction successfully submitted; a few minutes later query stake-address-info --address $(cat stake.addr) lists the address.

III-3 Pool keys and the operational certificate

Cold keys stay offline; the block producer gets three files.

Air-gapped machinenever online · 2 encrypted backups
  • cold.skey
  • cold.counter
  • payment.skey
  • stake.skey
Block producerread-only for the node
  • kes.skey90 days
  • vrf.skeyalso in backup
  • node.cert
Publicsafe anywhere
  • *.vkey
  • payment.addr
  • stake.addr
  • pool ID
  • tx.raw · tx.signed
Where each key lives.
Air-gapped machine
cd ~/cold-keys
cardano-cli latest node key-gen \
  --cold-verification-key-file cold.vkey \
  --cold-signing-key-file cold.skey \
  --operational-certificate-issue-counter-file cold.counter
cardano-cli latest node key-gen-VRF \
  --verification-key-file vrf.vkey \
  --signing-key-file vrf.skey
cardano-cli latest node key-gen-KES \
  --verification-key-file kes.vkey \
  --signing-key-file kes.skey

The current KES period:

Block producer or relay
slotsPerKESPeriod=$(jq -r '.slotsPerKESPeriod' /etc/cardano/shelley-genesis.json)
slotNo=$(cardano-cli latest query tip | jq -r '.slot')
echo $(( slotNo / slotsPerKESPeriod ))
Air-gapped machine
cardano-cli latest node issue-op-cert \
  --kes-verification-key-file kes.vkey \
  --cold-signing-key-file cold.skey \
  --operational-certificate-issue-counter-file cold.counter \
  --kes-period <kes-period> \
  --out-file node.cert

Copy kes.skey, vrf.skey, node.cert to the block producer:

Block producer
sudo mkdir -p /var/lib/cardano/keys
sudo install -o cardano -g cardano -m 400 kes.skey vrf.skey node.cert /var/lib/cardano/keys/
shred -u kes.skey vrf.skey
sudo systemctl daemon-reload && sudo systemctl restart cardano-node

III-4 Register the pool

Metadata, two certificates, the 500 ADA deposit.

Publish poolMetaData.json at a URL of at most 64 characters (homepage or GitHub Pages). Ticker: 3 to 5 characters.

File
{
  "name": "Your Pool Name",
  "description": "What your pool stands for",
  "ticker": "TICK",
  "homepage": "https://yourpool.example"
}
Block producer or relay
cardano-cli latest stake-pool metadata-hash \
  --pool-metadata-file <(curl -s -L https://yourpool.example/poolMetaData.json) \
  --out-file poolMetaDataHash.txt
jq '.minPoolCost, .stakePoolDeposit' protocol.json

With poolMetaDataHash.txt on the air-gapped machine — amounts in lovelace (1 ₳ = 1,000,000): 1,000 ₳ pledge, 170 ₳ fixed cost, 1 % margin, three relays:

Air-gapped machine
cardano-cli latest 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.01 \
  --pool-reward-account-verification-key-file stake.vkey \
  --pool-owner-stake-verification-key-file stake.vkey \
  --single-host-pool-relay relay1.yourpool.example --pool-relay-port 6000 \
  --single-host-pool-relay relay2.yourpool.example --pool-relay-port 6000 \
  --single-host-pool-relay relay3.yourpool.example --pool-relay-port 6000 \
  --metadata-url https://yourpool.example/poolMetaData.json \
  --metadata-hash $(cat poolMetaDataHash.txt) \
  --mainnet \
  --out-file pool.cert
cardano-cli latest stake-address stake-delegation-certificate \
  --stake-verification-key-file stake.vkey \
  --cold-verification-key-file cold.vkey \
  --out-file deleg.cert
Block producer or relay
cardano-cli latest transaction build \
  --tx-in $(cardano-cli latest query utxo --address $(cat payment.addr) --output-json | jq -r 'keys[0]') \
  --change-address $(cat payment.addr) \
  --certificate-file pool.cert \
  --certificate-file deleg.cert \
  --witness-override 3 \
  --out-file tx.raw
Air-gapped machine
cardano-cli latest transaction sign \
  --tx-body-file tx.raw \
  --signing-key-file payment.skey \
  --signing-key-file stake.skey \
  --signing-key-file cold.skey \
  --mainnet \
  --out-file tx.signed
Block producer or relay
cardano-cli latest transaction submit --tx-file tx.signed

Caution Relay names in the certificate, never the block producer. Keep the pledge in the owner wallet — below it, the pool earns nothing that epoch.

III-5 Check the registration

Your pool ID and its stake.

Air-gapped machine
cardano-cli latest stake-pool id --cold-verification-key-file cold.vkey --output-format bech32 > stakepoolid.txt
cat stakepoolid.txt
Block producer or relay
cardano-cli latest query stake-snapshot --stake-pool-id $(cat stakepoolid.txt)

Check The ID starting with pool1 shows up on cexplorer.io and adastat.net. Stake counts after two epoch boundaries.

Your pool is registered. What do you need today?

Part IV is a toolbox; pick what is due.

IV-1 Renew the KES key

Every 90 days at the latest.

  • Day 0–80 blocks as usual
  • Day 80–90 renew now
  • After 93 no more blocks

1 KES period = 36 hours · 62 periods ≈ 93 days

KES key lifetime: renew between day 80 and 90; after day 93 no blocks.
Block producer
sudo -u cardano cardano-cli latest query kes-period-info \
  --mainnet \
  --socket-path /run/cardano/node.socket \
  --op-cert-file /var/lib/cardano/keys/node.cert

Current KES period as in Part III, step 3; then on the air-gapped machine:

Air-gapped machine
cd ~/cold-keys
cardano-cli latest node key-gen-KES \
  --verification-key-file kes.vkey \
  --signing-key-file kes.skey
cardano-cli latest node issue-op-cert \
  --kes-verification-key-file kes.vkey \
  --cold-signing-key-file cold.skey \
  --operational-certificate-issue-counter-file cold.counter \
  --kes-period <kes-period> \
  --out-file node.cert
Block producer
sudo install -o cardano -g cardano -m 400 kes.skey node.cert /var/lib/cardano/keys/
shred -u kes.skey
sudo systemctl restart cardano-node

Caution The new certificate's counter may be at most one above the counter in the pool's last block. No block since the last renewal? Reuse that certificate or reset with cardano-cli latest node new-counter.

Check kes-period-info reports the new expiry date and matching counters.

IV-2 Monitor and the leader schedule

Daily health, and the slots your pool is due for.

gLiveView in the terminal, or Prometheus and Grafana on the node's metrics (port 12798, never public). The next epoch's schedule is known 1.5 days ahead:

Block producer
sudo -u cardano cardano-cli latest query leadership-schedule \
  --mainnet \
  --socket-path /run/cardano/node.socket \
  --genesis /etc/cardano/shelley-genesis.json \
  --stake-pool-id $(cat stakepoolid.txt) \
  --vrf-signing-key-file /var/lib/cardano/keys/vrf.skey \
  --next

Note BRIAN also runs a Mithril signer — see mithril.network.

IV-3 Delegate your vote

Needed before rewards can be withdrawn.

Air-gapped machine
cardano-cli latest stake-address vote-delegation-certificate \
  --stake-verification-key-file stake.vkey \
  --always-abstain \
  --out-file vote-deleg.cert

Build with --certificate-file vote-deleg.cert --witness-override 2, sign with payment.skey and stake.skey, submit. A DRep instead: --drep-key-hash.

IV-4 Withdraw the rewards

The whole reward balance into your wallet.

Block producer or relay
rewards=$(cardano-cli latest query stake-address-info --address $(cat stake.addr) | jq -r '.[0].rewardAccountBalance')
cardano-cli latest transaction build \
  --tx-in $(cardano-cli latest query utxo --address $(cat payment.addr) --output-json | jq -r 'keys[0]') \
  --withdrawal "$(cat stake.addr)+${rewards}" \
  --change-address $(cat payment.addr) \
  --witness-override 2 \
  --out-file tx.raw

Sign with payment.skey and stake.skey, submit.

IV-5 Vote as a pool

Hard forks, some parameters, no-confidence, committee changes.

Block producer or relay
cardano-cli latest query proposals --all-proposals \
  | jq '.[] | {id: .actionId, type: .proposalProcedure.govAction.tag, url: .proposalProcedure.anchor.url}'
Air-gapped machine
cardano-cli latest governance vote create \
  --yes \
  --governance-action-tx-id <tx-id> \
  --governance-action-index 0 \
  --cold-verification-key-file cold.vkey \
  --out-file pool.vote
Block producer or relay
cardano-cli latest transaction build \
  --tx-in $(cardano-cli latest query utxo --address $(cat payment.addr) --output-json | jq -r 'keys[0]') \
  --change-address $(cat payment.addr) \
  --vote-file pool.vote \
  --witness-override 2 \
  --out-file tx.raw

Sign with cold.skey and payment.skey, submit. --no or --abstain as you decide.

IV-6 Change pool parameters

Pledge, cost, margin, relays, metadata — no new deposit.

New pool.cert as in Part III, step 4; submit it alone, signed with payment.skey, stake.skey, cold.skey. It takes effect at an epoch boundary.

IV-7 Upgrade the node

Relays first, block producer last — right after a block.

All nodes
VERSION=<new-version>
cd ~
wget https://github.com/IntersectMBO/cardano-node/releases/download/${VERSION}/cardano-node-${VERSION}-linux-amd64.tar.gz
wget https://github.com/IntersectMBO/cardano-node/releases/download/${VERSION}/cardano-node-${VERSION}-sha256sums.txt
sha256sum --ignore-missing -c cardano-node-${VERSION}-sha256sums.txt
tar -xzf cardano-node-${VERSION}-linux-amd64.tar.gz -C ~/.local/
sudo systemctl stop cardano-node
sudo install -m 755 ~/.local/bin/cardano-node ~/.local/bin/cardano-cli /usr/local/bin/
sudo systemctl start cardano-node
cardano-node --version

Read the release notes first; some need new configuration files. Bring the new cardano-cli to the air-gapped machine too.

IV-8 Retire the pool

The 500 ADA deposit returns at the epoch you name.

Air-gapped machine
cardano-cli latest stake-pool deregistration-certificate \
  --cold-verification-key-file cold.vkey \
  --epoch <retirement-epoch> \
  --out-file pool.dereg

Submit it signed with payment.skey and cold.skey. Announce it and give delegators two epochs or more.

V-1 Send ADA

A plain payment, here 10 ₳.

Block producer or relay
cardano-cli latest transaction build \
  --tx-in $(cardano-cli latest query utxo --address $(cat payment.addr) --output-json | jq -r 'keys[0]') \
  --tx-out "<receiver-address>+10000000" \
  --change-address $(cat payment.addr) \
  --out-file tx.raw

Sign with payment.skey, submit. Check the receiver address on the air-gapped machine before signing: cardano-cli debug transaction view --tx-file tx.raw.

V-2 Move files

scp to the nodes, a USB stick to the air-gapped machine.

Your own computer
scp -P 2222 cardano-op@<node-ip>:~/tx.raw .
scp -P 2222 tx.signed cardano-op@<node-ip>:~/

Caution Never copy a signing key over the network. Wipe the stick after moving key files.

V-3 Add swap

Protects a node with little RAM from memory peaks.

All nodes
sudo fallocate -l 8G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -h

V-4 Checklist

The habits that keep a pool safe.

  • Cold keys only offline, two tested encrypted backups
  • Block producer reachable only from your relays
  • SSH with keys only, fail2ban on
  • Every download checked against its SHA-256
  • KES renewed before day 90 — reminder set
  • Pledge never below what you declared
  • Releases installed, relays first
  • Votes cast, reward account delegated
  • Every change tried on the testnet first

That is the whole path.

Your pool runs on habits now: KES before day 90, upgrades relays first, votes when they come.

Questions about this step?

An AI helper for this guide is on its way and will answer right here. Until then, ask BRIAN on X @Brian67587820.

Never send anyone a key, a seed phrase or a .skey file — not BRIAN, not an AI.

Sources and license

Text, pictures and order are BRIAN's. Commands for system setup, the firewall, Mithril and the service file are adapted from the Cardano Developer Portal; every cardano-cli command was written for and run against cardano-cli 11.2.3. The node's own documentation always takes precedence.

Developer Portal license (MIT) — Copyright (c) 2021 Cardano Foundation

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.