> For the complete documentation index, see [llms.txt](https://services.validexis.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://services.validexis.com/testnets/monad/installation.md).

# ⚙️  Installation

### *System Preparation*

Run all commands as **root** unless stated otherwise.

#### *Update system*

```bash
apt update && apt upgrade -y
```

Reboot if kernel was updated.

#### *Install dependencies*

```bash
apt install -y curl nvme-cli aria2 jq ufw
```

### *Install Monad Package*

#### *Configure APT repository*

```bash
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.gpg
```

#### *Install Monad*

```shell
apt update
apt install -y monad=0.12.7
apt-mark hold monad
```

### *Create Monad User*

```bash
useradd -m -s /bin/bash monad
```

Create directory structure:

```bash
mkdir -p /home/monad/monad-bft/config/{forkpoint,validators} \
         /home/monad/monad-bft/ledger
```

***

### *Configure TrieDB NVMe Device*

#### *Identify NVMe drive*

```bash
nvme list
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,MODEL
```

⚠ **Ensure the drive has no mounted filesystem**

#### *Partition drive*

```bash
TRIEDB_DRIVE=/dev/nvme1n1   # CHANGE THIS

parted $TRIEDB_DRIVE mklabel gpt
parted $TRIEDB_DRIVE mkpart triedb 0% 100%
```

#### *Create udev rule*

```bash
PARTUUID=$(lsblk -o PARTUUID $TRIEDB_DRIVE | tail -n 1)

echo "ENV{ID_PART_ENTRY_UUID}==\"$PARTUUID\", MODE=\"0666\", SYMLINK+=\"triedb\"" \
  > /etc/udev/rules.d/99-triedb.rules

udevadm trigger
udevadm control --reload
udevadm settle
ls -l /dev/triedb
```

#### *Verify 512-byte LBA*

```bash
nvme id-ns -H $TRIEDB_DRIVE | grep 'LBA Format' | grep 'in use'
```

Expected:

```bash
Data Size: 512 bytes (in use)
```

If not:

```bash
nvme format --lbaf=0 $TRIEDB_DRIVE
```

### &#x20;*Initialize TrieDB (MPT)*

```bash
systemctl start monad-mpt
journalctl -u monad-mpt -n 20 -o cat
```

Service must finish with:

```bash
monad-mpt.service: Deactivated successfully.
```

### *Firewall Configuration*

```bash
ufw allow ssh
ufw allow 8000
ufw enable
ufw status
```

Anti-spam UDP filter:

```bash
iptables -I INPUT -p udp --dport 8000 -m length --length 0:1400 -j DROP
```

Persist rule using `iptables-persistent` if desired.

### *Install OTEL Collector*

```bash
OTEL_VERSION=0.139.0
curl -fsSL https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v${OTEL_VERSION}/otelcol_${OTEL_VERSION}_linux_amd64.deb \
  -o /tmp/otelcol.deb

dpkg -i /tmp/otelcol.deb

cp /opt/monad/scripts/otel-config.yaml /etc/otelcol/config.yaml
systemctl restart otelcol
```

Metrics endpoint:

```bash
http://<NODE_IP>:8889/metrics
```

### *Download Configuration Files*

#### *Testnet example*

```bash
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/node.toml
```

### *Keystore & Keys*

#### *Generate password*

```bash
sed -i "s|^KEYSTORE_PASSWORD=$|KEYSTORE_PASSWORD='$(openssl rand -base64 32)'|" /home/monad/.env
source /home/monad/.env
```

Backup password:

```bash
mkdir -p /opt/monad/backup
echo "$KEYSTORE_PASSWORD" > /opt/monad/backup/keystore-password-backup
```

#### *Generate keys*

```shell
monad-keystore create --key-type secp \
  --keystore-path /home/monad/monad-bft/config/id-secp \
  --password "$KEYSTORE_PASSWORD"

monad-keystore create --key-type bls \
  --keystore-path /home/monad/monad-bft/config/id-bls \
  --password "$KEYSTORE_PASSWORD"
```

⚠ Store backups **off-server**

***

### *Hard Reset (Snapshot Restore)*

```bash
systemctl stop monad-bft monad-execution monad-rpc
bash /opt/monad/scripts/reset-workspace.sh
```

Restore snapshot:

```bash
MF_BUCKET=https://bucket.monadinfra.com
curl -sSL $MF_BUCKET/scripts/testnet/restore-from-snapshot.sh | bash
```

***

### *Validator Configuration*

Edit:

```bash
/home/monad/monad-bft/config/node.toml
```

#### *Mandatory changes*

```bash
beneficiary = "0x<VALIDATOR_REWARD_ADDRESS>"
node_name = "validator_<PROVIDER>"
```

Ensure validator-specific template is used.

Reload config without restart:

```bash
monad-debug-node --control-panel-ipc-path \
  /home/monad/monad-bft/controlpanel.sock reload-config
```

### *Start Services*

```bash
chown -R monad:monad /home/monad
systemctl enable monad-bft monad-execution monad-rpc
systemctl start monad-bft monad-execution monad-rpc
```

### &#x20;*Final Notes*

* Monitor logs: `journalctl -u monad-bft -f`
* Monitor metrics via OTEL
* Backup keys regularly
* Never expose keystore password
