How the Indexer Works

Network Status API Docs

ENS Indexer is an indexer for the Ethereum Name Service (ENS). It pulls ENS data from the Ethereum blockchain, processes it, and stores it in a PostgreSQL database, where it can be quickly and efficiently accessed via a RESTful API.

All instances run the same codebase and independently index data from the canonical Ethereum chain.

Optionally, operators can expose their instance to consumers via the ENSIndexer.com API proxy, which routes requests across multiple independent indexer nodes to provide resilient, decentralized access to ENS data.

Technology Stack

The indexer is written entirely in JavaScript and runs on Bun, a fast JavaScript runtime. Each service runs as a systemd unit, making deployment and process management straightforward on any Linux server.

This was an intentional decision to avoid overengineering. Rather than building a complex distributed system with message queues, orchestration layers, and microservice frameworks, the indexer uses simple, proven tools: PostgreSQL for storage, Redis for coordination, and plain JavaScript for business logic. The result is a system that's easy to understand, deploy, debug, and modify.

Architecture Overview

The indexer connects to an Ethereum RPC provider (Quicknode, dRPC, Infura, or Alchemy) to fetch on-chain events from ENS smart contracts. These raw events are stored in PostgreSQL, then processed into queryable domain records. Redis is used for coordination between services and managing processing queues.

Data stores: PostgreSQL (primary database), Redis (queues and coordination)

Contracts indexed (23 total):

Category Contracts From Block
Registries Current Registry, Old Registry (Vyper, 2017–2020) 3,327,417
Registrars ERC-721 Registrar, Old Deed-based Registrar (2017–2019) 3,605,331
Controllers 5 versions (2019–2025) 7,666,495
Name Wrapper NameWrapper (ERC-1155) 16,925,607
Reverse Resolution Reverse Registrar, Default Reverse Resolver 16,925,605
DNS Registrar DNS domain claims 19,027,588
Subdomain Registrar Subdomain registrations 9,380,459
Public Resolvers 9 resolver versions (2017–2025) 3,648,359
Note: The Old Registry and Old Registrar cover the original ENS system (2017–2019) that used a Vickrey auction mechanism and deed-based ownership.

The Five Services

Fetcher Event Fetcher

The fetcher polls the Ethereum blockchain via eth_getLogs RPC calls for new events from 23 ENS smart contracts, starting from block 3,327,417. It processes events in batches and writes raw event data to the ens_events table.

Processor Event Processor

The processor reads unprocessed events from ens_events and transforms them into domain records in the domains table. Events are staged in Redis queues and bulk-flushed to PostgreSQL for efficiency.

Each domain's effective_owner_address is computed using a priority hierarchy: wrapper owner > registrar owner > registry owner.

Address Address Service

The address service extracts all Ethereum addresses referenced in events (owners, resolvers, transaction senders) and stores them in address_table. This enables optimized data retrieval like "show me all domains owned by this address", and allows us to process reverse names efficiently.

During idle time (once at the head of the chain), it also resolves pending domains against the local rainbow table to discover unknown labels.

Reverse Reverse Name Service

A standalone service for resolving primary ENS names from reverse node claims. When an address sets a primary name, the reverse name service resolves it using a tiered strategy:

Resolved names are written to the address_primary_names table.

Monitor Monitor Server

The monitor provides a web interface that allows you see the synchronization status of your indexer. It also exposes a RESTful API for querying indexed data.

Database Schema

Table Purpose
ens_events Raw blockchain events
domains Processed domain records with computed effective_owner_address
ens_event_domains Junction table linking events to domains
address_table Unique Ethereum addresses with reverse node references
address_primary_names Maps addresses to their primary ENS names

Data Flow Example

Here's what happens when someone registers a domain. e.g. test.eth:

Initial Sync

When a new indexer is deployed, it must process all historical ENS events from the beginning (block 3,327,417 for the earliest ENS contract). This initial sync covers over 23 contracts and takes approximately 12 hours depending on your RPC provider and server resources.

All five services run in parallel - the processor and address service start working on events as soon as the fetcher writes them, even while the fetcher is still catching up.

Tip: You can watch initial sync progress in real-time by SSHing into your server and running: sudo tail -f /var/log/cloud-init-output.log

Redis Queues

The indexer uses Redis for coordination between services and to handle edge cases:

Domains

Queue Purpose
Pending Domains In many cases the information discerned from an individual event is not enough to uniquely identify it. Domains waiting for more data (e.g., missing parent domain or label) are added to the 'Pending Domains' queue.
Ready Domains When we have enough data about a given domain, we add it to the 'Ready Domains' queue. Rows in this queue are batched and efficiently inserted into the domains table.

Events

Queue Purpose
Pending Events If an event hasn't provided us enough data to uniquely identify the domain to which it relates, we queue it here. When we have enough data, i.e. the relevant 'Pending' domain becomes 'Ready' and a domain row has been inserted, associated 'Pending Events' will be linked to their domain via the ens_event_domains junction table.
Direct Events When a row already exists for the domain to which an event relates, we add it to the 'Direct Events' queue. Rows in this queue are batched and efficiently inserted into the ens_event_domains junction table, linking events to their domains.

Metadata

We maintain 7 queues for metadata updates for already existing domain rows. This mechanism was implemented as an optimization such that once a row has been created in the domains table we can batch and efficiently update the associated data when additional events are processed. The result is that if a domain name has changed ownership 10 times throughout history, during the initial sync we will only update the appropriate column once, at the end of the processing queue.

Note: During the initial sync, these numbers will be large.

Rainbow Table

Some ENS events only contain a labelhash (keccak256 hash of the label) rather than the actual label text. To resolve these, the indexer uses a "rainbow table" - a precomputed database of ~200M known label to hash mappings.

During idle time (once at the head of the chain), the address service automatically checks pending domains against the local rainbow table and resolves any matches. Labels are discovered through multiple mechanisms including LLM inference, web search, and external APIs.