Overview
The IBC v2 Relayer is a request-driven service that relays IBC v2 packets between Cosmos SDK chains and EVM-compatible chains (Ethereum, Base, Optimism, Arbitrum, Polygon, etc.). It exposes a gRPC API for submitting transactions to relay and tracking relay progress, and runs a background dispatcher that polls for pending packets. The relayer is stateful — it persists packet state in PostgreSQL — and connects to two external services: a Proof API that generates relay transactions, and a signing service (local key file or remote gRPC signer) that authorizes those transactions.Components
1. Relayer (this service)
The core relay process. Exposes:- gRPC API on port
9000(configurable) — for submitting and tracking relay requests - Prometheus metrics on port
8888(configurable) — for observability
platform-relayer— the relayer process itselfplatform-relayer-migrate— one-shot migration runner (golang-migrate), must run before the relayer starts
2. PostgreSQL (required)
The relayer stores all packet state in PostgreSQL. The schema is applied by the migration container before the relayer starts. Credentials are passed via thePOSTGRES_USER and POSTGRES_PASSWORD environment variables (both default to relayer).
3. Proof API (required)
The relayer delegates proof and transaction generation to an external Proof API service. This is the IBC Eureka Relayer / Proof API. The relayer connects to it via gRPC (ibcv2_proof_api.grpc_address).
The Proof API itself depends on:
- IBC Attestor instances — one per chain being attested (see the Attestor Deployment guide)
- Chain RPC endpoints — for both chains in each relay pair
4. Signing Service (required)
Two modes:| Mode | When to use | Config field |
|---|---|---|
| Local key file | Development, low-security deployments | signing.keys_path |
| Remote signer (gRPC) | Production, key isolation via KMS/HSM | signing.grpc_address |
signing.grpc_address is set, it takes precedence and the key file is ignored. If neither is set, the relayer will not start.
5. Chain RPC Endpoints (required)
The relayer queries chain state directly for gas estimation and transaction submission:| Chain Type | Required |
|---|---|
| Cosmos | Tendermint RPC (cosmos.rpc) + Cosmos gRPC (cosmos.grpc) |
| EVM | JSON-RPC HTTP endpoint (evm.rpc) |
Pre-Deployment: On-Chain Setup
Before the relayer can relay packets, IBC light clients must be created on both chains and counterparty information registered. This is a one-time setup per chain pair. Required steps (via the Proof API):- Deploy IBC contracts on the EVM chain — ICS26 Router and ICS20 Transfer contracts
- Create a light client on the EVM chain tracking the Cosmos chain state
- Create a light client on the Cosmos chain tracking the EVM chain state
- Register counterparty info on the Cosmos chain — associates the Cosmos client ID with the EVM client ID and merkle prefix
- Register the light client address on the EVM router — maps the client ID to the deployed light client contract
counterparty_chains map in ibcv2 config must reflect these client IDs:
Building the Images
The repository contains a single Dockerfile for both the relayer and the migration runner.- Builder — Go 1.24 on Debian Trixie, compiles the binary with
CGO_ENABLED=0 - Certs — Installs CA certificates from Debian Trixie
- Runtime —
gcr.io/distroless/static-debian13:nonroot; minimal, runs asnonroot:nonroot
Database Migrations
Migrations must run and complete before the relayer process starts. They are idempotent and can be run on every deploy.Option 1: Docker Compose (development)
The includeddocker-compose.yml handles this automatically:
migrate/migrate before the relayer boots.
Option 2: Migration container
Option 3: Generic migrate image with local migration files
Option 4: migrate CLI directly
Configuration
The relayer is configured via a YAML file passed with--config. See the Relayer Configuration Reference for full details. Below is a practical deployment example.
Cosmos ↔ EVM example
Key Management
Local signing (keys_path)
Create a JSON file mapping chain IDs to private keys:
- EVM chains: hex-encoded ECDSA private key with
0xprefix - Cosmos chains: hex-encoded secp256k1 private key (no
0xprefix)
config/local/ibcv2keys.json.example for the full format.
Remote signing (grpc_address)
Point signing.grpc_address at a gRPC signing service implementing the SignerService proto (see proto/signer/signerservice.proto). The platform signer service is compatible out of the box.
authorization: Bearer <token> header on every signing RPC call.
CLI Flags
Docker Deployment
Building and running
Full Docker Compose (with all dependencies)
Kubernetes Deployment
Startup ordering
Deploy in this order:- PostgreSQL
- Migration job (wait for completion)
- Relayer deployment
Migration Job
Relayer Deployment
Note: Run exactly one replica. The relayer is not designed for active-active horizontal scaling — multiple instances would compete on database state and double-submit transactions.
Ports
| Port | Protocol | Purpose |
|---|---|---|
9000 | gRPC (HTTP/2) | Relay API — Relay, Status RPCs used by clients |
8888 | HTTP | Prometheus metrics scrape endpoint |
relayer_api.address and metrics.prometheus_address in the YAML config.
Networking
Docker
The relayer must share a Docker network with:- PostgreSQL
- Proof API service
- Any chain RPC containers (if running locally)
kt-optimism) so it can resolve Kurtosis service hostnames.
RPC endpoint authentication
For RPC endpoints that require HTTP basic auth, store credentials in an environment variable and reference it by name in the config:Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
POSTGRES_USER | No | relayer | Database username |
POSTGRES_PASSWORD | No | relayer | Database password |
SERVICE_ACCOUNT_TOKEN | Remote signer only | — | Bearer token sent on signer gRPC calls |
<rpc_basic_auth_var> | If set in config | — | Basic auth credentials for a chain RPC endpoint (variable name defined per-chain in config) |
Health Checking
The relayer exposes an HTTP health endpoint at/health on the gRPC port (e.g. GET http://localhost:9000/health). The service is considered healthy when it returns HTTP 200. Startup typically takes up to 60 seconds while the relayer establishes connections to PostgreSQL, the Proof API, and all configured chain RPCs.
Startup Ordering
Observability
The relayer emits Prometheus metrics on the configuredmetrics.prometheus_address. Key metrics to alert on:
| Metric | Alert condition |
|---|---|
relayer_gas_balance_state | Value ≥ 1 (warning) or ≥ 2 (critical) per chain |
excessive_relay_latency_counter | Counter increasing — packets taking too long |
relayer_api_request_count | Unexpected error codes on Relay or Status RPCs |
transactions_submitted_counter | High rate of submission failures |
signer_gas_alert_thresholds.ibcv2.
Gas Balance Management
The relayer’s signing address must maintain a gas balance on each chain. The relayer monitors this and exposes it as a metric. Set alert thresholds in config:Upgrade Path
Because the relayer stores state in PostgreSQL:- Run migrations before deploying the new image (
platform-relayer-migrate:new-version up) - Replace the relayer container/pod — the relayer will resume from existing database state
- Migrations are forward-only; to roll back, run
downto the previous version before restoring the old image