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.
Architecture
Section titled “Architecture” clients │ 443 ──►│ ▼ Caddy (TLS termination) │ 3402 ──►│ ▼ Node (facilitator) ├── ethers.js → RPC nodes └── (optional) AWS DynamoDB for audit logsA 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.
Prerequisites
Section titled “Prerequisites”- 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.
Get the code
Section titled “Get the code”git clone https://github.com/0xgasless/x402-facilitatorcd x402-facilitator/b402-facilitatorConfigure environment
Section titled “Configure environment”cp .env.example .env$EDITOR .envAt minimum, set one of these relayer keys (the chain stays unregistered if its key is missing):
# Per-chain private keysRELAYER_PRIVATE_KEY_AVAX=0x... # Avalanche mainnet (43114)RELAYER_PRIVATE_KEY_FUJI=0x... # Fuji testnet (43113) — get free AVAX from a faucetRELAYER_PRIVATE_KEY_BASE=0x... # Base mainnet (8453)
# Or one key that gets reused across every EVM chainRELAYER_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/rpcFUJI_RPC_URL=https://api.avax-test.network/ext/bc/C/rpcBASE_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 portPORT=3402NODE_ENV=productionOption A — Docker Compose (recommended)
Section titled “Option A — Docker Compose (recommended)”The repo ships a docker-compose.yml that runs the facilitator behind Caddy with auto-TLS.
# Edit Caddyfile and change `x402.0xgasless.com` to your domain$EDITOR Caddyfile
# Bring it updocker compose up -d --build
# Watch logsdocker compose logs -f facilitatorYou 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.
Option B — Bare metal with PM2
Section titled “Option B — Bare metal with PM2”If you’d rather not use Docker:
# Install Node 20curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash -sudo apt-get install -y nodejs
# Buildcd b402-facilitatornpm installnpm run build
# Run under PM2npm install -g pm2pm2 start dist/server.js --name x402-facilitator --cwd "$(pwd)"pm2 savepm2 startup systemdThen put Caddy or nginx in front for TLS termination, proxying :443 → :3402.
Verify it works
Section titled “Verify it works”curl https://your-domain.com/healthShould return the chain list with your relayer addresses.
curl https://your-domain.com/list | jq '.networks[] | { network, chainId }'Lists every chain you registered.
Pointing clients at your facilitator
Section titled “Pointing clients at your facilitator”Agent SDK
Section titled “Agent SDK”const client = new OxGasAgent({ apiKey: process.env.OXGAS_API_KEY!, facilitatorUrl: 'https://your-domain.com', // your self-hosted instance});Direct REST callers
Section titled “Direct REST callers”Just change the base URL from https://x402.0xgasless.com to your own.
Funding the relayer
Section titled “Funding the relayer”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):
| Chain | Gas per transferWithAuthorization | Cost at typical gas |
|---|---|---|
| Avalanche | ~120,000 | ~$0.005 |
| Base | ~80,000 | ~$0.04 |
| Fuji (testnet) | ~120,000 | free |
A relayer wallet with $5–10 worth of native gas handles thousands of settlements before needing a refill.
Adding a new chain
Section titled “Adding a new chain”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.
Disabling DynamoDB audit logging
Section titled “Disabling DynamoDB audit logging”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.
Repository
Section titled “Repository”github.com/0xgasless/x402-facilitator
License: MIT.
See also
Section titled “See also”- Facilitator API — REST contract your self-hosted instance will serve.
- Supported chains — the chains we ship support for out of the box.