Diagnosis and fixes for common Bitcoin Indexer problems — slow RPC, PostgreSQL errors, connection failures, and more.
Before diagnosing, check which stage is slow:
Block 140231: RPC total=10.084s getblockhash=10.062s getblock=21ms parse=1ms (97 txs)
| Log field | What it measures |
|---|---|
getblockhash |
Time waiting for Bitcoin Core to return block hash |
getblock |
Time to fetch full decoded block JSON |
parse |
Time to parse JSON into DB rows |
DB write |
Time for PostgreSQL COPY write |
The slow field points you directly to the bottleneck.
getblockhash (most common)Block 140231: RPC total=10.084s getblockhash=10.062s getblock=21ms parse=1ms
getblockhash is 90%+ of total time.
Bitcoin Core RPC is stalling. This almost always means Bitcoin Core is in IBD (ibd=true) and is busy with:
1. Check IBD status:
bitcoin-cli getblockchaininfo | grep initialblockdownload
If true, this latency is expected and will improve as the node syncs.
2. Increase dbcache:
# bitcoin.conf
dbcache=16384
Restart bitcoind. A larger cache means fewer LevelDB disk reads during validation, which reduces RPC stall time.
3. Increase RPC capacity:
rpcthreads=8
rpcworkqueue=64
4. Reduce workers during IBD:
workers: 2
batch_size: 2
More workers send more concurrent RPC requests to an already-busy node, making the stalls worse.
getblockgetblockhash=5ms getblock=8s parse=1ms
getblock is slow but getblockhash is fast.
Fetching the full verbose block (verbosity=2) is slow. Common causes:
1. Check disk I/O on the Bitcoin Core host:
iostat -x 1 5
If %util on the Bitcoin Core disk is near 100%, the disk is the bottleneck.
2. Ensure Bitcoin Core is on SSD/NVMe:
Spinning HDD will cause getblock to take 5–30 seconds for large recent blocks.
3. Reduce concurrent workers:
workers: 2
batch_size: 2
4. Check Bitcoin Core CPU:
top -p $(pgrep bitcoind)
Batch 141045-141046: fetched 2 blocks (87 txs) in 45ms wall time, DB write in 5s
DB write is slow relative to fetch time.
Missing partition:
The batch writer creates partitions automatically, but if there is a bug, a write can hit an unpartitioned fallback. Check:
SELECT tablename FROM pg_tables WHERE tablename LIKE 'transactions_%';
PostgreSQL disk I/O:
iostat -x 1 5
# Check the disk where PostgreSQL data directory lives
Index maintenance overhead:
During heavy bulk writes, indexes slow things down. For initial sync only:
-- Drop non-critical indexes before sync
-- Re-create after sync completes
WAL bottleneck:
# postgresql.conf
synchronous_commit = off
wal_buffers = 64MB
max_wal_size = 4GB
checkpoint_completion_target = 0.9
getblockhash=5ms getblock=30ms parse=2s
Go CPU-bound: JSON decode or struct allocation is slow.
Check Go CPU:
top -p $(pgrep indexer)
Check GOMAXPROCS:
# Should be unset, or set to number of CPU cores
echo $GOMAXPROCS
Check for memory pressure causing GC pauses:
Add GODEBUG=gctrace=1 to see GC activity:
GODEBUG=gctrace=1 ./indexer
failed to connect to database: dial tcp 127.0.0.1:5432: connect: connection refused
Check if PostgreSQL container is running:
docker compose ps
docker compose logs postgres
Restart if needed:
docker compose restart postgres
Check connection string:
database_url: "postgres://user:password@localhost:5432/btcindex?sslmode=disable"
Verify credentials match docker-compose.yml.
RPC error: dial tcp 127.0.0.1:8332: connect: connection refused
Check if bitcoind is running:
ps aux | grep bitcoind
bitcoin-cli getblockcount
Check bitcoin.conf has server=1:
server=1
rpcuser=youruser
rpcpassword=yourpassword
rpcbind=127.0.0.1
rpcallowip=127.0.0.1
Restart bitcoind after config changes:
bitcoin-cli stop
bitcoind -daemon
RPC error: 401 Unauthorized
Verify rpc_url credentials match bitcoin.conf:
rpc_url: "http://rpcuser:rpcpassword@127.0.0.1:8332"
rpcuser=rpcuser
rpcpassword=rpcpassword
ERROR: duplicate key value violates unique constraint "blocks_pkey"
The indexer is trying to write a block that already exists. This can happen if START_HEIGHT is set to a height that has already been indexed.
Let the indexer resume automatically from the last indexed height:
# Remove start_height override
# The indexer reads the max indexed height from PostgreSQL automatically
Or check and set explicitly:
SELECT MAX(height) FROM blocks;
Then set START_HEIGHT to one above that value.
work queue depth exceededRPC error: 429 Work queue depth exceeded
Bitcoin Core’s RPC work queue is full. Too many concurrent requests are being sent.
# bitcoin.conf
rpcworkqueue=128
rpcthreads=16
Also reduce indexer workers:
workers: 2
batch_size: 2
error: Dirty database version 3. Fix and force version.
migrate \
-path migrations \
-database "postgres://user:password@localhost:5432/btcindex?sslmode=disable" \
force 3
migrate \
-path migrations \
-database "postgres://user:password@localhost:5432/btcindex?sslmode=disable" \
up
bitcoin-cli getblockcount)docker compose ps)safe_tip and is waiting for new blocksdmesg | grep -i oom)journalctl -u bitcoin-indexer -n 50
If the last line is:
Bitcoin node syncing | blocks=886457 headers=948409 remaining=0
The indexer has caught up and is in live sync mode — this is normal.