> For the complete documentation index, see [llms.txt](https://docs.gravity.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.gravity.xyz/gravity-networks/run-a-mainnet-pfn.md).

# Run a Gravity Mainnet Public Full Node

This guide walks you through running a Public Full Node (PFN) on Gravity Mainnet (L1, Chain ID `127001`). A PFN syncs from upstream public or VFN seeds and can serve EVM JSON-RPC for your applications, indexers, or internal services.

A PFN does **not** participate in consensus, does **not** hold validator stake, and does **not** require an invite. If you only need occasional reads or writes, you can use the public endpoint at <https://mainnet-rpc.gravity.xyz> instead.

> **Placeholders.** Commands below use `<YOUR_...>` placeholders. Replace each value before running. Never commit private keys, generated identities, or node databases to version control.

## Network Role

Gravity L1 separates node traffic into validator, VFN, and public networks. A PFN uses the public network and syncs outward from upstream infrastructure:

```
validators --VFN net--> VFN --public net--> PFN / RPC --> app or indexer
```

Use a PFN when you want:

* a private or high-throughput JSON-RPC endpoint;
* a node close to your backend or indexer;
* independent chain data for analytics or monitoring;
* no validator-set or governance operations.

## Prerequisites

1. **A Linux x86-64 host.** Production nodes are built and run on Ubuntu 24.04 LTS. Build on the same OS family that you deploy to.
2. **`gravity_node` and `gravity_cli`.** Build from source:

   ```bash
   git clone https://github.com/Galxe/gravity-sdk.git
   cd gravity-sdk
   RUSTFLAGS="--cfg tokio_unstable" \
     cargo build --profile quick-release -p gravity_node -p gravity_cli
   ```

   The binaries land in `target/quick-release/`. You can also download a release package from the [gravity-sdk releases](https://github.com/Galxe/gravity-sdk/releases). For `gravity_cli` command usage, see the [Gravity CLI Skill](https://github.com/Galxe/gravity-sdk/blob/main/.agents/skills/gravity-cli/SKILL.md).
3. **Mainnet `genesis.json` and `waypoint.txt`.** These artifacts pin the exact chain you are joining. Download the canonical files from [`gravity-sdk/genesis/mainnet`](https://github.com/Galxe/gravity-sdk/tree/main/genesis/mainnet); do not regenerate genesis locally for mainnet.
4. **An upstream public-network seed.** For the seed you need its `peer_id`, host, public port, and `network_public_key`. The current public PFN seed is listed below.
5. **SSD/NVMe storage.** Chain data grows with uptime. Budget well above the current chain size.

## 1. Prepare Genesis Artifacts

Place the canonical mainnet artifacts where your cluster config references them:

```bash
mkdir -p <YOUR_GRAVITY_SDK_PATH>/cluster/output
curl -fsSL https://raw.githubusercontent.com/Galxe/gravity-sdk/main/genesis/mainnet/genesis.json \
  -o <YOUR_GRAVITY_SDK_PATH>/cluster/output/genesis.json
curl -fsSL https://raw.githubusercontent.com/Galxe/gravity-sdk/main/genesis/mainnet/waypoint.txt \
  -o <YOUR_GRAVITY_SDK_PATH>/cluster/output/waypoint.txt
```

```
<YOUR_GRAVITY_SDK_PATH>/cluster/output/genesis.json
<YOUR_GRAVITY_SDK_PATH>/cluster/output/waypoint.txt
```

If the artifacts live elsewhere, you can point to absolute paths in `genesis_source`:

```toml
[genesis_source]
genesis_path = "<YOUR_MAINNET_ARTIFACT_DIR>/genesis.json"
waypoint_path = "<YOUR_MAINNET_ARTIFACT_DIR>/waypoint.txt"
```

The `genesis.json` and `waypoint.txt` must match each other. A wrong pair can start a node on a different chain identity and prevent it from syncing mainnet.

## 2. Gather Seed Information

### Current Mainnet PFN Connectivity

At the moment, public internet access is intended for the mainnet RPC/PFN fleet. Other validator and VFN hosts should be treated as private/internal unless the Gravity team explicitly shares invite-only seed information.

| Node    | Host                            | JSON-RPC | Public-network seed port | Peer ID                                                            | Network public key                                                 |
| ------- | ------------------------------- | -------- | ------------------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------ |
| `rpc-1` | `mainnet-rpc-p2p-1.gravity.xyz` | `8545`   | `6180`                   | `38013b46c21388c3fd08ab32b86b478b3109125566d63c2da8fdc941dc474077` | `234aee14677a3d2198208ea72ca5e95ed75520df27f01f0c36220303ff78642f` |

For normal JSON-RPC traffic, prefer the aggregate endpoint:

```
https://mainnet-rpc.gravity.xyz
```

Before using a PFN host as an upstream seed, verify from your node host that the public-network port is reachable:

```bash
nc -vz mainnet-rpc-p2p-1.gravity.xyz 6180
```

Validator and VFN joining is currently invite-only for Gravity-operated mainnet seeds. A PFN does not need validator access, but it still needs a reachable public-network upstream seed plus that seed's public identity values.

Use the current public PFN seed directly in `local-pfn.toml`.

```toml
seeds = [
  { peer_id = "38013b46c21388c3fd08ab32b86b478b3109125566d63c2da8fdc941dc474077", role = "PreferredUpstream", address = "/dns/mainnet-rpc-p2p-1.gravity.xyz/tcp/6180/noise-ik/234aee14677a3d2198208ea72ca5e95ed75520df27f01f0c36220303ff78642f/handshake/0" },
]
```

For an explicit PFN seed, fill these values from the upstream node's public identity:

| Value                                | Meaning                                                 |
| ------------------------------------ | ------------------------------------------------------- |
| `<YOUR_UPSTREAM_PEER_ID>`            | Upstream sidecar `account_address`.                     |
| `<YOUR_UPSTREAM_HOST>`               | DNS name or IP address of the upstream public listener. |
| `<YOUR_UPSTREAM_PUBLIC_PORT>`        | Public-network port exposed by the upstream node.       |
| `<YOUR_UPSTREAM_NETWORK_PUBLIC_KEY>` | Upstream sidecar `network_public_key`.                  |

Prefer explicit seed entries over `seeds = [{ from = "<id>" }]` for a standalone PFN config. The `{ from = ... }` form only works when the seed node is also defined in the same TOML file.

## 3. Write `local-pfn.toml`

Create a config such as:

```toml
[cluster]
name = "<YOUR_PFN_CLUSTER_NAME>"
base_dir = "<YOUR_DEPLOY_BASE_DIR>"

[genesis_source]
genesis_path = "./output/genesis.json"
waypoint_path = "./output/waypoint.txt"

[[nodes]]
id = "local-pfn"
role = "pfn"
source = { bin_path = "../target/quick-release/gravity_node" }
identity = { source = "file" }

# Deploy directory, not the database directory.
# deploy.sh renders storage to <YOUR_DEPLOY_BASE_DIR>/local-pfn/data
# and reth to <YOUR_DEPLOY_BASE_DIR>/local-pfn/data/reth.
data_dir = "<YOUR_DEPLOY_BASE_DIR>/local-pfn"

host = "127.0.0.1"
# Example local public-network port for this PFN. If other nodes should sync
# from this node, expose and publish the chosen public listener; PFN/VFN public
# listeners commonly use 6195.
public_port = 6180
rpc_port = 8545
metrics_port = 9001
inspection_port = 10001
authrpc_port = 8661
reth_p2p_port = 12024
txpool_max_account_slots = 64
prune_transactionlookup_distance = 10064

seeds = [
  { peer_id = "38013b46c21388c3fd08ab32b86b478b3109125566d63c2da8fdc941dc474077", role = "PreferredUpstream", address = "/dns/mainnet-rpc-p2p-1.gravity.xyz/tcp/6180/noise-ik/234aee14677a3d2198208ea72ca5e95ed75520df27f01f0c36220303ff78642f/handshake/0" },
]
```

Port choices can be changed if you run multiple nodes on one host. Keep the RPC port private unless you intentionally expose it behind your own gateway, authentication, rate limiting, and monitoring.

`prune_transactionlookup_distance = 10064` starts reth with `--full --prune.transactionlookup.distance 10064`. This keeps recent transaction-hash lookup data while allowing older transaction lookup indexes to be pruned. Use a value **greater than or equal to `10064`** for production PFN/RPC nodes. Treat smaller values, such as short test-only distances, as invalid for mainnet PFN configs and reject them during config review.

Use transaction-lookup pruning when you want lower execution database growth and do not need arbitrary old `eth_getTransactionByHash` lookups from this node.

If you omit `prune_transactionlookup_distance`, the generated PFN reth config uses archive-style storage for transaction lookups and keeps historical lookup indexes instead of pruning them. That is simpler for indexers or debugging flows that need old transaction hashes, but it uses more disk over time.

Do not set `prune_transactionlookup_distance = 0` to disable pruning. A distance of `0` is still a pruning mode and can prune transaction lookup data aggressively. To run without transaction-lookup pruning, leave the field out.

The example above generates and stores identity files locally. In production, you can also load the node identity from a secret manager. For example:

```toml
identity = {
  source = "gcp_secret",
  secret = "projects/<YOUR_PROJECT>/secrets/<YOUR_PFN_IDENTITY_SECRET>/versions/1",
}
```

Pin a fixed secret version instead of `latest` when you want reproducible deployments and safer rollbacks.

## 4. Generate Identity and Deploy

From the SDK cluster directory:

```bash
cd <YOUR_GRAVITY_SDK_PATH>/cluster
just init local-pfn.toml
just deploy local-pfn.toml
```

Expected layout:

```
<YOUR_DEPLOY_BASE_DIR>/local-pfn/config/
<YOUR_DEPLOY_BASE_DIR>/local-pfn/data/
<YOUR_DEPLOY_BASE_DIR>/local-pfn/script/start.sh
<YOUR_DEPLOY_BASE_DIR>/local-pfn/script/stop.sh
```

If the package is generated on a build host, copy it to the runtime host while preserving the node directory layout.

## 5. Optional: Warm Start From a Snapshot or Existing Node

A new PFN can sync from genesis, but that may take a long time on an established chain.

### Option A: Download the Public Mainnet Data Snapshot

Gravity publishes daily mainnet PFN data snapshots in the public bucket. Pick the latest available date and download it to your node host. For example, use `2026-06-14` for the snapshot at `gravity-mainnet-data/2026-06-14.tar`:

```bash
SNAPSHOT_DATE=<YYYY-MM-DD>
curl -L --fail --continue-at - \
  "https://storage.googleapis.com/gravity-public-bucket/gravity-mainnet-data/${SNAPSHOT_DATE}.tar" \
  -o "/tmp/${SNAPSHOT_DATE}.tar"
```

Before the first start, replace the generated empty data directory with the snapshot data:

```bash
tar -xf "/tmp/${SNAPSHOT_DATE}.tar" \
  -C <YOUR_DEPLOY_BASE_DIR>/local-pfn/data
```

After extraction, the data directory should contain the snapshot databases while preserving the node directory layout:

```
<YOUR_DEPLOY_BASE_DIR>/local-pfn/data/consensus_db/
<YOUR_DEPLOY_BASE_DIR>/local-pfn/data/quorumstoreDB/
<YOUR_DEPLOY_BASE_DIR>/local-pfn/data/reth/
```

### Option B: Copy From an Existing Node You Control

If you already run a trusted RPC/PFN, you can copy only the chain databases from that node:

```bash
cd <YOUR_EXISTING_NODE_DATA_DIR>
tar -cf - consensus_db quorumstoreDB reth | \
  ssh <YOUR_TARGET_HOST> \
    "mkdir -p <YOUR_DEPLOY_BASE_DIR>/local-pfn/data && \
     cd <YOUR_DEPLOY_BASE_DIR>/local-pfn/data && tar -xf -"
```

Do **not** copy `rand_db` or `secure_storage.json` from another node. Those files carry node-specific randomness or safety state and should not be reused.

## 6. Start the Node

```bash
bash <YOUR_DEPLOY_BASE_DIR>/local-pfn/script/start.sh
```

Check the logs under the node directory:

```bash
tail -n 200 <YOUR_DEPLOY_BASE_DIR>/local-pfn/consensus_log/pfn.log
ls -lh <YOUR_DEPLOY_BASE_DIR>/local-pfn/execution_logs
```

## 7. Verify Sync

Query your local node:

```bash
curl -s http://127.0.0.1:8545 \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
```

Compare it with the public chain head:

```bash
curl -s https://mainnet-rpc.gravity.xyz \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
```

When the two heights are close and your local height keeps advancing, the node is caught up. Gravity has BFT-final blocks: once a block is returned by your node, there is no EVM-style reorg to wait for.

## Troubleshooting

| Symptom                                                         | Cause / fix                                                                                                              |
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `seeds: from=<id>: missing host or public_port in cluster.toml` | Use an explicit `peer_id` / `address` seed, or define the seed node in the same TOML.                                    |
| Node stays at a low height                                      | Re-check seed `peer_id`, host, port, network public key, `genesis.json`, and `waypoint.txt`.                             |
| `net_peerCount` returns `0`                                     | Usually expected. Gravity syncs through AptosBFT/VFN/public networks, not EVM devp2p gossip. Watch block height instead. |
| RPC unavailable                                                 | Confirm `rpc_port` is free, the process is running, and logs exist under the deploy directory.                           |
| Database paths look nested incorrectly                          | `data_dir` should be the deploy directory. Storage is rendered to `$data_dir/data`.                                      |

## See Also

* [Run a Gravity Mainnet VFN](/gravity-networks/run-a-mainnet-vfn.md)
* [Run a Gravity Mainnet Validator](/gravity-networks/run-a-mainnet-validator.md)
* [Gravity Mainnet (L1)](/gravity-networks/l1-mainnet.md)
* [Node Providers](/ecosystem-infrastructures/node-providers.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://docs.gravity.xyz/gravity-networks/run-a-mainnet-pfn.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
