How to set up Bitcoin Indexer from scratch — Bitcoin Core, PostgreSQL, Apache AGE, migrations, and the indexer itself.
Before installing Bitcoin Indexer, you need:
| Requirement | Version | Notes |
|---|---|---|
| Go | 1.22+ | Download |
| Docker | Latest | For PostgreSQL + AGE |
| Docker Compose | v2+ | Included in Docker Desktop |
| Bitcoin Core | 25+ | Fully synced or syncing node |
| golang-migrate | Latest | For running DB migrations |
Bitcoin Indexer requires a running Bitcoin Core node with txindex=1 enabled.
# Linux (example - check https://bitcoincore.org for latest version)
wget wget https://bitcoincore.org/bin/bitcoin-core-${BTC_VERSION}
tar -xzf bitcoin-${BTC_VERSION}-linux-gnu.tar.gz
sudo install -m 0755 -o root -g root -t /usr/local/bin bitcoin-${BTC_VERSION}/bin/*
Create or edit ~/.bitcoin/bitcoin.conf:
# Required for transaction indexing
server=1
txindex=1
# Performance tuning (adjust to your available RAM)
dbcache=16384
rpcthreads=8
rpcworkqueue=64
# Network
maxconnections=64
par=8
# Mempool
maxmempool=512
mempoolexpiry=72
# RPC credentials
rpcuser=youruser
rpcpassword=yourpassword
rpcbind=127.0.0.1
rpcallowip=127.0.0.1
Important:
dbcacheis the single biggest performance lever for Bitcoin Core during Initial Block Download. Set it as high as your RAM allows (leave 4GB+ for the OS and PostgreSQL).
bitcoind -daemon
Check sync status:
bitcoin-cli getblockchaininfo
You will see "initialblockdownload": true while syncing. The indexer works during IBD but RPC latency will be higher.
# Download Go 1.22+
wget https://go.dev/dl/go1.22.4.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.22.4.linux-amd64.tar.gz
# Add to PATH
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
source ~/.bashrc
# Verify
go version
# Install the migrate CLI tool
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
# Verify
migrate --version
git clone https://github.com/Abhinav7903/bitcoin-indexer.git
cd bitcoin-indexer
The project ships with a docker-compose.yml that runs PostgreSQL 16 with Apache AGE pre-installed.
docker compose up -d
Verify PostgreSQL is running:
docker compose ps
docker compose logs postgres
PostgreSQL 16 is required. Apache AGE is not compatible with PostgreSQL 17+. Do not upgrade the PostgreSQL version in
docker-compose.yml.
If you are running PostgreSQL 16 directly on the host:
CREATE DATABASE btcindex;
\c btcindex
CREATE EXTENSION age;
LOAD 'age';
SET search_path = ag_catalog, "$user", public;
Copy the example config:
cp config.example.yaml config.yaml
Edit config.yaml:
database_url: "postgres://user:password@localhost:5432/btcindex?sslmode=disable"
rpc_url: "http://youruser:yourpassword@127.0.0.1:8332"
workers: 2
batch_size: 2
start_height: 0
historical_sync: true
See Configuration for all available options.
migrate \
-path migrations \
-database "postgres://user:password@localhost:5432/btcindex?sslmode=disable" \
up
This creates all tables, partitions, and indexes. Migrations are safe to re-run.
Verify the schema was created:
psql "postgres://user:password@localhost:5432/btcindex" -c "\dt"
You should see tables: blocks, transactions, tx_inputs, tx_outputs, address_transactions, utxo_set, address_balances.
# Run directly with Go
go run ./cmd/indexer
# Or build and run
go build -o indexer ./cmd/indexer
./indexer
You should see logs like:
RPC blockchain info | blocks=886457 headers=948409 ibd=true
Bitcoin node syncing | blocks=886457 headers=948409 remaining=61952
Ingesting blocks 0 -> 1 | safe_tip=886447 blocks=886457 headers=948409
Batch 0-1: fetched 2 blocks (2 txs) in 48ms wall time, DB write in 15ms
go run ./cmd/api
The API server starts and reads from the indexed PostgreSQL tables. See API for endpoint documentation.
To skip already-indexed blocks or start from a checkpoint:
export START_HEIGHT=500000
go run ./cmd/indexer
Or set start_height in config.yaml.
Check that blocks are being written:
psql "postgres://user:password@localhost:5432/btcindex" \
-c "SELECT height, hash, tx_count FROM blocks ORDER BY height DESC LIMIT 10;"
Check transaction count:
psql "postgres://user:password@localhost:5432/btcindex" \
-c "SELECT COUNT(*) FROM transactions;"
The indexer handles SIGINT and SIGTERM gracefully. Press Ctrl+C and it will finish the current batch before shutting down.
getblockhash is very slow — Bitcoin Core is still in IBD. This is normal. See Troubleshooting.
migrate: error: no change — Migrations already applied. This is fine.
connection refused on PostgreSQL — Check docker compose ps. If the container is not running, check docker compose logs postgres.
connection refused on RPC — Verify rpcuser, rpcpassword, and rpcbind in bitcoin.conf. Restart bitcoind after config changes.