X PrimeFlow

Lumera

Lumera
Chain ID: lumera-testnet-2
Block Height:
Network Status: Connecting
Explorer
lumera-testnet-2

Manual node installation

Lumera reviewed manual node installation and validator workflow.

BonyNode setup profile

Configure once โ€” generate every command

These values update the Lumera installation, wallet, validator, sync and firewall commands below.

No seed phrases or private keys
Advanced node settings

Optional pruning, indexer and validator defaults.

Recommended hardware: 6 Cores, 8GB RAM, 200GB of storage (NVME)Profile is valid โ€” commands are ready

Network: Testnet

Chain ID: lumera-testnet-2

Guide review: 2026-08-27 ยท 3 source references

Reviewed binary version: v1.20.2-rc1

Recommended hardware: 6 Cores, 8GB RAM, 200GB of storage (NVME)

Go version: 1.26.2

Binary: lumerad

Node home: ~/.lumera

Prepare the node host

# install dependencies
sudo apt update
sudo apt install -y ca-certificates curl git wget htop tmux build-essential jq make lz4 gcc unzip

# install Go, if needed
cd "$HOME"
VER="1.26.2"
case "$(uname -m)" in
  x86_64) ARCH="amd64" ;;
  aarch64|arm64) ARCH="arm64" ;;
  *) echo "Unsupported architecture: $(uname -m)" >&2; exit 1 ;;
esac
GO_FILE="go${VER}.linux-${ARCH}.tar.gz"
GO_SHA256=$(curl -fsSL 'https://go.dev/dl/?mode=json&include=all' \
  | jq -r --arg file "$GO_FILE" '.[] | .files[] | select(.filename == $file) | .sha256' \
  | head -n 1)
test -n "$GO_SHA256"
curl -fL --retry 3 "https://go.dev/dl/$GO_FILE" -o "$GO_FILE"
printf '%s  %s\n' "$GO_SHA256" "$GO_FILE" | sha256sum --check -

if [ -d /usr/local/go ]; then
  sudo mv /usr/local/go "/usr/local/go.backup.$(date -u +%Y%m%dT%H%M%SZ)"
fi
sudo tar -C /usr/local -xzf "$GO_FILE"
rm -f "$GO_FILE"
touch "$HOME/.bash_profile"
grep -qxF 'export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin' "$HOME/.bash_profile" \
  || echo 'export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin' >> "$HOME/.bash_profile"
source "$HOME/.bash_profile"
mkdir -p "$HOME/go/bin"
go version

Set node variables

# set variables
ENV_FILE="$HOME/.bonynode-lumera.env"
cat > "$ENV_FILE" <<'EOF'
export WALLET='wallet'
export MONIKER='validator-name'
export LUMERA_CHAIN_ID='lumera-testnet-2'
export LUMERA_PORT='59'
export LUMERA_HOME="$HOME/.lumera"
export DAEMON_NAME='lumerad'
EOF
chmod 600 "$ENV_FILE"

touch "$HOME/.bash_profile"
PROFILE_LINE='source "$HOME/.bonynode-lumera.env"'
grep -qxF "$PROFILE_LINE" "$HOME/.bash_profile" || echo "$PROFILE_LINE" >> "$HOME/.bash_profile"
source "$ENV_FILE"

printf 'Wallet: %s | Moniker: %s | Chain ID: %s | Port prefix: %s\n' \
  "$WALLET" "$MONIKER" "$LUMERA_CHAIN_ID" "$LUMERA_PORT"

Build the pinned node binary

set -euo pipefail
RELEASE="v1.20.2-rc1"
EXPECTED_COMMIT="aafc4a287d2ad78f5be873eb9ee24189fe6302fb"
SOURCE_DIR="$HOME/lumera-testnet-v1.20.2-rc1"

test ! -e "$SOURCE_DIR" || { echo "Stop: $SOURCE_DIR already exists; review it first." >&2; exit 1; }
git clone --branch "$RELEASE" --depth 1 https://github.com/LumeraProtocol/lumera.git "$SOURCE_DIR"
ACTUAL_COMMIT=$(git -C "$SOURCE_DIR" rev-parse HEAD)
[ "$ACTUAL_COMMIT" = "$EXPECTED_COMMIT" ] || { echo "Unexpected source commit: $ACTUAL_COMMIT" >&2; exit 1; }
cd "$SOURCE_DIR"
make build
test -x "build/lumerad"
sudo install -m 0755 "build/lumerad" "/usr/local/bin/lumerad"
"lumerad" version --long || "lumerad" version

# Verify which binary will be used before initialization.
command -v "lumerad"
"lumerad" version

Initialize a new node home

set -euo pipefail
BIN="lumerad"
CHAIN_ID="lumera-testnet-2"
MONIKER='validator-name'
NODE_HOME="$HOME/.lumera"

command -v "$BIN"
test ! -e "$NODE_HOME" || { echo "Stop: $NODE_HOME already exists; back it up and review it first." >&2; exit 1; }
"$BIN" init "$MONIKER" --chain-id "$CHAIN_ID" --home "$NODE_HOME"
test -f "$NODE_HOME/config/genesis.json"

set -euo pipefail
CHAIN_ID="lumera-testnet-2"
NODE_HOME="$HOME/.lumera"
CLIENT_TOML="$NODE_HOME/config/client.toml"
LOCAL_RPC="tcp://127.0.0.1:59657"

test -f "$CLIENT_TOML"
cp --no-clobber "$CLIENT_TOML" "$CLIENT_TOML.bonynode-backup" || true
sed -i -E \
  -e "s|^chain-id[[:space:]]*=.*|chain-id = \"$CHAIN_ID\"|" \
  -e 's|^keyring-backend[[:space:]]*=.*|keyring-backend = "os"|' \
  -e "s|^node[[:space:]]*=.*|node = \"$LOCAL_RPC\"|" \
  "$CLIENT_TOML"
grep -E '^(chain-id|keyring-backend|node)[[:space:]]*=' "$CLIENT_TOML"

Install genesis and configure peers

set -euo pipefail
CHAIN_ID='lumera-testnet-2'
NODE_HOME="$HOME/.lumera"
GENESIS_URL='https://testnet-files.bonynode.online/lumera/genesis.json'
GENESIS_SHA256='8d30d41d2711b43d1f27f49816c68c6bafb799f0f08fb7e4a6676c4158150031'
GENESIS_MAX_BYTES=1000000
WORK_DIR=$(mktemp -d)
trap 'rm -rf "$WORK_DIR"' EXIT
GENESIS_DOWNLOAD="$WORK_DIR/genesis.download"
GENESIS_JSON="$WORK_DIR/genesis.json"
GENESIS_CHUNK="$WORK_DIR/genesis-chunk-zero.json"

test -d "$NODE_HOME/config"
curl --fail --show-error --location --retry 3 \
  --connect-timeout 15 --max-time 1800 --proto '=https' --proto-redir '=https' \
  --max-filesize "$GENESIS_MAX_BYTES" --output "$GENESIS_JSON" "$GENESIS_URL"
jq -e --arg chain "$CHAIN_ID" 'type == "object" and .chain_id == $chain and (.app_state | type == "object")' "$GENESIS_JSON" >/dev/null
printf '%s  %s\n' "$GENESIS_SHA256" "$GENESIS_JSON" | sha256sum --check -
cp --no-clobber "$NODE_HOME/config/genesis.json" "$NODE_HOME/config/genesis.json.init-backup" || true
install -m 0644 "$GENESIS_JSON" "$NODE_HOME/config/genesis.json"
printf 'Installed verified genesis for %s (%s)\n' "$CHAIN_ID" "$GENESIS_SHA256"

set -euo pipefail
NODE_HOME="$HOME/.lumera"
ADDRBOOK_URL='https://testnet-files.bonynode.online/lumera/addrbook.json'
ADDRBOOK_SHA256='39e634c2fff19c45f2eb3d9e549a1be1c6f8811c2ab5703c620aec698f143af8'
ADDRBOOK_MAX_BYTES=1000000
WORK_DIR=$(mktemp -d)
trap 'rm -rf "$WORK_DIR"' EXIT
ADDRBOOK_JSON="$WORK_DIR/addrbook.json"

test -d "$NODE_HOME/config"
curl --fail --show-error --location --retry 3   --connect-timeout 15 --max-time 120 --proto '=https' --proto-redir '=https'   --max-filesize "$ADDRBOOK_MAX_BYTES" --output "$ADDRBOOK_JSON" "$ADDRBOOK_URL"
jq -e 'type == "object" and (.addrs | type == "array") and (.addrs | length > 0)' "$ADDRBOOK_JSON" >/dev/null
printf '%s  %s
' "$ADDRBOOK_SHA256" "$ADDRBOOK_JSON" | sha256sum --check -
cp --no-clobber "$NODE_HOME/config/addrbook.json" "$NODE_HOME/config/addrbook.json.init-backup" || true
install -m 0644 "$ADDRBOOK_JSON" "$NODE_HOME/config/addrbook.json"
printf 'Installed reviewed addrbook (%s peers, %s)
' 131 "$ADDRBOOK_SHA256"

Apply ports, pruning, gas and network settings

set -euo pipefail
NODE_HOME="$HOME/.lumera"
APP_TOML="$NODE_HOME/config/app.toml"
CONFIG_TOML="$NODE_HOME/config/config.toml"

test -f "$APP_TOML"
test -f "$CONFIG_TOML"
cp --no-clobber "$APP_TOML" "$APP_TOML.bonynode-backup" || true
cp --no-clobber "$CONFIG_TOML" "$CONFIG_TOML.bonynode-backup" || true

# Application API, gRPC and optional EVM ports.
sed -i \
  -e 's|:1317|:59317|g' \
  -e 's|:8080|:59080|g' \
  -e 's|:9090|:59090|g' \
  -e 's|:9091|:59091|g' \
  -e 's|:8545|:59545|g' \
  -e 's|:8546|:59546|g' \
  -e 's|:6065|:59065|g' \
  "$APP_TOML"

# CometBFT RPC, P2P, proxy-app, pprof and Prometheus ports.
sed -i \
  -e 's|:26658|:59658|g' \
  -e 's|:26657|:59657|g' \
  -e 's|:6060|:59060|g' \
  -e 's|:26656|:59656|g' \
  -e 's|:26660|:59660|g' \
  "$CONFIG_TOML"

sed -i -E \
  -e 's|^pruning[[:space:]]*=.*|pruning = "custom"|' \
  -e 's|^pruning-keep-recent[[:space:]]*=.*|pruning-keep-recent = "100"|' \
  -e 's|^pruning-keep-every[[:space:]]*=.*|pruning-keep-every = "0"|' \
  -e 's|^pruning-interval[[:space:]]*=.*|pruning-interval = "19"|' \
  "$APP_TOML"
sed -i -E 's|^indexer[[:space:]]*=.*|indexer = "null"|' "$CONFIG_TOML"
sed -i -E 's|^prometheus[[:space:]]*=.*|prometheus = true|' "$CONFIG_TOML"

grep -E '^(pruning|pruning-keep-recent|pruning-interval)[[:space:]]*=' "$APP_TOML"
grep -E '^(indexer|prometheus)[[:space:]]*=' "$CONFIG_TOML"
printf 'RPC=%s P2P=%s API=%s gRPC=%s Prometheus=%s\n' \
  '59657' '59656' '59317' \
  '59090' '59660'

set -euo pipefail
APP_TOML="$HOME/.lumera/config/app.toml"
test -f "$APP_TOML"
cp --no-clobber "$APP_TOML" "$APP_TOML.bonynode-backup" || true

if grep -q '^minimum-gas-prices' "$APP_TOML"; then
  sed -i 's|^minimum-gas-prices *=.*|minimum-gas-prices = "15000ulume"|' "$APP_TOML"
else
  printf '\nminimum-gas-prices = "15000ulume"\n' >> "$APP_TOML"
fi
grep '^minimum-gas-prices' "$APP_TOML"

Create the service, start and verify the node

set -euo pipefail
BIN="lumerad"
CHAIN_ID="lumera-testnet-2"
NODE_HOME="$HOME/.lumera"
SERVICE="lumerad.service"
BIN_PATH=$(command -v "$BIN")
SERVICE_USER="$USER"
LOCAL_RPC_PORT="59657"

test -x "$BIN_PATH"
test -s "$NODE_HOME/config/genesis.json"
sudo tee "/etc/systemd/system/$SERVICE" >/dev/null <<EOF
[Unit]
Description=Lumera node
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=${SERVICE_USER}
ExecStart=${BIN_PATH} start --home ${NODE_HOME}
Restart=on-failure
RestartSec=5
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now "$SERVICE"
sudo systemctl status "$SERVICE" --no-pager

# Wait briefly for the local RPC listener and require the configured chain ID.
for attempt in {1..30}; do
  if STATUS=$(curl --fail --silent "http://127.0.0.1:$LOCAL_RPC_PORT/status"); then
    jq -e --arg chain "$CHAIN_ID" '.result.node_info.network == $chain' <<<"$STATUS"
    exit 0
  fi
  sleep 2
done
echo 'Local RPC did not become ready; inspect the service journal.' >&2
sudo journalctl -u "$SERVICE" -n 100 --no-pager
exit 1

Node sync checker

Compare the selected local RPC height with the chain-verified BonyNode reference. Stop with Ctrl+C.

#!/usr/bin/env bash

CHAIN_ID="lumera-testnet-2"
CONFIG="$HOME/.lumera/config/config.toml"
RPC_PORT=$(grep -m 1 -oP '^laddr = "\K[^"]+' "$CONFIG" | cut -d ':' -f 3)
RPC_PORT="${RPC_PORT:-59657}"
NETWORK_RPC="https://lumera-testnet-rpc.bonynode.online"

while true; do
  LOCAL_STATUS=$(curl -fsS --max-time 10 "http://127.0.0.1:$RPC_PORT/status" 2>/dev/null || true)
  NETWORK_STATUS=$(curl -fsS --max-time 10 "$NETWORK_RPC/status" 2>/dev/null || true)
  LOCAL_CHAIN=$(jq -r '.result.node_info.network // empty' <<<"$LOCAL_STATUS" 2>/dev/null)
  NETWORK_CHAIN=$(jq -r '.result.node_info.network // empty' <<<"$NETWORK_STATUS" 2>/dev/null)
  LOCAL_HEIGHT=$(jq -r '.result.sync_info.latest_block_height // empty' <<<"$LOCAL_STATUS" 2>/dev/null)
  NETWORK_HEIGHT=$(jq -r '.result.sync_info.latest_block_height // empty' <<<"$NETWORK_STATUS" 2>/dev/null)

  if [[ "$LOCAL_CHAIN" != "$CHAIN_ID" || "$NETWORK_CHAIN" != "$CHAIN_ID" ||
        ! "$LOCAL_HEIGHT" =~ ^[0-9]+$ || ! "$NETWORK_HEIGHT" =~ ^[0-9]+$ ]]; then
    echo -e "\033[1;31mError: invalid RPC response. Retrying...\033[0m"
    sleep 5
    continue
  fi

  BLOCKS_LEFT=$((NETWORK_HEIGHT - LOCAL_HEIGHT))
  (( BLOCKS_LEFT < 0 )) && BLOCKS_LEFT=0
  echo -e "\033[1;33mNode Height:\033[1;34m $LOCAL_HEIGHT\033[0m \033[1;33m| Network Height:\033[1;36m $NETWORK_HEIGHT\033[0m \033[1;33m| Blocks Left:\033[1;31m $BLOCKS_LEFT\033[0m"
  sleep 5
done

Create or restore wallet

set -euo pipefail
BIN="lumerad"
WALLET='wallet'
[[ "$WALLET" =~ ^[a-zA-Z0-9_-]{1,64}$ ]] || { echo "Invalid wallet name" >&2; exit 1; }

# Create a new key. Store the mnemonic offline; it cannot be recovered by BonyNode.
"$BIN" keys add "$WALLET"

# To restore an existing key, run this separately:
# "$BIN" keys add "$WALLET" --recover

WALLET_ADDRESS=$("$BIN" keys show "$WALLET" -a)
"$BIN" query bank balances "$WALLET_ADDRESS"

Create validator

Preferred by newer Cosmos SDK versions. The validator message is saved to a JSON file and shown before signing.

#!/usr/bin/env bash
set -euo pipefail

BIN="lumerad"
CHAIN_ID="lumera-testnet-2"
WALLET='wallet'
MONIKER='validator-name'
IDENTITY=''
WEBSITE=''
DETAILS='Independent validator'
AMOUNT="1000000ulume"
MIN_SELF_DELEGATION="1"
COMMISSION_RATE="0.10"
COMMISSION_MAX_RATE="0.20"
COMMISSION_MAX_CHANGE="0.01"
VALIDATOR_FILE="$HOME/lumera-validator.json"
if PUBKEY_JSON=$("$BIN" comet show-validator 2>/dev/null); then
  :
else
  PUBKEY_JSON=$("$BIN" tendermint show-validator)
fi
jq -e 'type == "object"' <<<"$PUBKEY_JSON" >/dev/null

jq -n \
  --argjson pubkey "$PUBKEY_JSON" \
  --arg amount "$AMOUNT" \
  --arg moniker "$MONIKER" \
  --arg identity "$IDENTITY" \
  --arg website "$WEBSITE" \
  --arg details "$DETAILS" \
  --arg rate "$COMMISSION_RATE" \
  --arg maxRate "$COMMISSION_MAX_RATE" \
  --arg maxChange "$COMMISSION_MAX_CHANGE" \
  --arg minSelfDelegation "$MIN_SELF_DELEGATION" \
  '{pubkey:$pubkey, amount:$amount, moniker:$moniker, identity:$identity,
    website:$website, security:"", details:$details,
    "commission-rate":$rate, "commission-max-rate":$maxRate,
    "commission-max-change-rate":$maxChange,
    "min-self-delegation":$minSelfDelegation}' > "$VALIDATOR_FILE"

jq -e 'type == "object" and .pubkey and .amount and .moniker' "$VALIDATOR_FILE" >/dev/null
cat "$VALIDATOR_FILE"

"$BIN" tx staking create-validator "$VALIDATOR_FILE" \
  --from "$WALLET" \
  --chain-id "$CHAIN_ID" \
  --gas auto --gas-adjustment 1.5 --fees 15000ulume \
  --yes

Basic host firewall

set -euo pipefail

P2P_PORT="59656"
sudo ufw default allow outgoing
sudo ufw default deny incoming
sudo ufw allow OpenSSH
sudo ufw allow "$P2P_PORT/tcp"
sudo ufw enable
sudo ufw status verbose

# RPC/API/gRPC remain private unless you deliberately add authenticated
# reverse-proxy rules. Never expose validator signing services publicly.
Advanced and recovery operations

Reversible decommission

This procedure stops and disables the service but retains the node home, binary and service file. Verify the backup before any later deletion.

set -euo pipefail

SERVICE="lumerad"
NODE_HOME="$HOME/.lumera"
STAMP=$(date -u +%Y%m%dT%H%M%SZ)
RETIRED_HOME="$NODE_HOME.retired.$STAMP"
KEY_BACKUP="$HOME/lumerad-validator-keys-$STAMP.tar.gz"
NODE_STOPPED=0

rollback() {
  trap - ERR INT TERM
  if [ "$NODE_STOPPED" -eq 1 ]; then sudo systemctl start "$SERVICE" || true; fi
  exit 1
}
trap rollback ERR INT TERM

test -d "$NODE_HOME"
test ! -e "$RETIRED_HOME"
test -s "$NODE_HOME/config/priv_validator_key.json"
test -s "$NODE_HOME/data/priv_validator_state.json"
sudo systemctl stop "$SERVICE"
NODE_STOPPED=1

# Back up validator identity/state before moving anything.
tar -czf "$KEY_BACKUP" -C "$NODE_HOME" \
  config/priv_validator_key.json config/node_key.json data/priv_validator_state.json
test -s "$KEY_BACKUP"
mv "$NODE_HOME" "$RETIRED_HOME"
sudo systemctl disable "$SERVICE"
NODE_STOPPED=0
trap - ERR INT TERM

echo "Node home was retained at: $RETIRED_HOME"
echo "Critical key backup: $KEY_BACKUP"
echo "No binary, service file or node data was deleted."