Full Node Installation
These instructions are for testnet. Mainnet instructions will be made available shortly.
Please refer to the Hardware Requirements before continuing.
Components
monad-bft- Consensus clientmonad-execution- Execution clientmonad-rpc- RPC clientmonad-mpt- One-time execution for initializing the disk for TrieDB making the disk available for it. Not required when upgrading unless explicitly mentioned
Prerequisites
- Ubuntu 24.04+ OS and Kernel >= 6.8 (see warning below)
- Disable hyperthreading or simultaneous multithreading (SMT) via BIOS settings - these can degrade performance
There is a known bug affecting
Linux kernel versions v6.8.0.56-generic - v6.8.0.59-generic (inclusive) that causes Monad
clients to hang in an uninterruptible sleep state, severely impacting node stability.
We recommend v6.8.0.60-generic or higher.
Prepare the Node
Instructions below assume you are running from root user
Provision TrieDB Disk
Set the drive to be used for TrieDB, create a new partition table and a partition that spans the entire drive (the drive should be on a disk that has no filesystem mounted and no RAID configured).
TRIEDB_DRIVE=/dev/nvme1n1 # CHANGE THIS TO YOUR NVME DRIVE
parted $TRIEDB_DRIVE mklabel gptparted $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 "ENV{ID_PART_ENTRY_UUID}==\"$PARTUUID\", MODE=\"0666\", SYMLINK+=\"triedb\"" | tee /etc/udev/rules.d/99-triedb.rulesTrigger and reload udev rules.
udevadm triggerudevadm control --reloadGive permissions to the /dev/triedb drive
chmod a+rwx /dev/triedbCheck LBA Configuration
Enable 512 byte LBA if not enabled.
Install nvme-cli which is used to check for 512 byte LBA.
apt-get install nvme-cliCheck if 512 byte LBA is enabled on TRIEDB_DRIVE.
nvme id-ns -H $TRIEDB_DRIVE | grep 'LBA Format' | grep 'in use'This command should return the following expected output.
LBA Format 0 : Metadata Size: 0 bytes - Data Size: 512 bytes - Relative Performance: 0 Best (in use)Data Size should be set to 512 bytes and marked as (in use). If that is not the case,
then you will need to set the TRIEDB_DRIVE to use 512 byte LBA with the following command.
nvme format --lbaf=0 $TRIEDB_DRIVEVerify that the configuration has been corrected.
nvme id-ns -H $TRIEDB_DRIVE | grep 'LBA Format' | grep 'in use'Create Dedicated User
Create a non-privileged user named monad with a home directory and Bash shell.
useradd -m -s /bin/bash monadCreate config directories in /home/monad.
sudo -u monad bash -c "mkdir -p /home/monad/monad-bft/config \ /home/monad/monad-bft/ledger \ /home/monad/monad-bft/config/forkpoint \ /home/monad/monad-bft/config/validators"/home/monad/monad-bft/config/node.toml- contains configurable consensus parameters, most notably the name of the node, typically"<PROVIDER_NAME>-1"and a list of upstream validators (denoted by secp pubkey and DNS) who have been configured to republish blocks to your full node./home/monad/monad-bft/config/forkpoint/- contains consensus quorum checkpoints (written every block) used for bootstrapping state/home/monad/monad-bft/config/validators- contains validator sets generated at the boundary block. The newly generated file contains the consensus validator sets for the current epoch and for the upcoming epoch. The most recent validator set is found atvalidators.toml./home/monad/monad-bft/ledger- contains consensus (BFT) block headers and bodies, including the transactions
Add APT Repo
cat <<EOF > /etc/apt/sources.list.d/category-labs.sourcesTypes: debURIs: https://pkg.category.xyz/Suites: nobleComponents: mainSigned-By: /etc/apt/keyrings/category-labs.gpgEOFAdd GPG Key
curl -fsSL https://pkg.category.xyz/keys/public-key.asc | gpg --dearmor -o /etc/apt/keyrings/category-labs.gpgInstall Monad
apt updateapt install monad=0.12.0~rcConfigure UFW
Configure UFW to allow SSH inbound connections (remote access) and traffic to port 8000. The Monad consensus client uses both TCP and UDP.
# allow ssh connectionssudo ufw allow ssh# allow p2p portsudo ufw allow 8000# enable ufw# default behavior: block all incoming traffic and enable all outgoingsudo ufw enableIf using hardware firewalls, you may need to perform additional steps to open up port 8000 to UDP and TCP traffic.
Add Configuration Files
# Create backup folder for BLS and SECP keysmkdir -p /opt/monad/backup
# Make monad owner of the backup directorychown -R monad:monad /opt/monad/backupInstructions below assume you are running from monad user
Download a collection of chain related configuration files and scripts
MF_BUCKET=https://bucket.monadinfra.comcurl -o /home/monad/.env $MF_BUCKET/config/testnet/latest/.env.examplecurl -o /home/monad/monad-bft/config/node.toml $MF_BUCKET/config/testnet/latest/full-node-node.tomlUpdate /home/monad/.env
In /home/monad/.env, type a strong password in KEYSTORE_PASSWORD. This password
should be wrapped in single quotes, e.g. KEYSTORE_PASSWORD='str0ngp@ssw0rd' and is used
for key encryption / decryption.
Key Management
Generate encrypted BLS and SECP keys using the keystore binary:
# source the .env file which will load the KEYSTORE_PASSWORDsource /home/monad/.env
# Create the SECP keymonad-keystore create \--key-type secp \--keystore-path /home/monad/monad-bft/config/id-secp \--password "${KEYSTORE_PASSWORD}" > /opt/monad/backup/secp-backup
# Create the BLS keymonad-keystore create \--key-type bls \--keystore-path /home/monad/monad-bft/config/id-bls \--password "${KEYSTORE_PASSWORD}" > /opt/monad/backup/bls-backupKeypairs will be generated and backups stored locally under secp-backup and bls-backup.
Please make sure keys are properly backed up.
For convenience, save pubkeys in /home/monad:
# Create a backup of both keys in /home/monadgrep "public key" /opt/monad/backup/secp-backup /opt/monad/backup/bls-backup > /home/monad/pubkey-secp-blsSign Name Record
After the keypairs are created, a fullnode will need to sign its name record using the SECP key in order to participate in peer discovery.
source /home/monad/.envmonad-sign-name-record \ --address $(curl -s4 ifconfig.me):8000 \ --keystore-path /home/monad/monad-bft/config/id-secp \ --password "${KEYSTORE_PASSWORD}" \ --self-record-seq-num 0This should provide an output in the form as shown below.
self_address = "<IP>:8000"self_record_seq_num = 0self_name_record_sig = "<HEX SIG>"The output will need to be used when filling in the node.toml later.
Launch the Monad Stack
Full nodes can receive block proposals either directly from a validator that whitelists it, or via a raptorcast group. These different configurations are described here.
The instructions displayed here are intended for the public full node configuration, where a full node joins the network by connecting to available upstream validators participating in secondary raptorcast. Joining in this configuration is permissionless.
Update node.toml
Update the node.toml that was downloaded previously (for reference, you can also see it
here):
-
Update the
node_namefield innode.tomlto include your provider name and remove '#' from the variable. Note that if you are provisioning multiple nodes, this name should be unique. We recommend a naming scheme likefull_<PROVIDER>-1,full_<PROVIDER>-2, etc.node_name = # "full_<PROVIDER>-<OPTIONAL_SUFFIX>" -
Update the
peer_discoverysection with the IP addresses and name record signature obtained from Sign Name Record. -
Update the
beneficiaryfield to include the address that should receive block rewards (in the event your node will ever be a staked validator). If that's not the case, this can be set to the burn address"0x0000000000000000000000000000000000000000"or any other valid address. This address should be prefixed by0x. -
Ensure
enable_client = trueunder[fullnode_raptorcast]. -
[blocksync_override]and[statesync]peers can be left empty.
[[bootstrap.peers]]address = "bootstrap-node-ip:8000"secp256k1_pubkey = "<bootstrap node pubkey>"record_seq_num = <name record sequence number>name_record_sig = "<bootstrap node name record signature>"
[blocksync_override]peers = []
[statesync]peers = []
[fullnode_raptorcast]enable_publisher = trueenable_client = trueraptor10_fullnode_redundancy_factor = 3.0max_group_size = 150round_span = 240invite_lookahead = 120max_invite_wait = 10deadline_round_dist = 10init_empty_round_span = 23max_num_group = 3invite_future_dist_min = 1invite_future_dist_max = 600invite_accept_heartbeat_ms = 10000The final directory structure should look similar to this:
/home/monad └── .env └── monad-bft/ └──config ├── id-bls ├── id-secp └── node.toml └──validators └── validators.toml └──forkpoint └── forkpoint.tomlmonad-cruft
Installation of the monad Debian package enables the monad-cruft timer, which runs hourly to
clear old artifacts (/opt/monad/scripts/clear-old-artifacts.sh). This is necessary to prevent
inode exhaustion as artifacts like forkpoint.toml and ledger files accumulate.
Initialize the database
systemctl start set-hugepagessystemctl start monad-mptCheck journalctl to ensure that this worked correctly.
Below is an example of a successful outcome:
$ journalctl -u monad-mpt
# NOTE: output is "trimmed" for easier reading hereMPT database on storages: Capacity Used % Path 1.82 Tb 1.85 Gb 0.10% "/dev/nvme1n1p1"MPT database internal lists: Fast: 7 chunks with capacity 1.75 Gb used 1.60 Gb Slow: 1 chunks with capacity 256.00 Mb used 0.00 bytes Free: 7441 chunks with capacity 1.82 Tb used 0.00 bytesMPT database has 281868 history, earliest is 0 latest is 281867. It has been configured to retain no more than 33554432. Latest voted is (281866, 281868). Latest finalized is 281865, latest verified is 281862, auto expire version >Hard Reset
Follow the Hard Reset instructions, including the commands
to bring the stack up (systemctl start ...).
Execute the CLI --help command via the desired binary. Please note that these should not be
changed arbitrarily as some configurations may result in unexpected behavior or crashes.
monad-rpc --helpInstall & Start OTEL Collector
curl -fsSL -o /tmp/otelcol_0.125.0_linux_amd64.deb https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.125.0/otelcol_0.125.0_linux_amd64.debdpkg -i /tmp/otelcol_0.125.0_linux_amd64.debcp /opt/monad/scripts/otel-config.yaml /etc/otelcol/config.yaml
systemctl start otelcolThe Debian package supports OTEL collector. Through this, you will be able to see all the
relevant Monad-specific metrics (available at 0.0.0.0:8889/metrics).