# Cardano Stake Pool Guide

Step-by-step guide to setting up a Cardano Stake Pool on Linux — from server setup to block production.

A practical, copy-paste-ready guide for setting up and operating a Cardano stake pool on **Ubuntu/Debian Linux**. Maintained by the [StakePool247](https://t.me/StakePool247help) community.

{% hint style="success" %}
**Current version: cardano-node 10.6.2** (Mainnet)

Upgrading from an older release? See the [Upgrade Guide](/cardano-node-upgrades/upgrade-to-10.6.2).
{% endhint %}

## Quick setup (automated)

Get a relay node installed in minutes with our interactive setup script:

```bash
sudo apt update && sudo apt install -y curl
curl -sL -o setup-relay.sh \
  https://raw.githubusercontent.com/stakepool247/gitbook-stakepool-installation-guide/main/scripts/setup-relay.sh
sudo bash setup-relay.sh
```

The script lets you choose your network, node version, and DB backend via a TUI menu — then handles everything: user creation, packages, binary install, config files, Mithril client, and systemd service.

Prefer to understand each step? Follow the guide below.

***

## What you'll need

### Skills

* Basic Linux command-line experience
* Willingness to keep the node updated when new releases drop
* Security-first mindset (keys, firewall, SSH hardening, backups)

### Hardware — Mainnet

| Component   | Minimum                   | Recommended              |
| ----------- | ------------------------- | ------------------------ |
| **Servers** | 2 (1 BP + 1 relay)        | 3 (1 BP + 2 relays)      |
| **OS**      | Ubuntu 22.04 / Debian 12+ | Ubuntu 24.04 LTS         |
| **CPU**     | 2 vCPU                    | 4 vCPU                   |
| **RAM**     | 8 GB (LMDB backend)       | 24 GB (InMemory backend) |
| **Storage** | 300 GB SSD                | 350+ GB SSD              |
| **Network** | 10 Mbps, low packet loss  | 50+ Mbps                 |

You also need:

* **Offline machine** — for generating and storing cold keys (never connected to internet)
* **Hardware wallet** — Trezor or Ledger (strongly recommended for pledge security)

### Hardware — Testnet (pre-prod)

Same architecture, lighter requirements: 2 vCPU, 16 GB RAM, 150+ GB SSD.

***

## What this guide covers

1. **Server setup** — user creation, swap, packages, firewall
2. **Node installation** — binary install, config files, architecture detection
3. **Relay configuration** — topology, blockchain sync (Mithril), systemd service
4. **Block producer setup** — SPOS scripts, wallet keys, pool keys, registration
5. **Operations** — KES key rotation, upgrades, topology management

***

## Community & support

|                            |                                                                              |
| -------------------------- | ---------------------------------------------------------------------------- |
| **StakePool247 Support**   | [t.me/StakePool247help](https://t.me/StakePool247help)                       |
| **Cardano SPO Workgroup**  | [t.me/CardanoStakePoolWorkgroup](https://t.me/CardanoStakePoolWorkgroup)     |
| **Community Tech Support** | [t.me/CardanoCommunityTechSupport](https://t.me/CardanoCommunityTechSupport) |

{% hint style="info" %}
**Open source:** This guide lives on [GitHub](https://github.com/stakepool247/gitbook-stakepool-installation-guide). Found an error or have an improvement? PRs and issues welcome.
{% endhint %}


# Creating the cardano user

Create a dedicated cardano user with sudo privileges.

## Add the user

Create a system user named `cardano`:

```bash
sudo adduser cardano
```

You will be prompted for a password and optional user details.

![](/files/XGF4Mc0rJdNGbKhFSVKy)

## Grant sudo access

Add the user to the sudo group:

```bash
sudo usermod -aG sudo cardano
```

If the user was created without a password (e.g., via the setup script), enable passwordless sudo:

```bash
echo 'cardano ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/cardano
sudo chmod 440 /etc/sudoers.d/cardano
```

## Switch to the cardano user

```bash
sudo su - cardano
```

Verify you are logged in correctly:

```bash
whoami
```

Expected output: `cardano`


# Configuring swap space

Add swap space to prevent out-of-memory crashes during node operation.

Swap provides overflow memory on disk when physical RAM is exhausted. It prevents out-of-memory crashes but is significantly slower than real RAM.

## Check existing swap

```bash
swapon -s
```

{% hint style="warning" %}
If this shows an active swapfile, swap is already configured — skip to the next section.
{% endhint %}

![Example of enabled swap](/files/yOSMGek4DDsJ4zpBZwPc)

## Recommended swap size

| Server RAM | Swap size |
| ---------- | --------- |
| 4 -- 16 GB | 8 GB      |
| 16+ GB     | 16 GB     |

{% hint style="info" %}
Swap is not a replacement for physical RAM. If your server constantly uses swap, upgrade memory instead.
{% endhint %}

## Create and enable swap

1. Create an 8 GB swap file:

```bash
sudo fallocate -l 8G /swapfile
```

2. Restrict permissions:

```bash
sudo chmod 600 /swapfile
```

3. Initialize and enable the swap area:

```bash
sudo mkswap /swapfile
sudo swapon /swapfile
```

4. Make swap persistent across reboots:

```bash
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
```

5. Verify swap is active:

```bash
sudo swapon --show
free -h
```

![](/files/97DdPnFGTQqqvBdAbppw)

6. Tune swap behavior:

```bash
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
echo 'vm.vfs_cache_pressure=50' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
```


# Prerequisites

Prepare your server for Cardano Node 10.6.2 installation.

Prepare a clean Ubuntu/Debian server for cardano-node **10.6.2**.

{% hint style="info" %}
For most operators, installing official release binaries is the fastest and most reproducible approach. Source builds are only needed for custom patches.
{% endhint %}

## 1) System update and required packages

```bash
sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install -y \
  curl wget jq git tmux htop nload unzip xz-utils \
  build-essential pkg-config libffi-dev libgmp-dev libssl-dev \
  libsystemd-dev zlib1g-dev libncurses-dev libtool autoconf automake \
  libsodium-dev
```

## 2) Directory layout and environment variables

Run as your `cardano` user:

```bash
mkdir -p $HOME/.local/bin

cd $HOME
mkdir -p cnode
cd cnode
mkdir -p config db sockets keys logs scripts

grep -q 'export PATH="$HOME/.local/bin:$PATH"' $HOME/.bashrc || \
  echo 'export PATH="$HOME/.local/bin:$PATH"' >> $HOME/.bashrc

grep -q 'CARDANO_NODE_SOCKET_PATH' $HOME/.bashrc || \
  echo 'export CARDANO_NODE_SOCKET_PATH="$HOME/cnode/sockets/node.socket"' >> $HOME/.bashrc

source $HOME/.bashrc
```

## 3) Verify architecture

```bash
uname -m
```

| Output    | Artifact to download |
| --------- | -------------------- |
| `x86_64`  | `linux-amd64`        |
| `aarch64` | `linux-arm64`        |

{% hint style="info" %}
If architecture and binary mismatch, the node will not start (`Exec format error`). Always verify before downloading.
{% endhint %}

## 4) Source-build prerequisites (optional)

Only needed if building from source instead of release binaries:

| Dependency | Version      |
| ---------- | ------------ |
| GHC        | 9.6          |
| Cabal      | 3.8+ or 3.12 |
| libblst    | 0.3.14       |

## 5) Firewall

Check whether UFW is active:

```bash
sudo ufw status verbose
```

For a **production relay**, enable UFW with the required ports:

```bash
sudo ufw allow 22/tcp
sudo ufw allow 3001/tcp
sudo ufw enable
sudo ufw status verbose
```

{% hint style="warning" %}
For a **block producer**, do NOT expose the BP port publicly. Keep your BP reachable only from your relays (private network, WireGuard, or strict IP allowlist).
{% endhint %}

## 6) Sanity checks

```bash
df -h
free -h
nproc
```

Verify at least 300 GB of free storage (350+ GB recommended for long-term growth).

{% hint style="info" %}
Swap can prevent OOM crashes, but it is much slower than real RAM. If your server consistently hits swap, upgrade memory rather than relying on swap as a permanent fix.
{% endhint %}


# Installing cardano-node 10.6.2

Install cardano-node and cardano-cli 10.6.2 from official release binaries.

This section installs **cardano-node 10.6.2** from official GitHub release artifacts.

{% hint style="info" %}
Path layout: `/home/cardano/cnode/{config,db,sockets,keys,logs,scripts}` — kept compatible with existing SPO setups.
{% endhint %}

## 1) Download release artifacts

For **x86\_64 / amd64** Linux:

```bash
cd /tmp
curl -L -o cardano-node-10.6.2-linux-amd64.tar.gz \
  https://github.com/IntersectMBO/cardano-node/releases/download/10.6.2/cardano-node-10.6.2-linux-amd64.tar.gz
curl -L -o cardano-node-10.6.2-sha256sums.txt \
  https://github.com/IntersectMBO/cardano-node/releases/download/10.6.2/cardano-node-10.6.2-sha256sums.txt
```

For **arm64** Linux:

```bash
cd /tmp
curl -L -o cardano-node-10.6.2-linux-arm64.tar.gz \
  https://github.com/IntersectMBO/cardano-node/releases/download/10.6.2/cardano-node-10.6.2-linux-arm64.tar.gz
curl -L -o cardano-node-10.6.2-sha256sums.txt \
  https://github.com/IntersectMBO/cardano-node/releases/download/10.6.2/cardano-node-10.6.2-sha256sums.txt
```

## 2) Verify checksum

```bash
sha256sum cardano-node-10.6.2-linux-*.tar.gz
cat cardano-node-10.6.2-sha256sums.txt
```

Compare your tarball's checksum with the matching line in the official checksums file. They must match exactly.

## 3) Install binaries

Extract the tarball (use the correct filename for your architecture):

```bash
tar -xzf cardano-node-10.6.2-linux-*.tar.gz
install -m 755 ./bin/cardano-node ./bin/cardano-cli $HOME/.local/bin/
```

Optionally install additional tools if present in the archive:

```bash
[ -f ./bin/cardano-submit-api ] && install -m 755 ./bin/cardano-submit-api $HOME/.local/bin/
[ -f ./bin/cardano-tracer ] && install -m 755 ./bin/cardano-tracer $HOME/.local/bin/
```

## 4) Validate installation

```bash
which cardano-node
which cardano-cli
cardano-node --version
cardano-cli --version
```

You should see **cardano-node 10.6.2**. The `cardano-cli` version is released separately and may show a different version (e.g., 10.15.x).

## 5) Install network configuration files

The release archive contains environment configs under `./share/`:

{% tabs %}
{% tab title="Mainnet" %}

```bash
cp ./share/mainnet/* $HOME/cnode/config/
```

{% endtab %}

{% tab title="Testnet (pre-prod)" %}

```bash
cp ./share/preprod/* $HOME/cnode/config/
```

{% endtab %}
{% endtabs %}

Latest configs are also available from the [Intersect environments page](https://book.play.dev.cardano.org/environments.html).

***

Binaries and configs are installed. Continue to **Relay Configuration** to set up and launch your node.


# Relay configuration

Configure your Cardano relay node — verify config files, understand topology, and test startup.

## Understanding relay vs block producer

Your **relay nodes** are publicly reachable and connect to the wider Cardano network. They shield your **block producer** (BP) from direct internet exposure. A typical setup is 1 BP + 2 relays.

The default `topology.json` from the release uses **P2P (peer-to-peer)** networking, which automatically discovers and connects to peers. After you set up your BP, you will add it as a local root peer in your relay's topology. This is covered in the [launching relay](/cardano-relay-configuration/launching-cardano-nodes) section.

## Verifying configuration files

If you followed the installation guide, your config files are already in `~/cnode/config/`.

Verify all 6 files are present:

```bash
ls ~/cnode/config/
```

Expected: `alonzo-genesis.json`, `byron-genesis.json`, `config.json`, `conway-genesis.json`, `shelley-genesis.json`, `topology.json`

## Updating configs manually (optional)

To fetch the latest configs after a new node release:

{% tabs %}
{% tab title="Mainnet" %}

```bash
cd ~/cnode/config

curl -o config.json https://book.play.dev.cardano.org/environments/mainnet/config.json
curl -o topology.json https://book.play.dev.cardano.org/environments/mainnet/topology.json
curl -o byron-genesis.json https://book.play.dev.cardano.org/environments/mainnet/byron-genesis.json
curl -o shelley-genesis.json https://book.play.dev.cardano.org/environments/mainnet/shelley-genesis.json
curl -o alonzo-genesis.json https://book.play.dev.cardano.org/environments/mainnet/alonzo-genesis.json
curl -o conway-genesis.json https://book.play.dev.cardano.org/environments/mainnet/conway-genesis.json

ls -al
```

{% endtab %}

{% tab title="Testnet (pre-prod)" %}

```bash
cd ~/cnode/config

curl -o config.json https://book.play.dev.cardano.org/environments/preprod/config.json
curl -o topology.json https://book.play.dev.cardano.org/environments/preprod/topology.json
curl -o byron-genesis.json https://book.play.dev.cardano.org/environments/preprod/byron-genesis.json
curl -o shelley-genesis.json https://book.play.dev.cardano.org/environments/preprod/shelley-genesis.json
curl -o alonzo-genesis.json https://book.play.dev.cardano.org/environments/preprod/alonzo-genesis.json
curl -o conway-genesis.json https://book.play.dev.cardano.org/environments/preprod/conway-genesis.json

ls -al
```

{% endtab %}
{% endtabs %}

## Quick test run

Verify the node starts before configuring the systemd service:

```bash
cardano-node run \
  --database-path /home/cardano/cnode/db \
  --socket-path /home/cardano/cnode/sockets/node.socket \
  --port 3001 \
  --config /home/cardano/cnode/config/config.json \
  --topology /home/cardano/cnode/config/topology.json
```

You should see the node start syncing:

<figure><img src="/files/TGQWBbwQ8nIyM7LgQ9iR" alt="cardano-node syncing from genesis"><figcaption></figcaption></figure>

Press **Ctrl+C** to stop the node and continue to the next section.


# Downloading the blockchain

Bootstrap the blockchain database using Mithril snapshots or csnapshots.io.

Syncing from genesis can take days. Use a snapshot service to bootstrap the database in minutes instead.

{% tabs %}
{% tab title="Mithril (Mainnet)" %}
Auto-detects architecture and fetches the latest Mithril release:

```bash
cd /home/cardano/cnode

rm -rf db

ARCH=$(uname -m)
if [ "$ARCH" = "x86_64" ]; then MARCH=x64; elif [ "$ARCH" = "aarch64" ]; then MARCH=arm64; else echo "Unsupported arch: $ARCH"; exit 1; fi

MITHRIL_VERSION=$(curl -s https://api.github.com/repos/input-output-hk/mithril/releases/latest | jq -r '.tag_name')
echo "Installing mithril-client ${MITHRIL_VERSION} (${MARCH})"

curl -L -o mithril.tar.gz \
  "https://github.com/input-output-hk/mithril/releases/download/${MITHRIL_VERSION}/mithril-${MITHRIL_VERSION}-linux-${MARCH}.tar.gz"
tar -xzf mithril.tar.gz
install -m 755 mithril-client $HOME/.local/bin/
rm -f mithril.tar.gz mithril-client mithril-signer mithril-aggregator mithril-relay

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

mithril-client cardano-db download --include-ancillary latest
```

{% hint style="info" %}
`--include-ancillary` downloads the last ledger state and immutable file, significantly speeding up initial sync. The ancillary data is verified against a separate Ed25519 key.
{% endhint %}
{% endtab %}

{% tab title="Mithril (Testnet / pre-prod)" %}

```bash
cd /home/cardano/cnode

rm -rf db

ARCH=$(uname -m)
if [ "$ARCH" = "x86_64" ]; then MARCH=x64; elif [ "$ARCH" = "aarch64" ]; then MARCH=arm64; else echo "Unsupported arch: $ARCH"; exit 1; fi

MITHRIL_VERSION=$(curl -s https://api.github.com/repos/input-output-hk/mithril/releases/latest | jq -r '.tag_name')
echo "Installing mithril-client ${MITHRIL_VERSION} (${MARCH})"

curl -L -o mithril.tar.gz \
  "https://github.com/input-output-hk/mithril/releases/download/${MITHRIL_VERSION}/mithril-${MITHRIL_VERSION}-linux-${MARCH}.tar.gz"
tar -xzf mithril.tar.gz
install -m 755 mithril-client $HOME/.local/bin/
rm -f mithril.tar.gz mithril-client mithril-signer mithril-aggregator mithril-relay

export CARDANO_NETWORK=preprod
export AGGREGATOR_ENDPOINT=https://aggregator.release-preprod.api.mithril.network/aggregator
export GENESIS_VERIFICATION_KEY=$(wget -q -O - https://raw.githubusercontent.com/input-output-hk/mithril/main/mithril-infra/configuration/release-preprod/genesis.vkey)
export ANCILLARY_VERIFICATION_KEY=$(wget -q -O - https://raw.githubusercontent.com/input-output-hk/mithril/main/mithril-infra/configuration/release-preprod/ancillary.vkey)

mithril-client cardano-db download --include-ancillary latest
```

{% endtab %}

{% tab title="csnapshots.io" %}
[csnapshots.io](https://csnapshots.io/) provides compressed database archives streamed and extracted on the fly.

Install dependencies:

```bash
sudo apt update && sudo apt install liblz4-tool jq curl -y
```

Remove any existing database:

```bash
rm -rf /home/cardano/cnode/db
```

Download and extract:

**Mainnet** (archive is 100+ GB):

```bash
wget -c -O - "https://downloads.csnapshots.io/mainnet/$(wget -qO- https://downloads.csnapshots.io/mainnet/mainnet-db-snapshot.json | jq -r .[].file_name)" | lz4 -c -d - | tar -x -C /home/cardano/cnode/
```

**Pre-prod testnet** (archive is under 10 GB):

```bash
curl -o - "https://downloads.csnapshots.io/snapshots/testnet/$(curl -s https://downloads.csnapshots.io/snapshots/testnet/testnet-db-snapshot.json | jq -r .[].file_name)" | lz4 -c -d - | tar -x -C /home/cardano/cnode/
```

{% endtab %}
{% endtabs %}


# Launching the relay node

Set up cardano-node as a systemd service so it runs in the background and survives reboots.

Running cardano-node as a **systemd service** is the recommended approach for production servers. The node starts automatically on boot and restarts on failure.

## Create the systemd service

```bash
cat <<EOF | sudo tee /etc/systemd/system/cardano-node.service
[Unit]
Description=Cardano Relay Node
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=cardano
Group=cardano
WorkingDirectory=/home/cardano/cnode
ExecStart=/home/cardano/.local/bin/cardano-node run \\
    --config /home/cardano/cnode/config/config.json \\
    --topology /home/cardano/cnode/config/topology.json \\
    --database-path /home/cardano/cnode/db \\
    --socket-path /home/cardano/cnode/sockets/node.socket \\
    --host-addr 0.0.0.0 \\
    --port 3001
KillSignal=SIGINT
RestartKillSignal=SIGINT
StandardOutput=journal
StandardError=journal
SyslogIdentifier=cardano-relay
LimitNOFILE=1048576
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
```

## Enable and start

```bash
sudo systemctl daemon-reload
sudo systemctl enable cardano-node.service
sudo systemctl start cardano-node.service
```

![creating and enabling cardano node as system service](/files/IAHGCKWzZ8mSEgqtY5Cx)

## Verify the node is running

```bash
journalctl -u cardano-node.service -f -o cat
```

<figure><img src="/files/Am14HaYObYlW7opjGqNe" alt="journalctl showing cardano-node syncing"><figcaption></figcaption></figure>

Your first relay is running. Repeat this process on your second relay server.

***

## Architecture overview

A stake pool requires a minimum of 2 servers:

| Role                   | Purpose                                                                                |
| ---------------------- | -------------------------------------------------------------------------------------- |
| **Relay nodes** (1-2)  | Publicly reachable, connect to the Cardano network, shield the BP from direct exposure |
| **Block producer** (1) | Mints blocks, connected only to your relays                                            |
| **Offline machine**    | Air-gapped computer for generating and storing cold keys and signing transactions      |

Recommended: 2 relay nodes per block producer.

![](/files/gK8cfpV5IYmxjFiVwfDS)

***

## Topology: connecting relays to your BP

After you set up your block producer, edit `topology.json` on **each** server so they know about each other.

### On each relay — add your BP as a local root peer

Edit `~/cnode/config/topology.json` and add your BP's **private IP** in the `localRoots` section:

```json
{
  "bootstrapPeers": [
    { "address": "backbone.cardano.iog.io", "port": 3001 },
    { "address": "backbone.mainnet.emurgornd.com", "port": 3001 },
    { "address": "backbone.mainnet.cardanofoundation.org", "port": 3001 }
  ],
  "localRoots": [
    {
      "accessPoints": [
        { "address": "YOUR_BP_PRIVATE_IP", "port": 3001, "description": "my BP" },
        { "address": "YOUR_OTHER_RELAY_IP", "port": 3001, "description": "my relay 2" }
      ],
      "advertise": false,
      "trustable": true,
      "hotValency": 2
    }
  ],
  "publicRoots": [
    { "accessPoints": [], "advertise": false }
  ],
  "useLedgerAfterSlot": 128908821
}
```

### On the BP — add your relays only

Set `useLedgerAfterSlot` to `-1` so the BP only connects to your relays and never discovers random peers. Remove `bootstrapPeers` entries:

```json
{
  "bootstrapPeers": [],
  "localRoots": [
    {
      "accessPoints": [
        { "address": "YOUR_RELAY1_IP", "port": 3001, "description": "relay 1" },
        { "address": "YOUR_RELAY2_IP", "port": 3001, "description": "relay 2" }
      ],
      "advertise": false,
      "trustable": true,
      "hotValency": 2
    }
  ],
  "publicRoots": [
    { "accessPoints": [], "advertise": false }
  ],
  "useLedgerAfterSlot": -1
}
```

{% hint style="info" %}
**`hotValency`** — number of peers the node actively maintains connections to. Set it to the number of nodes in the group.

**`trustable: true`** — use for your own infrastructure (BP and relays). For external pool peers, use `false`.

**`useLedgerAfterSlot: -1`** on the BP prevents it from connecting to random peers.
{% endhint %}

After editing topology, restart the node:

```bash
sudo systemctl restart cardano-node
```

{% hint style="danger" %}
**Never generate wallet or stake pool keys on your online servers.** Use an offline (air-gapped) machine or a hardware wallet (Trezor/Ledger). Anyone with access to your keys has full control over your pool and funds.
{% endhint %}


# Monitoring with gLiveView

Install gLiveView for real-time terminal-based monitoring of your Cardano node.

[gLiveView](https://cardano-community.github.io/guild-operators/Scripts/gliveview/) is a terminal-based monitoring tool from the Guild Operators community. It provides a real-time dashboard showing node status, sync progress, peer connections, block propagation, and resource usage.

## Installation

Download gLiveView and its environment config file:

```bash
mkdir -p $HOME/.local/logs
curl -sL -o $HOME/.local/bin/gLiveView.sh \
  https://raw.githubusercontent.com/cardano-community/guild-operators/refs/heads/alpha/scripts/cnode-helper-scripts/gLiveView.sh
curl -sL -o $HOME/.local/bin/env \
  https://raw.githubusercontent.com/cardano-community/guild-operators/refs/heads/alpha/scripts/cnode-helper-scripts/env
chmod 755 $HOME/.local/bin/gLiveView.sh
```

## Configuration

The default `env` file expects paths at `/opt/cardano/cnode`. Update it to match this guide's layout:

```bash
sed -i "s|#CNODE_HOME=.*|CNODE_HOME=\"/home/cardano/cnode\"|" $HOME/.local/bin/env
sed -i "s|#CNODE_PORT=.*|CNODE_PORT=3001|" $HOME/.local/bin/env
sed -i 's|#CONFIG=.*|CONFIG="${CNODE_HOME}/config/config.json"|' $HOME/.local/bin/env
sed -i 's|#SOCKET=.*|SOCKET="${CNODE_HOME}/sockets/node.socket"|' $HOME/.local/bin/env
sed -i 's|#TOPOLOGY=.*|TOPOLOGY="${CNODE_HOME}/config/topology.json"|' $HOME/.local/bin/env
```

Verify the settings:

```bash
grep -E "^(CNODE_HOME|CNODE_PORT|CONFIG|SOCKET|TOPOLOGY)" $HOME/.local/bin/env
```

Expected output:

```
CNODE_HOME="/home/cardano/cnode"
CNODE_PORT=3001
CONFIG="${CNODE_HOME}/config/config.json"
SOCKET="${CNODE_HOME}/sockets/node.socket"
TOPOLOGY="${CNODE_HOME}/config/topology.json"
```

## Usage

With your node running, launch gLiveView:

```bash
gLiveView.sh
```

The dashboard shows:

| Section               | Information                                     |
| --------------------- | ----------------------------------------------- |
| **Header**            | Node name, network, uptime, port, version       |
| **Epoch**             | Current epoch, progress bar, time remaining     |
| **Block/Slot**        | Current block, slot, tip reference, sync status |
| **Connections**       | P2P peer stats, incoming/outgoing connections   |
| **Block propagation** | Last block time, propagation percentiles        |
| **Resource usage**    | CPU, memory, disk I/O, process stats            |

{% hint style="info" %}
Press **Q** to quit gLiveView. Press **P** for peer analysis view.
{% endhint %}

## Install on all nodes

Install gLiveView on each of your relay and BP servers — it is a read-only monitoring tool and safe to run on any node.


# Installing SPOS scripts

Install and configure the StakePool Operator Scripts (SPOS) for pool management.

{% hint style="warning" %}
Install these scripts on a **secure, offline workstation** — not on your online relay or BP server. The keys generated here control your wallets and stake pool. Anyone with access to these keys has full control over your funds and pool.
{% endhint %}

{% hint style="info" %}
SPOS supports air-gapped (offline) key generation and hardware wallets (Trezor/Ledger) for maximum security.
{% endhint %}

## 1) Install dependencies

```bash
sudo apt update -y
sudo apt install -y curl bc jq
```

## 2) Clone the SPOS repository

```bash
cd ~
mkdir -p git && cd git
rm -rf scripts
git clone https://github.com/gitmachtl/scripts
cd scripts
ls -al
```

<figure><img src="/files/1MRQusAl0ukJzNHUQCcE" alt="SPOS scripts directory listing"><figcaption></figcaption></figure>

## 3) Copy scripts to your PATH

{% tabs %}
{% tab title="Mainnet" %}

```bash
cp cardano/mainnet/* ~/.local/bin/
```

{% endtab %}

{% tab title="Testnet (pre-prod)" %}

```bash
cp cardano/testnet/* ~/.local/bin/
```

{% endtab %}
{% endtabs %}

## 4) Create the SPOS configuration file

This tells the scripts where to find the node socket and genesis files. Placing it in your home directory means you do not need to reconfigure when upgrading scripts.

{% tabs %}
{% tab title="Mainnet" %}

```bash
cat <<EOF > ~/.common.inc
socket="/home/cardano/cnode/sockets/node.socket"

genesisfile="/home/cardano/cnode/config/shelley-genesis.json"
genesisfile_byron="/home/cardano/cnode/config/byron-genesis.json"

cardanocli="cardano-cli"
cardanonode="cardano-node"

magicparam="--mainnet"
addrformat="--mainnet"

byronToShelleyEpochs=208
EOF
```

{% endtab %}

{% tab title="Testnet (pre-prod)" %}

```bash
cat <<EOF > ~/.common.inc
socket="/home/cardano/cnode/sockets/node.socket"

genesisfile="/home/cardano/cnode/config/shelley-genesis.json"
genesisfile_byron="/home/cardano/cnode/config/byron-genesis.json"

cardanocli="cardano-cli"
cardanonode="cardano-node"

magicparam="--testnet-magic 1"
addrformat="--testnet-magic 1"

byronToShelleyEpochs=4
EOF
```

{% endtab %}
{% endtabs %}

## 5) Verify installation

```bash
00_common.sh
```

<figure><img src="/files/3UmNMjPxky0H47L7XHYg" alt="00_common.sh showing cli 10.15.0.0 / node 10.6.2"><figcaption></figcaption></figure>

The output shows the detected `cardano-cli` and `cardano-node` versions, the operating mode, and the configured network.

{% hint style="info" %}
The "Warning: Node-Socket does not exist" message is expected if the node is not running on this machine (e.g., on an offline workstation).
{% endhint %}


# Generating wallet keys

Generate payment and staking keys for pool pledge and transaction fees.

All key generation should happen in your `~/cnode/keys/` directory.

```bash
cd ~/cnode/keys/
```

## 1) Create a payment wallet

Generate a payment-only wallet (`myWallet`) for paying transaction fees and the pool deposit:

```bash
02_genPaymentAddrOnly.sh myWallet cli
ls -al
```

![](/files/-MTGXGPU1QL8FiAaQwBq)

## 2) Check the wallet balance

```bash
01_queryAddress.sh myWallet
```

![](/files/-MTGyalPxmrlLtRQGyDv)

The wallet is empty — expected for a new address.

{% hint style="info" %}
You need approximately **505 ADA** to complete pool registration:
{% endhint %}

| Purpose                | Amount                                           |
| ---------------------- | ------------------------------------------------ |
| Pool key deposit       | 500 ADA (refunded when you de-register the pool) |
| Delegation key deposit | \~2 ADA                                          |
| Transaction fees       | \~3 ADA                                          |

{% hint style="info" %}
Send 505 ADA to the address shown by the query command above, then verify the balance before continuing.
{% endhint %}

After funding, verify the balance:

```bash
01_queryAddress.sh myWallet
```

![](/files/-MTGz4XPdzQksWOUIdSf)

## 3) Create a staking/pledge address

Generate the address that will hold your pool's pledge:

```bash
03a_genStakingPaymentAddr.sh poolOwner cli
ls -al poolOwner*
```

![](/files/-MTGZ9dqX9K_y7U5jwdj)

## 4) Register the stake key on-chain

Register the owner stake key. `myWallet` pays for the transaction and deposit:

```bash
03b_regStakingAddrCert.sh poolOwner myWallet
```

![](/files/-MTGzSIHeRaSyoSw4pAV)

## 5) Verify registration

```bash
03c_checkStakingAddrOnChain.sh poolOwner
```

![](/files/-MTGzmokj1jlHS-d-a5x)

The payment wallet and pledge wallet are ready. Continue to generating pool keys.


# Generating block producer keys

Generate node keys, VRF keys, KES keys, operational certificate, and register your stake pool.

## 1) Generate node, VRF, and KES keys

```bash
04a_genNodeKeys.sh myPool cli
04b_genVRFKeys.sh myPool cli
04c_genKESKeys.sh myPool cli
04d_genNodeOpCert.sh myPool

ls -al myPool*
```

![](/files/-MTH1dBuxaXH1LlP-G8Z)

## 2) Generate the pool certificate

Run the certificate generation script:

```bash
05a_genStakepoolCert.sh myPool
```

This creates a JSON template (`myPool.pool.json`). Edit it with your pool's details:

```bash
nano myPool.pool.json
```

![](/files/-MTH2H4UDjpUlXeDJxaf)

Example configuration for a single-owner pool:

| Parameter             | Example value                                      |
| --------------------- | -------------------------------------------------- |
| Pledge                | 1,000,000 ADA                                      |
| Fixed fee             | 340 ADA (current minimum)                          |
| Margin                | 5%                                                 |
| Relays                | IP-based: 89.191.111.111:3001, 89.191.111.112:3001 |
| Ticker                | XPOOL                                              |
| Metadata URL          | <https://yoursite.com/pool.metadata.json>          |
| Extended metadata URL | <https://yoursite.com/pool.extended.json>          |

![](/files/-MTH5p12tAKxbthQ9OXU)

After editing, re-run the certificate generation:

```bash
05a_genStakepoolCert.sh myPool
```

![](/files/-MTHA76QuSah8rVLBtA9)

The script creates an extended metadata template. Edit it:

```bash
nano myPool.additional-metadata.json
```

![](/files/-MTHAKJTnFe7_eE6wtkQ)

Run the certificate generation one final time:

```bash
05a_genStakepoolCert.sh myPool
```

![](/files/-MTHAz3k8p09ttoXuDLS)

## 3) Upload metadata files

The script generates two metadata files that **must be uploaded to your web server** before proceeding:

| File                            | Upload to                                         |
| ------------------------------- | ------------------------------------------------- |
| `myPool.metadata.json`          | Your metadata URL (defined in pool.json)          |
| `myPool.extended-metadata.json` | Your extended metadata URL (defined in pool.json) |

Rename and upload them:

```bash
cp myPool.metadata.json pool.metadata.json
cp myPool.extended-metadata.json pool.extended.json
```

Upload via SCP, SFTP, or any method you prefer. Verify the URLs are accessible before continuing.

## 4) Create the delegation certificate

Delegate to your own pool:

```bash
05b_genDelegationCert.sh myPool poolOwner
```

## 5) Fund the pledge address

Send your pledged amount to the `poolOwner.payment` address:

```bash
cat poolOwner.payment.addr
```

![](/files/-MTHGlsQ7xWkZjFx1Rf4)

Send your pledge to this address and verify it has arrived:

```bash
01_queryAddress.sh poolOwner.payment
```

![](/files/-MTHGNpZOGDDzl5i2BOC)

## 6) Register the stake pool on-chain

Register the pool. `myWallet` pays the transaction fee and 500 ADA deposit:

```bash
05c_regStakepoolCert.sh myPool myWallet
```

![](/files/-MTHHgo-cjJwiuTpBYx9)

After registration propagates (minutes to hours), the pool will appear in wallets like Daedalus:

![](/files/-MTHM7RAxihndKNkmkSr)


# Launching the block producer

Configure and launch the block producer as a systemd service.

With keys and certificates generated, transfer them to your BP server and start the node.

## 1) Transfer keys to the BP server

Copy these files to `/home/cardano/cnode/keys/` on your block producer:

| Source file              | Destination          |
| ------------------------ | -------------------- |
| `myPool.kes-000.skey`    | `myPool.kes.skey`    |
| `myPool.vrf.skey`        | `myPool.vrf.skey`    |
| `myPool.node-000.opcert` | `myPool.node.opcert` |

Rename and secure the files:

```bash
cd ~/cnode/keys
mv myPool.kes-000.skey myPool.kes.skey
mv myPool.node-000.opcert myPool.node.opcert
chmod 400 *
```

## 2) Create the systemd service

{% tabs %}
{% tab title="Systemd service (recommended)" %}
Create the service file:

```bash
cat <<EOF | sudo tee /etc/systemd/system/cardano-node.service
[Unit]
Description=Cardano Block Producer
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=cardano
Group=cardano
WorkingDirectory=/home/cardano/cnode
ExecStart=/home/cardano/.local/bin/cardano-node run \\
    --config /home/cardano/cnode/config/config.json \\
    --topology /home/cardano/cnode/config/topology.json \\
    --database-path /home/cardano/cnode/db \\
    --socket-path /home/cardano/cnode/sockets/node.socket \\
    --host-addr 0.0.0.0 \\
    --port 3001 \\
    --shelley-kes-key /home/cardano/cnode/keys/myPool.kes.skey \\
    --shelley-vrf-key /home/cardano/cnode/keys/myPool.vrf.skey \\
    --shelley-operational-certificate /home/cardano/cnode/keys/myPool.node.opcert
KillSignal=SIGINT
RestartKillSignal=SIGINT
StandardOutput=journal
StandardError=journal
SyslogIdentifier=cardano-bp
LimitNOFILE=1048576
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
```

Enable and start:

```bash
sudo systemctl daemon-reload
sudo systemctl enable cardano-node.service
sudo systemctl start cardano-node.service
```

If updating an existing service, reload and restart:

```bash
sudo systemctl daemon-reload
sudo systemctl restart cardano-node.service
```

Check the logs:

```bash
journalctl -u cardano-node.service -f -o cat
```

![](/files/-MYaGpVA-TX4dzobcoNr)
{% endtab %}

{% tab title="Script with tmux (testnet only)" %}
Create the launch script:

```bash
cd ~/cnode/scripts
nano node.sh
```

```bash
#!/bin/bash

cardano-node run \
 --database-path ~/cnode/db \
 --socket-path ~/cnode/sockets/node.socket \
 --host-addr 0.0.0.0 \
 --port 3001 \
 --config ~/cnode/config/config.json \
 --topology ~/cnode/config/topology.json \
 --shelley-kes-key ~/cnode/keys/myPool.kes.skey \
 --shelley-vrf-key ~/cnode/keys/myPool.vrf.skey \
 --shelley-operational-certificate ~/cnode/keys/myPool.node.opcert
```

Make it executable and run inside tmux:

```bash
chmod +x ~/cnode/scripts/node.sh
tmux new -s cardano
bash ~/cnode/scripts/node.sh
```

Detach from tmux: **Ctrl+B** then **D**. Reattach later with `tmux attach -t cardano`.
{% endtab %}
{% endtabs %}

***

## KES key rotation

{% hint style="warning" %}
KES (Key Evolving Signature) keys **expire** after a set number of periods (typically 62 periods, approximately 93 days). When they expire, your BP stops producing blocks.
{% endhint %}

Check your current KES status:

```bash
cardano-cli query kes-period-info --mainnet \
  --op-cert-file ~/cnode/keys/myPool.node.opcert
```

To rotate, generate new KES keys and operational certificate on your **offline machine**:

```bash
cd ~/cnode/keys
04c_genKESKeys.sh myPool cli
04d_genNodeOpCert.sh myPool
```

Transfer the new `myPool.kes.skey` and `myPool.node.opcert` to your BP server and restart the node.

Set a calendar reminder to rotate KES keys **every 80 days** to stay ahead of expiration.


# Upgrade to 10.6.2

Safe upgrade guide from older 8.x/9.x/10.x nodes to cardano-node 10.6.2.

This guide upgrades an existing relay or block producer to **cardano-node 10.6.2** with minimal downtime.

{% hint style="info" %}
Upgrade your **relay nodes first**, then the block producer. This keeps the pool producing blocks while you validate the new version on relays.
{% endhint %}

## 0) Pre-checks

Record current versions and confirm the node is healthy before proceeding:

```bash
cardano-node --version
cardano-cli --version
systemctl status cardano-node --no-pager
```

## 1) Download and verify the release artifact

```bash
cd /tmp
ARCH=$(uname -m)
if [ "$ARCH" = "x86_64" ]; then FILE=cardano-node-10.6.2-linux-amd64.tar.gz; else FILE=cardano-node-10.6.2-linux-arm64.tar.gz; fi

curl -L -o "$FILE" "https://github.com/IntersectMBO/cardano-node/releases/download/10.6.2/$FILE"
curl -L -o cardano-node-10.6.2-sha256sums.txt "https://github.com/IntersectMBO/cardano-node/releases/download/10.6.2/cardano-node-10.6.2-sha256sums.txt"

sha256sum "$FILE"
cat cardano-node-10.6.2-sha256sums.txt
```

Verify the checksum matches exactly before continuing.

## 2) Back up binaries and configs

```bash
sudo cp -a /home/cardano/.local/bin/cardano-node /home/cardano/.local/bin/cardano-node.bak.$(date +%F-%H%M) || true
sudo cp -a /home/cardano/.local/bin/cardano-cli /home/cardano/.local/bin/cardano-cli.bak.$(date +%F-%H%M) || true
cp -a /home/cardano/cnode/config /home/cardano/cnode/config.bak.$(date +%F-%H%M)
```

## 3) Install new binaries

```bash
tar -xzf "$FILE"
install -m 755 ./bin/cardano-node ./bin/cardano-cli /home/cardano/.local/bin/
```

## 4) Refresh config files

For mainnet:

```bash
cp ./share/mainnet/* /home/cardano/cnode/config/
```

For pre-prod testnet, use `./share/preprod/*` instead.

## 5) Restart the node

```bash
sudo systemctl daemon-reload
sudo systemctl restart cardano-node
sleep 3
sudo systemctl status cardano-node --no-pager
```

## 6) Post-upgrade validation

```bash
cardano-node --version
cardano-cli --version
journalctl -u cardano-node -n 100 --no-pager
cardano-cli query tip --mainnet --socket-path /home/cardano/cnode/sockets/node.socket
```

## 7) Rollback (if needed)

List your backup files and restore the previous version:

```bash
ls /home/cardano/.local/bin/cardano-node.bak.*
```

```bash
sudo systemctl stop cardano-node
cp -a /home/cardano/.local/bin/cardano-node.bak.YYYY-MM-DD-HHMM /home/cardano/.local/bin/cardano-node
cp -a /home/cardano/.local/bin/cardano-cli.bak.YYYY-MM-DD-HHMM /home/cardano/.local/bin/cardano-cli
sudo systemctl start cardano-node
```

Replace `YYYY-MM-DD-HHMM` with the actual backup timestamp from the listing above.

***

## Notes

| Topic         | Detail                                                                             |
| ------------- | ---------------------------------------------------------------------------------- |
| Source builds | Must use **libblst 0.3.14**                                                        |
| CLI version   | `cardano-cli` version may differ from node version (expected in current packaging) |
| Upgrade order | Relay first, then BP                                                               |


