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

# Installation

## **Node Requirements**

**CPU:**

* 16 cores, **4.5+ GHz base clock**\
  (e.g., AMD Ryzen 9950X / 7950X, EPYC 4584PX)

**RAM:**

* **32 GB+**

**Storage:**

* **2 TB NVMe (PCIe Gen4x4)** — TrieDB
* **500 GB NVMe (PCIe Gen4x4)** — MonadBFT + OS

**Bandwidth:**

* **Validators:** 300 Mbit/s
* **Full Nodes:** 100 Mbit/s

**SSD Reliability Notes:**

* **Recommended:** Samsung 980/990 Pro, Samsung PM9A1
* **Acceptable:** Micron 7450 (occasional slowdowns)
* **Avoid:** Nextorage SSDs — can freeze under load

***

1. ### **Update packages and install useful tools.**

```bash
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 --yes
```

***

2. ### **Install Monad Debian Package.**

```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
```

***

3. ### **Install the `monad` package.**

```bash
apt install -y monad=0.14.5
apt-mark hold monad
```

***

4. ### **Create the `monad` User and Directories.**

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

***

5. ### **Configure TrieDB NVMe Device**

   > Use a **dedicated NVMe** with no filesystem / RAID on it.

```bash
TRIEDB_DRIVE=/dev/nvme1n1   # CHANGE THIS TO YOUR DEVICE
```

Create GPT and a full-disk partition:

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

Create a udev rule and `/dev/triedb` symlink:

```bash
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/triedb
```

***

6. ### **Ensure 512-byte LBA.**

Check active LBA format:

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

Expected: `Data Size: 512 bytes ... (in use)`.\
If not, switch to 512-byte LBA:

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

***

7. ### **Initialize TrieDB (monad-mpt).**

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

You should see output showing the MPT database on `/dev/nvme…` and `monad-mpt.service: Deactivated successfully.`

***

8. ### **Firewall Configuration.**

Open SSH and P2P port **8000** (TCP+UDP):

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

Recommended extra protection against spammy small UDP packets:

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

Remember: iptables rules are lost after reboot unless you persist them (e.g. `iptables-persistent`).

To make the mitigation rule permanent under UFW, modify **before.rules**.

1. Open the UFW pre-rules file:

```bash
nano /etc/ufw/before.rules
```

2. Just **below this line**:

```
# End required lines
```

add:

```
# Drop tiny UDP packets to mitigate raptorcast flooding
-A ufw-before-input -p udp --dport 8000 -m length --length 0:1400 -j DROP
```

3. Reload UFW:

```bash
ufw reload
```

4. Verify that the rule is active:

```bash
sudo iptables -S ufw-before-input | grep 8000
```

**Expected output:**

```
-A ufw-before-input -p udp -m udp --dport 8000 -m length --length 0:1400 -j DROP
```

This ensures the anti-spam rule survives reboots and operates consistently.

***

9. ### **Install and Configure OTEL Collector (optional but recommended).**

```bash
OTEL_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 otelcolcat
```

Monad metrics will be exposed at `http://0.0.0.0:8889/metrics`.

***

10. ### **Fetch Monad Config Files.**

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

***

11. ### **Set Keystore Password.**

Generate a strong random password and store it in `.env`:

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

***

12. ### **Generate Keystores (SECP + BLS).**

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

Back up off-node:

* `/opt/monad/backup/secp-backup`
* `/opt/monad/backup/bls-backup` [docs.monad.xyz](https://docs.monad.xyz/node-ops/full-node-installation)

***

13. ### **Update `node.toml` for a Public Full Node.**

Edit:

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

Key fields:

```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 nodes
```

***

14. ### Node Signature Record (Peer Discovery).

Sign your node’s name record with the SECP key:

```bash
source /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 0
```

The command prints `self_address`, `self_record_seq_num` and `self_name_record_sig`.\
Put them into the `peer_discovery` section of `node.toml`, for example:

```toml
self_address = "12.34.56.78:8000"
self_record_seq_num = 0
self_name_record_sig = "<LONG_HEX_SIGNATURE>"
```

15. ### Enable Remote Config Fetching (recommended).

In `/home/monad/.env` you can let the node auto-fetch the latest `validators.toml` and `forkpoint.toml` on startup:

```bash
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'
EOF
```

***

16. ### **Optional: Enable Call Traces (Archive / RPC Nodes).**

For archive / RPC workloads add `--trace_calls` to the `monad-execution` service:

```bash
systemctl edit monad-execution
```

Drop-in override:

```ini
[Service]
Type=simple
ExecStart=
ExecStart=/usr/local/bin/monad \
    ... \
    --trace_calls \
    ...
```

***

17. ### **Optional: Tune Monad Cruft Retention.**

To control how long artifacts are kept (minutes), append to `/home/monad/.env`:

```bash
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
EOF
```

`monad-cruft` runs hourly and will pick up changes automatically.

***

18. ### Start the Full Node.

Fix ownership:

```bash
chown -R monad:monad /home/monad/
```

Enable services at boot:

```bash
systemctl enable monad-bft monad-execution monad-rpc
```

Before first start, follow the [**Hard Reset Guide**](https://services.stakeangle.com/mainnets/monad/hard-reset-guide) to seed the node with a recent database.

Start services:

```bash
systemctl start monad-bft monad-execution monad-rpc
```

Check status and logs:

```bash
systemctl status monad-bft monad-execution monad-rpc
journalctl -u monad-bft -fo cat
journalctl -u monad-execution -fo cat
```
