> For the complete documentation index, see [llms.txt](https://service.block-pro.net/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://service.block-pro.net/mainnet/monad/full-node.md).

# Full Node

Install a Monad Mainnet full node on a dedicated server.

**Mainnet · Chain ID `143` · Ubuntu 24.04 / bare metal**

Use a fresh machine meeting the [requirements](/mainnet/monad.md). These commands run in a root shell. Set up SSH access and identify your dedicated TrieDB disk before continuing.

## Prepare the machine

### Open a root shell

Use administrator access for package, service and raw-device setup.

```bash
sudo -i
```

### Refresh packages

Refresh the package index.

```bash
apt update
```

### Install utilities

Install disk, download and configuration tools.

```bash
apt install -y ca-certificates curl gnupg nvme-cli parted aria2 jq openssl nano ufw
```

### Create the signing-key directory

Prepare APT’s keyring location.

```bash
install -m 0755 -d /etc/apt/keyrings
```

### Import the package key

Trust packages signed by the official Category Labs repository.

```bash
curl -fsSL https://pkg.category.xyz/keys/public-key.asc | gpg --dearmor --yes -o /etc/apt/keyrings/category-labs.gpg
```

### Configure the repository

Add the Ubuntu Noble package source.

```bash
cat > /etc/apt/sources.list.d/category-labs.sources <<EOF
Types: deb
URIs: https://pkg.category.xyz/
Suites: noble
Components: main
Signed-By: /etc/apt/keyrings/category-labs.gpg
EOF
```

### Refresh package versions

Load the available Monad package versions.

```bash
apt update
```

### Inspect available versions

Check that the release announced for Mainnet is available.

```bash
apt-cache madison monad
```

### Install the approved release

0.16.4 is the reviewed reference. Follow the Mainnet deployment announcement when choosing a version.

```bash
apt install monad=0.16.4
```

### Hold the package

Prevent unattended system upgrades from changing the consensus version.

```bash
apt-mark hold monad
```

### Create the service user

Skip only if the monad account already exists.

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

### Create node directories

Prepare configuration, ledger and runtime metadata paths.

```bash
mkdir -p /home/monad/monad-bft/config/forkpoint /home/monad/monad-bft/config/validators /home/monad/monad-bft/ledger
```

## Prepare the TrieDB disk

{% hint style="warning" %}
The following formatting steps erase the selected disk. Choose an unused dedicated NVMe device, with no mounted partitions, swap, LVM or RAID. Never select the OS disk. These steps are for initial setup, not upgrades.
{% endhint %}

### List NVMe devices

Identify the model and capacity of the dedicated disk.

```bash
nvme list
```

### Inspect mounts and filesystems

Check every partition and confirm the target disk is unused.

```bash
lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,MOUNTPOINTS,MODEL
```

### Choose the disk

Enter its exact path after inspection; no disk is selected by default.

```bash
read -rp "Unused dedicated TrieDB disk path: " TRIEDB_DRIVE
```

### Inspect LBA formats

Identify a 512-byte LBA format and its index.

```bash
nvme id-ns -H "$TRIEDB_DRIVE"
```

If 512-byte sectors are not active, select the index shown for the 512-byte format. Formatting destroys this disk’s contents.

### Select the format index — only if needed

Use the index reported by your device. Do not assume index 0 on every disk.

```bash
read -rp "512-byte LBA format index: " LBA_INDEX
```

### Apply 512-byte format — only if needed

Format the verified unused device.

```bash
nvme format --lbaf="$LBA_INDEX" "$TRIEDB_DRIVE"
```

### Create the partition table

Create a fresh GPT on the selected empty disk.

```bash
parted "$TRIEDB_DRIVE" mklabel gpt
```

### Create the TrieDB partition

Allocate the disk to one raw database partition.

```bash
parted "$TRIEDB_DRIVE" mkpart triedb 0% 100%
```

### Read the partition UUID

Use the only partition created above.

```bash
PARTUUID=$(lsblk -nr -o PARTUUID "$TRIEDB_DRIVE" | awk 'NF {print; exit}')
```

### Create a stable device name

Allow only root and the monad group to access the raw partition.

```bash
printf 'ENV{ID_PART_ENTRY_UUID}=="%s", GROUP="monad", MODE="0660", SYMLINK+="triedb"\n' "$PARTUUID" > /etc/udev/rules.d/99-triedb.rules
```

### Reload device rules

Load the new rule.

```bash
udevadm control --reload-rules
```

### Apply the rule

Apply permissions and the triedb symlink.

```bash
udevadm trigger
```

### Wait for device setup

Wait for udev to finish.

```bash
udevadm settle
```

### Verify the target

Confirm /dev/triedb points to the dedicated partition.

```bash
ls -l /dev/triedb
```

### Initialize TrieDB

Run the package’s one-time database initializer.

```bash
systemctl start monad-mpt
```

### Check initialization

Confirm the initializer completed successfully.

```bash
journalctl -u monad-mpt -n 30 --no-pager -o cat
```

## Configure Mainnet

### Download the environment template

Use the mainnet configuration supplied by Monad Foundation.

```bash
curl -fL https://bucket.monadinfra.com/config/mainnet/latest/.env.example -o /home/monad/.env
```

### Download the full-node template

Use the public full-node network configuration.

```bash
curl -fL https://bucket.monadinfra.com/config/mainnet/latest/full-node-node.toml -o /home/monad/monad-bft/config/node.toml
```

### Protect the environment

Restrict the file before adding the keystore password.

```bash
chmod 0600 /home/monad/.env
```

### Set the password

On this fresh setup, generate the password when the template contains an empty KEYSTORE\_PASSWORD entry.

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

### Load service variables

Load the template after reviewing it.

```bash
source /home/monad/.env
```

### Confirm a password is set

Continue only if this command succeeds.

```bash
test -n "$KEYSTORE_PASSWORD"
```

### Prepare private backups

Create a restricted backup location.

```bash
install -m 0700 -d /opt/monad/backup
```

### Restrict new private files

Keep newly generated backup files private.

```bash
umask 077
```

### Create the SECP identity

Run only on a new node with no existing id-secp file.

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

Run only on a new node with no existing id-bls file.

```bash
monad-keystore create --key-type bls --keystore-path /home/monad/monad-bft/config/id-bls --password "$KEYSTORE_PASSWORD" > /opt/monad/backup/bls-backup
```

Securely back up both keystores, their password and the recovery files outside the node. Do not post recovery-file contents.

### Edit node settings

Set a unique node\_name. For a full node, beneficiary may be the zero address. Keep enable\_client=true under fullnode\_raptorcast, expand\_to\_group=true under statesync, and the public full-node blocksync\_override list empty.

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

### Inspect name-record options

Use the flags supported by the installed release.

```bash
monad-sign-name-record --help
```

### Set the public IP

Enter the routable address assigned to this server.

```bash
read -rp "Node public IPv4: " NODE_PUBLIC_IP
```

### Sign the discovery record

This is the Mainnet command from the official installation guide. Match its port flags to your approved release.

```bash
monad-sign-name-record --ip "$NODE_PUBLIC_IP" --tcp-port 8000 --udp-port 8000 \
  --authenticated-udp-port 8001 --keystore-path /home/monad/monad-bft/config/id-secp \
  --password "$KEYSTORE_PASSWORD" --self-record-seq-num 1
```

Copy the generated signature and sequence number into `[peer_discovery]` in `node.toml`. Use the field layout of the downloaded release template: older Mainnet templates combine IP and TCP port in `self_address`; newer templates expose separate address and port fields. Do not paste an example signature. If your version replaces `--udp-port` with `--direct-udp-port`, follow the installed help and matching release instructions.

### Check remote metadata URLs

Confirm both variables use /mainnet/, then save any missing values in .env.

```bash
nano /home/monad/.env
```

```ini
REMOTE_VALIDATORS_URL='https://bucket.monadinfra.com/validators/mainnet/validators.toml'
REMOTE_FORKPOINT_URL='https://bucket.monadinfra.com/forkpoint/mainnet/forkpoint.toml'
```

## Network access

Preserve your actual SSH port before enabling a firewall. Allow the consensus ports required by the selected template; keep RPC 8080 and metrics private.

### Allow SSH

Use this on hosts using standard SSH port 22; adapt it before proceeding if yours differs.

```bash
ufw allow 22/tcp
```

### Allow consensus traffic

Permit the ports used by the official node template.

```bash
ufw allow 8000:8002
```

### Enable the firewall

Review the rules and apply the firewall.

```bash
ufw enable
```

The upstream guide also recommends a UDP anti-spam rule on port 8000. Confirm your release’s networking requirements and persist the rule with your firewall tooling. See the [official networking section](https://docs.monad.xyz/node-ops/full-node-installation#configure-firewall-rules).

## Bootstrap and start

### Set file ownership

Give the service account access to its configuration and data.

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

### Enable services

Enable startup on boot.

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

Follow [Snapshots](/mainnet/monad/snapshots.md) to import current Mainnet state before starting. The recovery procedure handles the active TrieDB storage format.

### Start the node

Start consensus, execution and RPC after restoration.

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

### Check services

All three should be active.

```bash
systemctl status monad-bft monad-execution monad-rpc --no-pager -l
```

### Verify Mainnet

After statesync, RPC should return 0x8f (143).

```bash
curl -fsS http://127.0.0.1:8080/ -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' | jq
```

### Verify progress

Repeat to confirm the block number advances.

```bash
curl -fsS http://127.0.0.1:8080/ -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' | jq
```

### Check cleanup

The package’s cleanup timer prevents old artifacts from accumulating.

```bash
systemctl status monad-cruft.timer --no-pager
```

Configure release-appropriate monitoring using the [metrics migration guide](https://docs.monad.xyz/node-ops/upgrade-instructions/prometheus-metrics-migration).

[Installation source](https://docs.monad.xyz/node-ops/full-node-installation).
