relayer/docs/SELF_HOSTING.md
The FHEVM Relayer bridges FHEVM host chains (e.g. Ethereum) and the Zama Gateway. It handles public decryption, user decryption, input proof verification, and FHE key material distribution. Anyone can self-host a relayer for permissionless access to the FHEVM network.
Running your own relayer gives you permissionless, independent access to the FHEVM network without depending on third-party infrastructure.
cast) -- for wallet operations during onboarding (install)All commands run from relayer/.
make db-start
Starts a local PostgreSQL instance via Docker Compose (port 5433).
make db-migrate
make preflight-mainnet
This interactive onboarding wizard:
config/local.mainnet.yaml from the example template if missingmake approve-payment-mainnet if missing)make run-mainnet
Starts the relayer with cargo run using the mainnet config. Requires the local PostgreSQL to be running.
make health
Checks /liveness, /healthz, /version, and /metrics endpoints.
Testnet follows the same flow with different tokens:
make db-start
make db-migrate
make preflight-testnet
make run-testnet
make health
The config file stores the private key at gateway.tx_engine.private_key. Best practices:
config/local.mainnet.yaml or config/local.testnet.yaml to version control (they are already in .gitignore)APP_GATEWAY__TX_ENGINE__PRIVATE_KEY=0x... to avoid storing the key in the config file at allKey operator-tunable fields in the config file:
| Field | Description | Default |
|---|---|---|
http.endpoint | API listen address | 0.0.0.0:3000 |
log.format | Log format (compact, pretty, json) | pretty |
gateway.tx_engine.tx_throttlers.*.per_seconds | TX throttle rate per operation type | 20 |
storage.app_pool.max_connections | Max database connections (app pool) | 10 |
storage.cron.timeout_cron_interval | How often the timeout worker runs | 60s |
storage.cron.public_decrypt_timeout | Timeout for public decryption requests | 30m |
storage.cron.user_decrypt_timeout | Timeout for user decryption requests | 30m |
storage.cron.input_proof_timeout | Timeout for input proof requests | 30m |
storage.cron.expiry_enabled | Enable automatic data cleanup | false |
storage.cron.public_decrypt_expiry | Retention for public decrypt records | 365d |
storage.cron.user_decrypt_expiry | Retention for user decrypt records | 7d |
storage.cron.input_proof_expiry | Retention for input proof records | 7d |
http.retry_after.max_seconds | Max Retry-After header value | 300 |
http.enable_admin_endpoint | Enable runtime config via /admin/config (see security note below) | false |
Configuration is hierarchical: YAML file -> environment variables (APP_ prefix, __ nesting) -> CLI args.
/admin/config is primarily intended for testing and benchmarking. It is disabled by default and intentionally has no application-level authentication. When enabling it, restrict reachability via network-level controls:
http.endpoint to loopback (127.0.0.1:3000) or an internal-only subnet, or:9898 (GET /metrics):3000 (/liveness, /healthz)src/metrics/docs_and_dashboards/ for Grafana dashboard queries and metric descriptionsSee the Troubleshooting section in the main README.