Chain (gRPC) -> Yaci Indexer -> PostgreSQL -> PostgREST -> Explorer UI
git clone https://github.com/Cordtus/yaci-explorer.git
cd yaci-explorer
cp .env.example .env
yarn configure:env
# Set CHAIN_GRPC_ENDPOINT in .env
docker compose -f docker/docker-compose.yml up -dAccess: http://localhost:3001
Monitor:
docker compose -f docker/docker-compose.yml logs -f yaci
docker exec -it yaci-explorer-postgres psql -U yaci -d yaci -c "SELECT MAX(id) FROM api.blocks_raw;"./scripts/setup.sh # Option 1
# Or manually:
cp .env.example .env
docker compose -f docker/docker-compose.yml up -dsudo ./scripts/setup.sh # Option 2Installs PostgreSQL, PostgREST, Yaci indexer, Node.js frontend as systemd services.
./scripts/setup.sh # Option 3Connect to existing PostgREST instance.
# PostgreSQL
fly postgres create --name yaci-pg
# Yaci Indexer
cd /path/to/yaci
fly launch --name yaci-indexer
fly secrets set YACI_POSTGRES_DSN="postgres://..." YACI_GRPC_ENDPOINT="..."
# Explorer
cd /path/to/yaci-explorer
fly launch --name yaci-explorer
# Configure frontend via config.json
cat > config.json << EOF
{
"apiUrl": "https://your-postgrest-url.fly.dev",
"chainRestEndpoint": "https://api.yourchain.io",
"evmEnabled": true,
"ibcEnabled": true,
"appName": "My Explorer"
}
EOF
# Copy config to deployed container
fly ssh console -C "cat > /usr/share/nginx/html/config.json" < config.jsonyourdomain.com {
reverse_proxy /api/* localhost:3000 {
header_up -/api
}
reverse_proxy localhost:3001
}
server {
listen 80;
server_name yourdomain.com;
location /api/ {
proxy_pass http://localhost:3000/;
}
location / {
proxy_pass http://localhost:3001;
}
}Frontend configuration is provided at runtime via config.json instead of build-time environment variables.
Create config.json:
cp public/config.json.example public/config.json
# Edit config.json with your settingsKey settings:
apiUrl: PostgREST endpoint URL (required)chainRestEndpoint: Chain REST API for IBC denom resolution (optional)evmEnabled: Enable/disable EVM featuresibcEnabled: Enable/disable IBC featuresappName: Custom application namebranding: Logo URLs, colors, footer textlinks: Website, docs, social media links
Docker deployment:
# Mount config.json as volume
docker run -v ./config.json:/usr/share/nginx/html/config.json ...
# Or copy to running container
docker cp config.json container:/usr/share/nginx/html/config.jsonSee public/config.json.example for all available options.
Docker/Backend:
CHAIN_GRPC_ENDPOINT: gRPC endpoint of chain (required)YACI_IMAGE: Yaci Docker image (default: ghcr.io/cordtus/yaci:main)POSTGRES_PASSWORD: PostgreSQL password (default: foobar)CHAIN_ID,CHAIN_NAME: Override auto-detection
Enable via config.json:
{
"evmEnabled": true
}Default: http://localhost:2112/metrics
Scrape config:
scrape_configs:
- job_name: yaci-indexer
static_configs: [{ targets: ['localhost:2112'] }]docker compose -f docker/docker-compose.yml down -v
docker compose -f docker/docker-compose.yml up -ddocker compose -f docker/docker-compose.yml down
docker compose -f docker/docker-compose.yml up -d postgres
docker exec -it yaci-explorer-postgres psql -U yaci -d yaci -c \
"TRUNCATE api.blocks_raw, api.transactions_main, api.messages_main, api.events_main;"
docker compose -f docker/docker-compose.yml up -dsudo -u postgres dropdb yaci
sudo -u postgres createdb yaci
sudo systemctl restart yaci./scripts/reset-devnet.sh
# Custom compose file:
./scripts/reset-devnet.sh --compose-file path/to/compose.yml
# Non-interactive:
SKIP_CONFIRM=1 ./scripts/reset-devnet.shBare metal:
# Full redeploy with rebuild:
yarn redeploy:systemdblocks_raw: id (bigint), data (jsonb)transactions_main: id (text), height (bigint), timestamp, fee (jsonb), gas_used (bigint), error (text)messages_main: id (text), message_index (int), type (text), sender (text), mentions (text[]), metadata (jsonb)events_main: id (text), event_index (int), event_type (text), attr_key (text), attr_value (text)evm_transactions: tx_id (text), evm_hash (text), type (int), from_address (text), to_address (text), value (text), gas_limit (bigint), gas_price (text), nonce (bigint), data (text), logs (jsonb)evm_logs: tx_id (text), log_index (int), address (text), topics (text[]), data (text)evm_tokens: address (text), name (text), symbol (text), decimals (int)evm_token_transfers: tx_id (text), log_index (int), token_address (text), from_address (text), to_address (text), value (text)
# Logs
docker compose -f docker/docker-compose.yml logs -f
# Status
docker compose -f docker/docker-compose.yml ps
# Restart
docker compose -f docker/docker-compose.yml restart
# Update Yaci
docker compose -f docker/docker-compose.yml pull yaci
docker compose -f docker/docker-compose.yml up -d yaci
# Rebuild Explorer
docker compose -f docker/docker-compose.yml up -d --build explorer
# Check Progress
docker exec -it yaci-explorer-postgres psql -U yaci -d yaci -c "SELECT MAX(id) FROM api.blocks_raw;"cd ~/repos/yaci-explorer-apis
export DATABASE_URL="<your-db-url>"
./scripts/migrate.shVerify:
\dt api.evm_*yarn decode:evmSystemd (recommended):
sudo systemctl enable --now evm-decode.timer
sudo systemctl status evm-decode.timerCron:
*/5 * * * * cd ~/repos/yaci-explorer-apis && DATABASE_URL="..." yarn decode:evm >> /var/log/evm-decode.log 2>&1Docker:
docker logs yaci-indexer -fSystemd:
journalctl -u yaci -fDatabase:
SELECT
COUNT(*) as total_txs,
COUNT(*) FILTER (WHERE type = 2) as eip1559_txs,
SUM(array_length(topics, 1)) as total_topics
FROM api.evm_transactions t
LEFT JOIN api.evm_logs l ON t.tx_id = l.tx_id;- Verify CHAIN_GRPC_ENDPOINT
- Check chain node accessibility
- For local Docker chains use host.docker.internal:9090
- Wait for PostgreSQL initialization
- Verify migrations completed
- Check api schema exists
- Check Yaci logs for "Extracting blocks"
- Test PostgREST:
curl http://localhost:3000/blocks_raw?limit=1
- Small/test: 2 vCPU, 4GB RAM, 50GB storage, YACI_MAX_CONCURRENCY=50
- Medium: 4 vCPU, 8GB RAM, 200GB storage, YACI_MAX_CONCURRENCY=100
- Large: 8 vCPU, 16GB RAM, 500GB+ storage, YACI_MAX_CONCURRENCY=200
Storage: ~1-5GB per million blocks (varies by transaction volume)
- Explorer UI: http://localhost:3001
- PostgREST API: http://localhost:3000
- Prometheus: http://localhost:2112
- PostgreSQL: localhost:5432
Run separate stacks with different ports in separate directories. Configure:
- POSTGRES_PORT
- POSTGREST_PORT
- EXPLORER_PORT
- YACI_METRICS_PORT
docker exec yaci-explorer-postgres pg_dump -U yaci -d yaci -F c -f /tmp/backup.dump
docker cp yaci-explorer-postgres:/tmp/backup.dump /backups/backup_$(date +%Y%m%d_%H%M%S).dump
find /backups -name "backup_*.dump" -mtime +7 -delete- Yaci Indexer: https://github.com/Cordtus/yaci/issues
- Middleware: https://github.com/Cordtus/yaci-explorer-apis/issues
- Explorer: https://github.com/Cordtus/yaci-explorer/issues
- PostgREST: https://postgrest.org/