Installation
A comprehensive guide to running a Monad Full Node and Validator on Testnet.
đĸ Full Node Installation
đĸ Components
Monad nodes are running services using systemd:
monad-bft- Consensus clientmonad-execution- Execution clientmonad-rpc- RPC servermonad-mpt- One-time execution for initializing the TrieDB diskmonad-cruft- Cleanup service running hourlyotelcol- OTEL collector service for metric collection
The systemd services are running using monad service user.
The configuration and data structure is:
/home/monad/.env- contains the environment variables to configure monad services/home/monad/monad-bft/config/node.toml- contains configurable consensus parameters/home/monad/monad-bft/config/forkpoint/- contains the forkpoint/home/monad/monad-bft/config/validators/- contains validator sets/home/monad/monad-bft/ledger/- contains consensus block headers and bodies/dev/triedb- TrieDB database device
đģ Prerequisites
- Bare-metal server
- Ubuntu 24.04+ Operating system
- Linux Kernel >= 6.8.0.60 and not 6.8.0.136
- Disabled HyperThreading (HT) or Simultaneous MultiThreading (SMT) via BIOS settings.
đĸ Prepare the Node
âšī¸ Info The following instructions assume the commands are executed as
rootuser.
đĸ Check Linux kernel
Get the current active Linux kernel version:
uname -râ ī¸ Warning There is a known bug affecting Linux kernel versions
v6.8.0.56-generictov6.8.0.59-generic(inclusive) that causes Monad clients to hang. We recommendv6.8.0.60-genericor higher. There is also a known bug affectingv6.8.0.136-genericthat causes Monad clients to fail to start. We recommendv6.8.0.134-genericif not using 6.8.0.60+.
Ignore unsupported Linux kernel versions, to prevent auto-upgrade:
cat <<'EOF' > /etc/apt/preferences.d/block-kernel-6.8.0-136
Package: linux-image-6.8.0-136-generic linux-headers-6.8.0-136-generic linux-modules-6.8.0-136-generic linux-modules-extra-6.8.0-136-generic
Pin: release *
Pin-Priority: -1
EOFđĸ Update the system
apt update
apt upgrade -yReboot the machine if required. Install additional dependencies:
apt install -y curl nvme-cli aria2 jqđĸ Install monad package
Configure the APT repository:
cat <<EOF > /etc/apt/sources.list.d/category-labs.sources
Types: deb
URIs: https://pkg.category.xyz/
Suites: noble
Components: main
Signed-By: /etc/apt/keyrings/category-labs.gpg
EOF
curl -fsSL https://pkg.category.xyz/keys/public-key.asc \
| gpg --dearmor --yes -o /etc/apt/keyrings/category-labs.gpgInstall monad package for Testnet (v0.15.2):
apt update
apt install -y monad=0.15.2
apt-mark hold monadđĸ Create monad user
useradd -m -s /bin/bash monad
mkdir -p /home/monad/monad-bft/config \
/home/monad/monad-bft/ledger \
/home/monad/monad-bft/config/forkpoint \
/home/monad/monad-bft/config/validatorsđĸ Configure TrieDB Device
Set the NVMe drive (e.g. /dev/nvme1n1) to be used for TrieDB device, create a new partition table and a partition that spans the entire drive.
â ī¸ IMPORTANT: Identify drives carefully Formatting the wrong drive will destroy your operating system! Before proceeding, verify which drive to use:
nvme list lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,MODEL
TRIEDB_DRIVE=/dev/nvme1n1 # CHANGE THIS TO YOUR NVME DRIVE
parted $TRIEDB_DRIVE mklabel gpt
parted $TRIEDB_DRIVE mkpart triedb 0% 100%Create a udev rule to set permissions and create a symlink for the partition:
PARTUUID=$(lsblk -o PARTUUID $TRIEDB_DRIVE | tail -n 1)
echo "Disk PartUUID: ${PARTUUID}"
echo "ENV{ID_PART_ENTRY_UUID}==\"$PARTUUID\", MODE=\"0666\", SYMLINK+=\"triedb\"" \
| tee /etc/udev/rules.d/99-triedb.rules
udevadm trigger
udevadm control --reload
udevadm settle
ls -l /dev/triedbVerify LBA Configuration, and enable 512 byte LBA if not enabled:
nvme id-ns -H $TRIEDB_DRIVE | grep 'LBA Format' | grep 'in use'(Data Size should be set to 512 bytes. If not, use nvme format --lbaf=0 $TRIEDB_DRIVE)
Format the partition by executing the monad-mpt service:
systemctl start monad-mpt
journalctl -u monad-mpt -n 14 -o catđĸ Configure Firewall rules
ufw allow ssh
ufw allow 8000
ufw allow 8001
ufw enable
ufw status
sudo iptables -I INPUT -p udp --dport 8000 -m length --length 0:1400 -j DROPđĸ Configure OTEL Collector
OTEL_VERSION="0.139.0"
OTEL_CHECKSUM="1a1576dde7d51fa7094f4963ceaff37c91ac7b9c9593ba735a3a328ec6f8acd9"
OTEL_PACKAGE="https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v${OTEL_VERSION}/otelcol_${OTEL_VERSION}_linux_amd64.deb"
OTEL_DEB="/tmp/otelcol_linux_amd64.deb"
curl -fsSL "$OTEL_PACKAGE" -o "$OTEL_DEB"
if echo "${OTEL_CHECKSUM} ${OTEL_DEB}" | sha256sum --check --quiet; then
dpkg -i "$OTEL_DEB"
cp /opt/monad/scripts/otel-config.yaml /etc/otelcol/config.yaml
systemctl restart otelcol
else
echo "Checksum verification failed â aborting install." >&2
fiđĸ Configure the Node
đĸ Get configuration files
Configuration files for Testnet full nodes:
MF_BUCKET=https://bucket.monadinfra.com
curl -o /home/monad/.env $MF_BUCKET/config/testnet/latest/.env.example
curl -o /home/monad/monad-bft/config/node.toml $MF_BUCKET/config/testnet/latest/full-node-node.toml(For validators, replace full-node-node.toml with node.toml)
đĸ Define Keystore password
sed -i "s|^KEYSTORE_PASSWORD=$|KEYSTORE_PASSWORD='$(openssl rand -base64 32)'|" /home/monad/.env
source /home/monad/.env
mkdir -p /opt/monad/backup/
echo "Keystore password: ${KEYSTORE_PASSWORD}" > /opt/monad/backup/keystore-password-backupđĸ Generate Keystores
bash <<'EOF'
set -e
source /home/monad/.env
if [[ -z "$KEYSTORE_PASSWORD" || \
-f /home/monad/monad-bft/config/id-secp || \
-f /home/monad/monad-bft/config/id-bls ]]; then
echo "Skipping: missing KEYSTORE_PASSWORD or keys already exist."
exit 1
fi
monad-keystore create \
--key-type secp \
--keystore-path /home/monad/monad-bft/config/id-secp \
--password "${KEYSTORE_PASSWORD}" > /opt/monad/backup/secp-backup
monad-keystore create \
--key-type bls \
--keystore-path /home/monad/monad-bft/config/id-bls \
--password "${KEYSTORE_PASSWORD}" > /opt/monad/backup/bls-backup
grep "public key" /opt/monad/backup/secp-backup /opt/monad/backup/bls-backup \
| tee /home/monad/pubkey-secp-bls
echo "Success: New keystores generated"
EOFâ ī¸ External Backup Ensure that
/opt/monad/backup/secp-backupand/opt/monad/backup/bls-backupare stored securely off-server.
đĸ Update node.toml
- Edit
/home/monad/monad-bft/config/node.tomlusing a text editor (e.g.,nano). - Update the
beneficiaryfield (burn address for full node, wallet address for validator). - Update the
node_namefield with your unique provider name. - Ensure
enable_client = trueandexpand_to_group = true.
đĸ Node signature record
Generate the node record signature for Testnet:
source /home/monad/.env
monad-sign-name-record \
--ip $(curl -s4 ifconfig.me) \
--tcp-port 8000 \
--udp-port 8000 \
--authenticated-udp-port 8001 \
--keystore-path /home/monad/monad-bft/config/id-secp \
--password "${KEYSTORE_PASSWORD}" \
--self-record-seq-num 1Update the [peer_discovery] section in node.toml with the generated values. self_address should be "<self_ip>:<self_tcp_port>".
đĸ Remote Configuration Fetching
In /home/monad/.env:
REMOTE_VALIDATORS_URL='https://bucket.monadinfra.com/validators/testnet/validators.toml'
REMOTE_FORKPOINT_URL='https://bucket.monadinfra.com/forkpoint/testnet/forkpoint.toml'đĸ Monad Cruft service
You can configure artifact retention times by setting environment variables in /home/monad/.env (all values in minutes):
RETENTION_LEDGER=600
RETENTION_WAL=300
RETENTION_FORKPOINT=300
RETENTION_VALIDATORS=43200đĸ Start the Node
chown -R monad:monad /home/monad/
systemctl enable monad-bft monad-execution monad-rpc
systemctl start monad-bft monad-execution monad-rpcđĸ Validator Installation
Setting up a validator requires completing the Full Node installation first, syncing, and then registering your node via the staking precompile.
đĸ Requirements
- Minimum self-stake: 100,000 MON
- Total stake (including delegations): 10,000,000 MON
- Must be in the top 200 validators by stake weight.
đĸ Staking CLI
Use staking-sdk-cli to interface with the staking precompile.
Follow the onboarding workflow on GitHub.
đĸ General Operations
đĸ Version Information
monad-node --versionđĸ Service Management
systemctl status monad-bft monad-execution monad-rpc --no-pager -l
journalctl -u monad-bft -fđĸ Monitoring Block Height
curl http://localhost:8080/ \
-X POST \
-H "Content-Type: application/json" \
--data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}'đĸ Viewing MonadDB Disk Usage
monad-mpt --storage /dev/triedbđĸ Log Analysis with monlog
usermod -a -G systemd-journal monad
cd /home/monad
curl -sSL https://pub-b0d0d7272c994851b4c8af22a766f571.r2.dev/scripts/monlog -O
chmod u+x ./monlog
./monlogđĸ Node Status with monad-status
curl -sSL https://bucket.monadinfra.com/scripts/monad-status.sh -o /usr/local/bin/monad-status
chmod +x /usr/local/bin/monad-status
monad-statusđĸ Hard Reset Instructions
If you need to wipe local state and download a fresh snapshot on Testnet:
# 1. Stop services and reset workspace
bash /opt/monad/scripts/reset-workspace.sh
# 2. Download and import snapshot (Monad Foundation)
MF_BUCKET=https://bucket.monadinfra.com
curl -sSL $MF_BUCKET/scripts/testnet/restore-from-snapshot.sh | bash
# 3. Fetch latest forkpoint and validators (if not using Auto Fetch)
VALIDATORS_FILE=/home/monad/monad-bft/config/validators/validators.toml
curl -sSL $MF_BUCKET/scripts/testnet/download-forkpoint.sh | bash
curl $MF_BUCKET/validators/testnet/validators.toml -o $VALIDATORS_FILE
chown monad:monad $VALIDATORS_FILE
# 4. Restart services
systemctl start monad-bft monad-execution monad-rpcđĸ Node Migration
To promote a full node to a validator with minimal downtime:
- Sync a full node to the tip of the network.
- Backup
id-secp,id-bls, andnode.tomlfrom the existing validator and transfer them to the synced full node. - On the full node, generate a new name-record signature:
source /home/monad/.env
monad-sign-name-record --ip $(curl -s4 ifconfig.me) \
--tcp-port 8000 --udp-port 8000 \
--node-config /home/monad/monad-bft/config/node.toml \
--authenticated-udp-port 8001 \
--self-record-seq-num 2 \
--keystore-path /home/monad/monad-bft/config/id-secp \
--password "$KEYSTORE_PASSWORD"- Replace
[peer_discovery]in the full node'snode.tomlwith the output. - Stop services on the original validator:
systemctl stop monad-bft monad-rpc monad-execution- Stop full node services, restart them as a validator:
systemctl stop monad-bft monad-rpc monad-execution
sleep 1
systemctl start monad-bft monad-rpc monad-execution