# ⚙️  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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://services.validexis.com/testnets/monad/installation.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
