Advanced 15 min read

Self-Hosting

Run your own x402 facilitator with x402-rs

If you prefer full control over your infrastructure, you can run your own x402 facilitator using x402-rs.

Consider Self-Hosting If:

  • Custom logic - You need specialized behavior during x402 workflows
  • Regulatory compliance - Data residency requirements or audit trails that must stay on your infrastructure
  • Absolute control - You want complete ownership of the payment verification and settlement process
  • Blockchain proximity - You’re already running nodes or have deep integration with the target chain
  • Ops-ready - You’re comfortable managing gas bumps, stuck transactions, nonce coordination, and private key management yourself

Use FareSide Hosted If:

  • Avoid settlement complexity - We handle gas bumps, stuck transactions, nonce management, private key management and other operational headaches for you
  • Getting started - Focus on building your product, not payment infrastructure
  • Managed reliability - Want uptime guarantees and automatic failover
  • Advanced features - Need payment splits, webhooks, or cross-chain support (coming soon)

Prerequisites

  • Docker (recommended) or Rust toolchain (1.80+)
  • Access to RPC endpoints for your target networks
  • Private key for the facilitator wallet (for gas)
  • Server with reliable uptime

Quick Start with Docker

Prebuilt Docker images are available from:

  • GitHub Container Registry: ghcr.io/x402-rs/x402-facilitator

1. Create Configuration File

Create a config.json file. Note the use of CAIP-2 identifiers for chain keys.

{
  "port": 8080,
  "host": "0.0.0.0",
  "chains": {
    "eip155:84532": {
      "eip1559": true,
      "signers": ["$EVM_PRIVATE_KEY"],
      "rpc": [{
        "http": "https://sepolia.base.org",
        "rate_limit": 100
      }]
    },
    "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1": {
      "signers": ["$SOLANA_PRIVATE_KEY"],
      "rpc": "https://api.devnet.solana.com"
    },
    "aptos:2": {
      "rpc": "https://fullnode.testnet.aptoslabs.com/v1",
      "sponsor_gas": true,
      "signer": "$APTOS_PRIVATE_KEY"
    }
  },
  "schemes": [{
    "id": "v1-eip155-exact",
    "chains": "eip155:*"
  }, {
    "id": "v2-eip155-exact",
    "chains": "eip155:*"
  }, {
    "id": "v2-eip155-upto",
    "chains": "eip155:*"
  }, {
    "id": "v2-solana-exact",
    "chains": "solana:*"
  }, {
    "id": "v2-aptos-exact",
    "chains": "aptos:*"
  }]
}

2. Create Environment File

Create a .env file with your secrets:

CONFIG=config.json
EVM_PRIVATE_KEY=0x...
SOLANA_PRIVATE_KEY=base58key...
APTOS_PRIVATE_KEY=0x...
RUST_LOG=info

3. Run the Container

docker run --env-file .env -v $(pwd)/config.json:/app/config.json -p 8080:8080 ghcr.io/x402-rs/x402-facilitator

4. Verify It’s Running

curl http://localhost:8080/health

Configuration Reference

The facilitator is configured via config.json.

Chains Configuration

Keys are CAIP-2 chain identifiers.

EVM (eip155):

"eip155:8453": {
  "eip1559": true,
  "flashblocks": false,
  "signers": ["$PRIVATE_KEY"],
  "rpc": [{ "http": "https://mainnet.base.org" }]
}
  • eip1559 — use EIP-1559 transaction format (defaults to true; set it to false if a chain does not support it)
  • flashblocks — when true, use latest block for gas estimation (check if your chain is flashblocks-enabled)
  • signers — array of private keys (hex with 0x prefix), supports $ENV_VAR syntax
  • rpc — array of RPC endpoint objects with http or https URL and optional rate_limit

Solana (solana):

"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": {
  "signer": "$PRIVATE_KEY",
  "rpc": "https://api.mainnet-beta.solana.com",
  "pubsub": "wss://api.mainnet-beta.solana.com"
}
  • signer — Ed25519 private key (base58-encoded), supports $ENV_VAR syntax
  • rpc — HTTP RPC endpoint URL
  • pubsub — WebSocket endpoint for transaction confirmations (optional but recommended)

Aptos (aptos):

"aptos:1": {
  "rpc": "https://fullnode.mainnet.aptoslabs.com/v1",
  "sponsor_gas": true,
  "signer": "$PRIVATE_KEY"
}
  • rpc — Aptos fullnode REST API URL
  • sponsor_gas — when true, the facilitator pays gas fees for client transactions (recommended)
  • signer — Ed25519 private key (hex with 0x prefix), supports $ENV_VAR syntax

Schemes Configuration

Defines which payment schemes are enabled for which chains.

"schemes": [
  {
    "id": "v2-eip155-exact",
    "chains": "eip155:*"
  },
  {
    "id": "v2-eip155-upto",
    "chains": "eip155:*"
  },
  {
    "id": "v2-solana-exact",
    "chains": "solana:*"
  },
  {
    "id": "v2-aptos-exact",
    "chains": "aptos:*"
  }
]

Supported chains field formats:

  • Specific: eip155:84532 matches only Base Sepolia
  • Pattern: eip155:{1,5,84532} matches specific chains
  • Wildcard: eip155:* matches all EVM chains, solana:* matches all Solana chains

Building from Source

If you prefer to build locally:

# Clone the repository
git clone https://github.com/x402-rs/x402-rs.git
cd x402-rs

# Build and run the facilitator package
cargo run --release --package x402-facilitator --features full

Or build your own Docker image:

docker build -t x402-facilitator .
docker run --env-file .env -v $(pwd)/config.json:/app/config.json -p 8080:8080 x402-facilitator

Docker Compose

For production deployments:

version: '3.8'
services:
  facilitator:
    image: ghcr.io/x402-rs/x402-facilitator
    ports:
      - "8080:8080"
    volumes:
      - ./config.json:/app/config.json
    env_file:
      - .env
    restart: unless-stopped

Connecting Your Services

Point your middleware to your self-hosted facilitator:

TypeScript

import { HTTPFacilitatorClient } from "@x402/core/server";

const facilitatorClient = new HTTPFacilitatorClient({
  url: "http://your-facilitator:8080"
});

Rust (x402-axum)

use x402_axum::X402Middleware;

// Point to your self-hosted facilitator
let x402 = X402Middleware::try_from("http://your-facilitator:8080/").unwrap();

Production Considerations

High Availability

For production, run multiple facilitator instances behind a load balancer:

Example docker-compose.prod.yml:

version: '3.8'
services:
  facilitator-1:
    image: ghcr.io/x402-rs/x402-facilitator
    volumes:
      - ./config.json:/app/config.json
    env_file: .env.instance1  # Unique private key

  facilitator-2:
    image: ghcr.io/x402-rs/x402-facilitator
    volumes:
      - ./config.json:/app/config.json
    env_file: .env.instance2  # Different private key

  nginx:
    image: nginx
    ports:
      - "8080:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
Each instance must have its own private key

Sharing a private key across multiple facilitator instances will cause nonce collisions and transaction failures. Each instance needs a unique EVM_PRIVATE_KEY, SOLANA_PRIVATE_KEY, and APTOS_PRIVATE_KEY, and each wallet must be funded with gas tokens.

Each facilitator instance needs its own funded wallet:

  • Generate separate keys for each instance
  • Fund each wallet with gas tokens on all supported networks
  • Monitor gas balances across all wallets
  • Consider using a key management service for rotation

RPC providers often have rate limits. For high-availability deployments:

  • Carefully distribute RPC endpoints across instances to spread the load
  • Use different RPC providers for different instances
# .env.instance1
EVM_PRIVATE_KEY=0xKey1...
RPC_URL_BASE=https://base-mainnet.g.alchemy.com/v2/KEY_1

# .env.instance2
EVM_PRIVATE_KEY=0xKey2...
RPC_URL_BASE=https://base.llamarpc.com

Security

  • Minimize wallet balances - Don’t keep large amounts of gas funds in facilitator wallets. Top up regularly as they deplete rather than pre-funding with large amounts
  • Never expose private keys in logs or error messages
  • Use HTTPS in production (terminate at load balancer)
  • Firewall the facilitator to only accept traffic from your services
  • Rotate keys periodically

Logs

Enable detailed logging via environment variable:

RUST_LOG=debug

Observability

The facilitator emits OpenTelemetry-compatible traces and metrics. To enable:

# For Honeycomb / Jaeger / etc
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_SERVICE_NAME=x402-facilitator

Comparison: Self-Hosted vs FareSide

AspectSelf-HostedFareSide
Setup timeHoursMinutes
Settlement opsGas bumps, nonces, stuck txs - all yoursHandled for you
Private key managementGenerate, secure, rotate, fund multiple walletsNot your problem
RPC managementRate limits, failover, multiple providersManaged
High availabilityComplex (unique keys per instance, RPC planning)Built-in
Cost modelFixed (servers + gas + ops time)Per transaction
UptimeYour responsibility99.9% SLA
Payment routingDIYIncluded
SupportCommunityDirect

Migration

From FareSide to Self-Hosted

  1. Set up your facilitator
  2. Test with a staging environment
  3. Update middleware configuration
  4. Monitor for issues

From Self-Hosted to FareSide

  1. Join the waitlist
  2. Get your API key
  3. Update middleware to use FareSide URL
  4. Decommission your facilitator

Resources