⚒️Installation
Quick start guide for installing and configuring a production-ready Monad full node on bare-metal servers.
Node Requirements
Last updated
Quick start guide for installing and configuring a production-ready Monad full node on bare-metal servers.
Last updated
sudo apt update && sudo apt upgrade --yes && \
sudo apt install git build-essential ufw curl jq snapd screen ncdu nano fuse ufw curl nvme-cli aria2 jq --yescat <<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.gpgapt install -y monad=0.12.4
apt-mark hold monaduseradd -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/validatorsTRIEDB_DRIVE=/dev/nvme1n1 # CHANGE THIS TO YOUR DEVICEparted $TRIEDB_DRIVE mklabel gpt
parted $TRIEDB_DRIVE mkpart triedb 0% 100%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/triedbnvme id-ns -H $TRIEDB_DRIVE | grep 'LBA Format' | grep 'in use'nvme format --lbaf=0 $TRIEDB_DRIVE
nvme id-ns -H $TRIEDB_DRIVE | grep 'LBA Format' | grep 'in use'systemctl start monad-mpt
journalctl -u monad-mpt -n 14 -o catufw allow ssh
ufw allow 8000
ufw enable
ufw statusiptables -I INPUT -p udp --dport 8000 -m length --length 0:1400 -j DROPnano /etc/ufw/before.rules# End required lines# Drop tiny UDP packets to mitigate raptorcast flooding
-A ufw-before-input -p udp --dport 8000 -m length --length 0:1400 -j DROPufw reloadsudo iptables -S ufw-before-input | grep 8000-A ufw-before-input -p udp -m udp --dport 8000 -m length --length 0:1400 -j DROPOTEL_VERSION="0.139.0"
OTEL_PACKAGE="https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v${OTEL_VERSION}/otelcol_${OTEL_VERSION}_linux_amd64.deb"
curl -fsSL "$OTEL_PACKAGE" -o /tmp/otelcol_linux_amd64.deb
dpkg -i /tmp/otelcol_linux_amd64.deb
cp /opt/monad/scripts/otel-config.yaml /etc/otelcol/config.yaml
systemctl restart otelcolcatMF_BUCKET=https://bucket.monadinfra.com
curl -o /home/monad/.env \
$MF_BUCKET/config/mainnet/latest/.env.example
curl -o /home/monad/monad-bft/config/node.toml \
$MF_BUCKET/config/mainnet/latest/full-node-node.tomlsed -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-backupbash <<'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"
EOFnano /home/monad/monad-bft/config/node.toml# Block rewards recipient (burn address for public full node)
beneficiary = "0x0000000000000000000000000000000000000000"
# Unique name for your node / provider
node_name = "full_<PROVIDER>-<SUFFIX>"
[fullnode_raptorcast]
enable_client = true
[statesync]
expand_to_group = true
[blocksync_override]
# keep peers empty for public full nodessource /home/monad/.env
monad-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 0self_address = "12.34.56.78:8000"
self_record_seq_num = 0
self_name_record_sig = "<LONG_HEX_SIGNATURE>"cat >> /home/monad/.env << 'EOF'
REMOTE_VALIDATORS_URL='https://bucket.monadinfra.com/validators/mainnet/validators.toml'
REMOTE_FORKPOINT_URL='https://bucket.monadinfra.com/forkpoint/mainnet/forkpoint.toml'
EOFsystemctl edit monad-execution[Service]
Type=simple
ExecStart=
ExecStart=/usr/local/bin/monad \
... \
--trace_calls \
...cat >> /home/monad/.env << 'EOF'
RETENTION_LEDGER=600 # default 10h
RETENTION_WAL=300 # default 5h
RETENTION_FORKPOINT=300 # default 5h
RETENTION_VALIDATORS=43200 # default 30d
EOFchown -R monad:monad /home/monad/systemctl enable monad-bft monad-execution monad-rpcsystemctl start monad-bft monad-execution monad-rpcsystemctl status monad-bft monad-execution monad-rpc
journalctl -u monad-bft -fo cat
journalctl -u monad-execution -fo cat