Custom Block Explorer Deployment
Standard question: why your own explorer if Etherscan exists? Answers: private EVM network (L2, corporate sidechain), branded interface, custom data (decoded events, protocol-specific metrics), indexing control. For public networks — mostly branding and customization. For private networks — only option.
Choosing Base Solution
Don't write block explorer from scratch — several open source projects with different trade-offs exist:
Blockscout — most mature and functional. Used by Gnosis Chain, Polygon, dozens of L2s. Written in Elixir/Phoenix, PostgreSQL. Supports EVM, ERC-20/721/1155, token tracking, decoded transactions, API. Complex setup, requires resources, but production-proven.
Otterscan — lightweight, runs as frontend-only over Archive node with Erigon (requires otterscan-compatible RPC API). No separate database needed. Good for development networks and small production.
Sirato (ex-Web3Labs Explorer) — commercial with open source Community Edition. Good Hyperledger Besu support.
Custom on The Graph basis — if you need maximum customization of protocol-specific data.
For most EVM networks choice is between Blockscout (full-featured) and Otterscan (simple and fast to deploy).
Blockscout Deployment
Architecture
Ethereum/EVM node (Archive) ←→ Blockscout Backend (Elixir)
↕
PostgreSQL + Redis
↕
Blockscout Frontend (Next.js)
Node Requirements
Blockscout requires archive node with trace API enabled:
- Geth:
--gcmode=archive --http.api=eth,net,web3,debug,trace - Erigon: archive by default,
--private.api.addr=0.0.0.0:9090 - Nethermind:
--JsonRpc.EnabledModules=Eth,Subscribe,Trace,TxPool,Web3,Personal,Proof,Net,Parity,Health
Without trace API internal transactions and token transfers from internal calls won't work.
Docker Compose Deployment
# docker-compose.yml
services:
db:
image: postgres:15
environment:
POSTGRES_DB: blockscout
POSTGRES_USER: blockscout
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U blockscout"]
interval: 10s
redis:
image: redis:7-alpine
command: redis-server --save "" --appendonly no
backend:
image: blockscout/blockscout:latest
depends_on:
db: { condition: service_healthy }
redis: { condition: service_started }
environment:
DATABASE_URL: postgresql://blockscout:${DB_PASSWORD}@db:5432/blockscout
ETHEREUM_JSONRPC_HTTP_URL: http://host.docker.internal:8545
ETHEREUM_JSONRPC_TRACE_URL: http://host.docker.internal:8545
ETHEREUM_JSONRPC_WS_URL: ws://host.docker.internal:8546
CHAIN_ID: "1" # your chain ID
COIN: ETH # native token
COIN_NAME: Ether
NETWORK: Ethereum
SUBNETWORK: Mainnet
LOGO: /images/blockscout_logo.svg # custom logo
REDIS_URL: redis://redis:6379
SECRET_KEY_BASE: ${SECRET_KEY_BASE} # 64-char random string
PORT: 4000
ports: ["4000:4000"]
command: sh -c "mix do ecto.create, ecto.migrate && mix phx.server"
frontend:
image: ghcr.io/blockscout/frontend:latest
environment:
NEXT_PUBLIC_API_HOST: http://backend:4000
NEXT_PUBLIC_APP_HOST: http://localhost:3000
NEXT_PUBLIC_NETWORK_NAME: "My Network"
NEXT_PUBLIC_NETWORK_SHORT_NAME: "MyNet"
NEXT_PUBLIC_NETWORK_ID: "1"
NEXT_PUBLIC_NETWORK_CURRENCY_NAME: Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL: ETH
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS: "18"
ports: ["3000:3000"]
Customization
Blockscout 6.x separated into backend (Elixir) and frontend (Next.js) — simplifies UI customization. Frontend env variables control:
# Branding
NEXT_PUBLIC_NETWORK_LOGO=/logo.png
NEXT_PUBLIC_NETWORK_ICON=/icon.png
NEXT_PUBLIC_COLOR_THEME_DEFAULT=dark
# Functionality
NEXT_PUBLIC_IS_TESTNET=false
NEXT_PUBLIC_MARKETPLACE_ENABLED=true # dApp catalog
NEXT_PUBLIC_STATS_ENABLED=true # network stats
For deep customization — fork frontend repository and edit React components.
Otterscan Deployment (Simplified Option)
Otterscan doesn't need separate database — reads directly from Erigon via extended RPC:
# Erigon with otterscan API
erigon --chain=mainnet \
--private.api.addr=0.0.0.0:9090 \
--http \
--http.api=eth,erigon,ots,web3,net,debug,trace
# Otterscan frontend
docker run --rm -p 5100:80 \
-e ERIGON_URL=http://your-erigon-host:8545 \
otterscan/otterscan:latest
Done. But functionality significantly poorer than Blockscout — no token tracker, no API, no verified contracts.
Indexing Performance
Blockscout on first launch indexes entire chain history. For mainnet Ethereum — weeks. For young private network — hours. Parameters to speed up:
INDEXER_BLOCKS_BATCH_SIZE=100 # blocks per batch (default 10)
INDEXER_RECEIPTS_BATCH_SIZE=250
INDEXER_CONCURRENCY=5 # parallel workers (depends on RPC limits)
Monitoring Indexing
-- Current indexing status
SELECT number FROM blocks ORDER BY number DESC LIMIT 1;
-- Missing blocks (blocks without transactions NOT missing, that's normal)
SELECT * FROM missing_block_ranges LIMIT 20;
-- Database size
SELECT pg_size_pretty(pg_database_size('blockscout'));
Verified Contracts
Blockscout supports contract verification via several methods:
Hardhat/Foundry plugin — automatic verification on deploy if you specify Blockscout API key:
// hardhat.config.ts
etherscan: {
apiKey: { myNetwork: "any-string" }, // Blockscout accepts any key
customChains: [{
network: "myNetwork",
chainId: YOUR_CHAIN_ID,
urls: {
apiURL: "http://your-blockscout/api",
browserURL: "http://your-blockscout",
},
}],
}
Sourcify integration: Blockscout can verify via Sourcify — decentralized source registry. For public networks preferred — verification stored not only with you.
Scaling
As load grows (many users, large chain):
- PostgreSQL read replicas for API queries
- PgBouncer for connection pooling
- CDN for frontend static
- Multiple backend instances behind load balancer (stateless part)
- Redis Cluster for caches
Estimated timelines: Blockscout deployment for new private network — 3–5 days. For existing network with historical indexing and custom UI — 1–2 weeks.







