> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/graphprotocol/graph-node/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration Overview

> Advanced TOML configuration for multi-database and multi-chain Graph Node deployments

Graph Node supports advanced configuration through TOML files for complex deployments requiring multiple databases, chains, or specialized node roles.

## When to Use Configuration Files

Use TOML configuration files when you need:

* **Multiple databases**: Sharding across multiple PostgreSQL instances
* **Multiple chains**: Indexing several blockchain networks
* **Read replicas**: Distributing query load across replica databases
* **Node specialization**: Dedicated index nodes and query nodes
* **Custom deployment rules**: Control where subgraphs are stored and indexed

For simple single-database setups, use [command-line arguments](/running/from-source) instead.

## Configuration File Structure

The TOML configuration file has four main sections:

```toml theme={null}
[general]        # General node settings
[store]          # Database configuration
[chains]         # Blockchain provider configuration  
[deployment]     # Subgraph deployment rules
```

## Using a Configuration File

Specify the configuration file with the `--config` flag:

```bash theme={null}
graph-node --config /path/to/config.toml --node-id index-node-1
```

When using `--config`, you cannot use:

* `--postgres-url`
* `--postgres-secondary-hosts`
* `--postgres-host-weights`
* `--ethereum-rpc` (define in config file instead)

### Environment Variable Expansion

Configuration files support environment variable expansion:

```toml theme={null}
[store.primary]
connection = "postgresql://graph:${PGPASSWORD}@${PGHOST}/graph"
```

For complex templating, use `envsubst`:

```bash theme={null}
envsubst < config.template.toml > config.toml
graph-node --config config.toml
```

## Store Configuration

The `[store]` section defines PostgreSQL databases for storing subgraph data.

### Single Database Setup

Minimal configuration equivalent to `--postgres-url`:

```toml theme={null}
[store]
[store.primary]
connection = "postgresql://graph:password@localhost:5432/graph-node"
pool_size = 10

[deployment]
[[deployment.rule]]
indexers = [ "index-node-1", "index-node-2" ]
```

### Multiple Databases (Sharding)

Split subgraph storage across multiple databases:

```toml theme={null}
[store]
# Primary shard (required)
[store.primary]
connection = "postgresql://graph:${PGPASSWORD}@primary-db/graph"
weight = 0  # Don't use for queries
pool_size = 10

# Read replicas for primary shard
[store.primary.replicas.repl1]
connection = "postgresql://graph:${PGPASSWORD}@primary-replica1/graph"
weight = 1  # 50% of query traffic

[store.primary.replicas.repl2]
connection = "postgresql://graph:${PGPASSWORD}@primary-replica2/graph"
weight = 1  # 50% of query traffic

# Additional shard for VIP subgraphs
[store.vip]
connection = "postgresql://graph:${PGPASSWORD}@vip-db/graph"
weight = 1  # Use for 50% of queries
pool_size = 20

[store.vip.replicas.repl1]
connection = "postgresql://graph:${PGPASSWORD}@vip-replica/graph"
weight = 1  # Use for 50% of queries
```

### Connection Pool Configuration

<ParamField path="pool_size" type="number | rules">
  Number of database connections per Graph Node instance

  Can be a fixed number or rules based on node ID:

  ```toml theme={null}
  # Fixed pool size
  pool_size = 10

  # Dynamic based on node role
  pool_size = [
    { node = "index_node_.*", size = 20 },
    { node = "query_node_.*", size = 80 }
  ]
  ```

  Rules are evaluated in order; first match wins.
</ParamField>

<ParamField path="weight" type="number" default="1">
  Query traffic distribution weight

  * `0`: Don't send queries (indexing only)
  * Higher values: More query traffic

  Traffic is distributed proportionally across all databases/replicas.
</ParamField>

<ParamField path="connection" type="string" required>
  PostgreSQL [libpq connection string](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING)

  Supports environment variable expansion: `${VAR_NAME}`
</ParamField>

### Validating Pool Configuration

Use `graphman` to validate connection pool settings:

```bash theme={null}
graphman --config config.toml config pools index-node-1 query-node-1
```

This shows total connections opened by all nodes.

## Chain Configuration

The `[chains]` section configures blockchain network providers.

### Basic Chain Setup

```toml theme={null}
[chains]
ingestor = "block_ingestor_node"  # Node responsible for block ingestion

[chains.mainnet]
shard = "primary"  # Store chain data in 'primary' shard
protocol = "ethereum"  # Default, can be omitted
provider = [
  { label = "mainnet-primary", url = "https://eth-mainnet.g.alchemy.com/v2/API_KEY", features = ["archive", "traces"] },
  { label = "mainnet-fallback", url = "https://mainnet.infura.io/v3/PROJECT_ID", features = [] }
]
```

### Multi-Chain Configuration

```toml theme={null}
[chains]
ingestor = "block_ingestor_node"

[chains.mainnet]
shard = "vip"
amp = "ethereum-mainnet"  # Name used by AMP
provider = [
  { label = "mainnet1", url = "http://eth-node-1:8545", features = ["archive", "traces"], headers = { Authorization = "Bearer token" } },
  { label = "mainnet2", url = "http://eth-node-2:8545", features = ["archive"] }
]

[chains.sepolia]
shard = "primary"
provider = [
  { label = "sepolia", url = "https://sepolia.infura.io/v3/PROJECT_ID", features = [] }
]

[chains.polygon]
shard = "primary"
protocol = "ethereum"
polling_interval = 1000  # Poll every 1 second
provider = [
  { label = "polygon-rpc", url = "https://polygon-rpc.com", features = ["archive"] }
]

[chains.near-mainnet]
shard = "primary"
protocol = "near"
provider = [
  { label = "near-firehose", details = { type = "firehose", url = "https://firehose.example.com", key = "api-key", features = ["compression", "filters"] } }
]
```

### Provider Configuration

<ParamField path="label" type="string" required>
  Provider name (appears in logs)
</ParamField>

<ParamField path="url" type="string" required>
  RPC endpoint URL
</ParamField>

<ParamField path="features" type="array" default="[]">
  Provider capabilities:

  * `archive`: Full historical state
  * `traces`: Supports `debug_traceBlockByNumber`
  * `no_eip1898`: Doesn't support EIP-1898 (block by hash/number object)
  * `no_eip2718`: Doesn't return `type` field in transaction receipts
  * `compression/gzip`: Supports gzip compression
  * `compression/brotli`: Supports brotli compression
  * `compression/deflate`: Supports deflate compression
</ParamField>

<ParamField path="headers" type="object">
  HTTP headers to include in requests

  ```toml theme={null}
  headers = { Authorization = "Bearer token", "X-API-Key" = "key" }
  ```
</ParamField>

<ParamField path="limit" type="number">
  Maximum subgraphs that can use this provider

  At least one provider should be unlimited.
</ParamField>

<ParamField path="transport" type="string" default="rpc">
  Connection type: `rpc`, `ws`, or `ipc`
</ParamField>

### Provider Types

<ParamField path="type" type="string" default="web3">
  Provider implementation:

  * `web3`: Standard Ethereum JSON-RPC (default)
  * `firehose`: StreamingFast Firehose
  * `web3call`: Web3 provider for `eth_call` only
</ParamField>

For Firehose providers:

```toml theme={null}
provider = [
  {
    label = "firehose-mainnet",
    details = {
      type = "firehose",
      url = "https://firehose.example.com",
      token = "bearer-token",
      # OR use key-based auth
      key = "api-key",
      features = ["compression", "filters"]
    }
  }
]
```

### Advanced: Provider Limits per Node

Limit which nodes can use specific providers:

```toml theme={null}
[chains.mainnet]
shard = "primary"
provider = [
  { label = "unlimited", url = "http://primary-node:8545", features = [] },
  {
    label = "limited",
    url = "http://secondary-node:8545",
    features = [],
    match = [
      { name = "index_node_.*", limit = 10 },   # Max 10 subgraphs on index nodes
      { name = "query_node_.*", limit = 0 }     # Never use on query nodes
    ]
  }
]
```

If a node's name doesn't match any rule, it cannot use that provider.

## Deployment Rules

The `[deployment]` section controls where new subgraphs are deployed.

### Deployment Rule Structure

Rules are evaluated in order. First matching rule determines placement.

```toml theme={null}
[deployment]
# VIP subgraphs go to dedicated shard
[[deployment.rule]]
match = { name = "(vip|important)/.*" }
shard = "vip"
indexers = [ "index_node_vip_0", "index_node_vip_1" ]

# Kovan network subgraphs
[[deployment.rule]]
match = { network = "kovan" }
indexers = [ "index_node_kovan_0" ]

# Multiple networks (xDai or POA Core)
[[deployment.rule]]
match = { network = [ "xdai", "poa-core" ] }
indexers = [ "index_node_other_0" ]

# Default rule (required, no match clause)
[[deployment.rule]]
shards = [ "sharda", "shardb" ]  # Distribute across shards
indexers = [
  "index_node_community_0",
  "index_node_community_1",
  "index_node_community_2"
]
```

### Match Conditions

<ParamField path="match.name" type="regex">
  Regular expression matching subgraph name

  ```toml theme={null}
  match = { name = "uniswap/.*" }
  ```
</ParamField>

<ParamField path="match.network" type="string | array">
  Network name(s) the subgraph indexes

  ```toml theme={null}
  # Single network
  match = { network = "mainnet" }

  # Multiple networks
  match = { network = [ "mainnet", "sepolia" ] }
  ```
</ParamField>

### Shard Selection

<ParamField path="shard" type="string" default="primary">
  Fixed shard name for deployment storage
</ParamField>

<ParamField path="shards" type="array">
  List of shards; chooses the one with fewest deployments

  ```toml theme={null}
  shards = [ "shard1", "shard2", "shard3" ]
  ```
</ParamField>

### Indexer Selection

<ParamField path="indexers" type="array" required>
  List of node IDs eligible to index this deployment

  Graph Node chooses the indexer with fewest assigned subgraphs.

  ```toml theme={null}
  indexers = [ "index-node-1", "index-node-2" ]
  ```

  Names must match the `--node-id` argument when starting nodes.
</ParamField>

### Simulating Deployment

Test where a subgraph would be deployed:

```bash theme={null}
graphman --config config.toml config place my-org/my-subgraph mainnet
```

Shows the target shard and eligible indexers without making changes.

## Query Node Configuration

Designate nodes as query-only (no indexing):

```toml theme={null}
[general]
query = "query_node_.*"  # Regex matching query node IDs
```

Query nodes:

* Only respond to GraphQL queries
* Don't connect to blockchain providers
* Don't index subgraphs

## Complete Example

```toml theme={null}
# Query-only nodes
[general]
query = "query_node_.*"

# Database shards with replicas
[store]
[store.primary]
connection = "postgresql://graph:${PGPASS}@primary-db:5432/graph"
weight = 0
pool_size = [
  { node = "index_.*", size = 20 },
  { node = "query_.*", size = 80 }
]

[store.primary.replicas.replica1]
connection = "postgresql://graph:${PGPASS}@primary-replica1:5432/graph"
weight = 1

[store.primary.replicas.replica2]
connection = "postgresql://graph:${PGPASS}@primary-replica2:5432/graph"
weight = 1

[store.vip]
connection = "postgresql://graph:${PGPASS}@vip-db:5432/graph"
weight = 1
pool_size = 30

# Blockchain providers
[chains]
ingestor = "index_node_mainnet_0"

[chains.mainnet]
shard = "vip"
amp = "ethereum-mainnet"
provider = [
  { label = "alchemy", url = "https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}", features = ["archive", "traces"] },
  { label = "infura", url = "https://mainnet.infura.io/v3/${INFURA_KEY}", features = ["archive"] }
]

[chains.sepolia]
shard = "primary"
provider = [
  { label = "sepolia", url = "https://sepolia.infura.io/v3/${INFURA_KEY}", features = [] }
]

[chains.polygon]
shard = "primary"
protocol = "ethereum"
polling_interval = 500
provider = [
  { label = "polygon", url = "https://polygon-rpc.com", features = ["archive"] }
]

# Deployment rules
[deployment]
# High-priority subgraphs
[[deployment.rule]]
match = { name = "(uniswap|aave|compound)/.*" }
shard = "vip"
indexers = [ "index_node_vip_0", "index_node_vip_1" ]

# Test network subgraphs
[[deployment.rule]]
match = { network = "sepolia" }
shard = "primary"
indexers = [ "index_node_testnet_0" ]

# Default for everything else
[[deployment.rule]]
shards = [ "primary" ]
indexers = [
  "index_node_mainnet_0",
  "index_node_mainnet_1",
  "index_node_mainnet_2"
]
```

## Starting Nodes with Configuration

### Index Nodes

```bash theme={null}
# Mainnet index nodes
graph-node --config config.toml --node-id index_node_mainnet_0
graph-node --config config.toml --node-id index_node_mainnet_1

# VIP index nodes  
graph-node --config config.toml --node-id index_node_vip_0
```

### Query Nodes

```bash theme={null}
graph-node --config config.toml --node-id query_node_0
graph-node --config config.toml --node-id query_node_1
```

Query nodes automatically detected via `[general] query` regex.

## Validation and Testing

### Validate Configuration

```bash theme={null}
graph-node --config config.toml config check
```

Checks for:

* Syntax errors
* Undefined shards referenced in deployment rules
* Missing required sections

### Check Connection Pools

```bash theme={null}
graphman --config config.toml config pools index_node_mainnet_0 query_node_0
```

Shows total database connections opened by specified nodes.

### Simulate Deployment

```bash theme={null}
graphman --config config.toml config place uniswap/v3 mainnet
```

Shows which shard and indexers would be selected.

## Best Practices

<Accordion title="Connection Pool Sizing">
  **Index nodes**: 10-30 connections per shard

  **Query nodes**: 50-100 connections per shard

  Monitor `GRAPH_STORE_CONNECTION_WAIT_TIME` to detect pool exhaustion.
</Accordion>

<Accordion title="Read Replica Usage">
  Set main database weight to `0` for shards with replicas:

  ```toml theme={null}
  [store.primary]
  weight = 0  # Indexing only

  [store.primary.replicas.repl1]
  weight = 1  # All queries go to replicas
  ```

  This reduces load on the primary database.
</Accordion>

<Accordion title="Provider Redundancy">
  Always configure at least 2 providers per chain for failover:

  ```toml theme={null}
  provider = [
    { label = "primary", url = "...", features = ["archive", "traces"] },
    { label = "fallback", url = "...", features = ["archive"] }
  ]
  ```
</Accordion>

<Accordion title="Deployment Rule Ordering">
  Place most specific rules first:

  ```toml theme={null}
  # Specific subgraph
  [[deployment.rule]]
  match = { name = "uniswap/v3" }

  # Subgraph pattern
  [[deployment.rule]]
  match = { name = "uniswap/.*" }

  # Network-wide
  [[deployment.rule]]
  match = { network = "mainnet" }

  # Catch-all (must be last)
  [[deployment.rule]]
  ```
</Accordion>

## Troubleshooting

<Accordion title="Configuration validation errors">
  Run validation to see specific issues:

  ```bash theme={null}
  graph-node --config config.toml config check
  ```

  Common issues:

  * Undefined shard referenced in deployment rule
  * Missing required `[deployment]` section
  * Invalid regex in deployment match
</Accordion>

<Accordion title="Subgraph deployed to wrong shard">
  Simulate deployment to check rules:

  ```bash theme={null}
  graphman --config config.toml config place subgraph-name network-name
  ```

  Verify deployment rules are ordered correctly (most specific first).
</Accordion>

<Accordion title="Connection pool exhaustion">
  Check pool usage:

  ```bash theme={null}
  graphman --config config.toml config pools <node-ids...>
  ```

  Increase pool size for heavily loaded nodes:

  ```toml theme={null}
  pool_size = [
    { node = "query_node_heavy", size = 150 }
  ]
  ```
</Accordion>

## Next Steps

* [Environment Variables](/running/environment-variables) - Fine-tune behavior beyond config files
* [Docker Setup](/running/docker-setup) - Run Graph Node with Docker
* [Building from Source](/running/from-source) - Compile and run Graph Node
