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 1 public · port 6000
Relay 2 other country
Relay 3 other 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 1 port 6000
Relay 2 2nd country
Relay 3 2nd 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 countries France, United Kingdom, United States — one data centre going dark does not stop the pool.
Hosted at Contabo Rented servers with a static IP; the relays run there.
Private block producer Reachable only from the three relays, its address published nowhere.
Cold keys offline Created and kept on an air-gapped machine; every signature happens there.
Mithril signer Helps certify the snapshots other nodes sync from.
Monitored daily Peers, 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
1 block producer
1 air-gapped computer
24 GB RAM with swap
300 GB disk
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 Build synced node tx.raw
USB
2 Sign air-gapped machine tx.signed
USB
3 Submit synced node on 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 machine never online · 2 encrypted backups cold.skeycold.counterpayment.skeystake.skeyBlock producer read-only for the node kes.skey90 days vrf.skeyalso in backup node.certPublic safe anywhere *.vkeypayment.addrstake.addrpool IDtx.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 30 60 80 93
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.
Arrow keys or a swipe turn the page. The address keeps your place — bookmark it to continue later.