Skip to content

Self-hosting the facilitator

The 0xGasless facilitator is open source under MIT and ready to self-host. Run it on a VPS, a Kubernetes cluster, or your laptop — whatever fits.

Reasons to self-host:

  • You want a private relayer wallet and to keep gas costs in-house.
  • You need higher rate limits than the public facilitator allows.
  • You want to add chains we don’t support yet.
  • You’re allergic to external dependencies and want everything on your infra.

If none of that applies, the public facilitator at https://x402.0xgasless.com is fine. No registration, no API key required.

clients
443 ──►│
Caddy (TLS termination)
3402 ──►│
Node (facilitator)
├── ethers.js → RPC nodes
└── (optional) AWS DynamoDB for audit logs

A single small VPS (1 GB RAM, $5–6/month) comfortably handles thousands of req/min. Caddy auto-issues a Let’s Encrypt cert. The Node process consumes ~25 MB RAM under load.

  • A VPS or container host.
  • A domain you control (for the TLS cert).
  • One funded relayer wallet per chain you want to support — these wallets pay the on-chain gas. ~1 AVAX on Avalanche, ~0.1 ETH on Base, free on Fuji are reasonable starting balances.

That’s it. No database, no Redis, no extra services.

Terminal window
git clone https://github.com/0xgasless/x402-facilitator
cd x402-facilitator/b402-facilitator
Terminal window
cp .env.example .env
$EDITOR .env

At minimum, set one of these relayer keys (the chain stays unregistered if its key is missing):

# Per-chain private keys
RELAYER_PRIVATE_KEY_AVAX=0x... # Avalanche mainnet (43114)
RELAYER_PRIVATE_KEY_FUJI=0x... # Fuji testnet (43113) — get free AVAX from a faucet
RELAYER_PRIVATE_KEY_BASE=0x... # Base mainnet (8453)
# Or one key that gets reused across every EVM chain
RELAYER_PRIVATE_KEY=0x...

Other useful overrides (all optional):

# RPC URLs — defaults to public RPCs. Switch to Alchemy/QuickNode under load.
AVAX_RPC_URL=https://api.avax.network/ext/bc/C/rpc
FUJI_RPC_URL=https://api.avax-test.network/ext/bc/C/rpc
BASE_RPC_URL=https://mainnet.base.org
# Audit logging — only if you want it. Leave blank to disable.
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
AWS_REGION=eu-north-1
# Server port
PORT=3402
NODE_ENV=production

The repo ships a docker-compose.yml that runs the facilitator behind Caddy with auto-TLS.

Terminal window
# Edit Caddyfile and change `x402.0xgasless.com` to your domain
$EDITOR Caddyfile
# Bring it up
docker compose up -d --build
# Watch logs
docker compose logs -f facilitator

You should see, within a few seconds:

🔥 x402 Facilitator Service
📡 Listening on http://localhost:3402
⛓️ avalanche (43114)
relayer: 0xYOURWALLET...
A402: 0x457Db7ceBAdaF6A043AcE833de95C46E982cEdC8
⛓️ base (8453)
relayer: 0xYOURWALLET...
💾 DynamoDB logging: ENABLED (eu-north-1)
Ready. 🚀

Point a DNS A record at the VPS IP, wait for Caddy to fetch the cert, and you’re done.

If you’d rather not use Docker:

Terminal window
# Install Node 20
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash -
sudo apt-get install -y nodejs
# Build
cd b402-facilitator
npm install
npm run build
# Run under PM2
npm install -g pm2
pm2 start dist/server.js --name x402-facilitator --cwd "$(pwd)"
pm2 save
pm2 startup systemd

Then put Caddy or nginx in front for TLS termination, proxying :443 → :3402.

Terminal window
curl https://your-domain.com/health

Should return the chain list with your relayer addresses.

Terminal window
curl https://your-domain.com/list | jq '.networks[] | { network, chainId }'

Lists every chain you registered.

const client = new OxGasAgent({
apiKey: process.env.OXGAS_API_KEY!,
facilitatorUrl: 'https://your-domain.com', // your self-hosted instance
});

Just change the base URL from https://x402.0xgasless.com to your own.

Each chain’s relayer wallet needs native gas to settle. Refill ~weekly based on volume.

Cost guide for canonical EIP-3009 USDC (most efficient path):

ChainGas per transferWithAuthorizationCost at typical gas
Avalanche~120,000~$0.005
Base~80,000~$0.04
Fuji (testnet)~120,000free

A relayer wallet with $5–10 worth of native gas handles thousands of settlements before needing a refill.

src/chains.ts is the registry. Adding a new EVM chain is a few lines:

// In bootChainsFromEnv()
const optimismKey = pickKey('RELAYER_PRIVATE_KEY_OPTIMISM', 'RELAYER_PRIVATE_KEY');
if (optimismKey) {
registerChain({
id: 10,
name: 'optimism',
aliases: ['optimism-mainnet', 'op'],
rpcUrl: process.env.OPTIMISM_RPC_URL || 'https://mainnet.optimism.io',
privateKey: optimismKey,
a402Relayer: null, // OP USDC is EIP-3009 native
erc8004Registry: null, // no identity yet
explorer: 'https://optimistic.etherscan.io',
isTestnet: false,
knownTokens: {
'0x0b2c639c533813f4aa9d7837caf62653d097ff85': {
symbol: 'USDC', name: 'USD Coin', decimals: 6,
},
},
});
}

Rebuild and restart. Clients can now target network: 'optimism' or chainId: 10.

Leave AWS_ACCESS_KEY_ID blank. The facilitator boots fine and just doesn’t write logs anywhere.

If you want logs in your own database, add a writer in src/logging.ts — the interface (logVerify(data) / logSettle(data)) is small.

github.com/0xgasless/x402-facilitator

License: MIT.