Maintenance & Operations

Keep a self-hosted instance healthy: upgrades, reindexing, backups, and recovery.


Upgrading

Docker Compose deployments:

Terminal window
cd blockscout/docker-compose
docker compose pull
docker compose up -d

The backend runs pending database migrations automatically on startup. Watch the logs during the first boot after an upgrade:

Terminal window
docker compose logs -f backend

Source installs: git pull, mix deps.get, mix ecto.migrate, restart.

Upgrade policy

  • Read the release notes before upgrading — some releases require a reindex or config changes.
  • Back up the database first. Migrations are one-way.
  • Upgrade during low-traffic windows; the instance may serve stale or partial data while migrations run.

Checking indexing health

Terminal window
curl http://localhost:4000/api/v2/main-page/indexing-status

A healthy response shows the indexed block within a few blocks of the chain head and finished_indexing: true (for historical catch-up). If the indexed block lags and stays lagged:

  1. Check backend logs for JSON-RPC errors (rate limits, missing trace methods).
  2. Check database disk space — a full disk stalls indexing silently.
  3. Check RPC node sync status (eth_syncing on the node).

Handling chain reorgs

The indexer detects reorgs automatically and re-fetches affected blocks — no operator action needed for normal, shallow reorgs. Signs of trouble are gaps in the block sequence or stuck counters after a deep reorg; in that case, reindex the affected range.

Reindexing

For a bounded range or a fresh start:

Terminal window
# Full resync (destroys indexed data)
docker compose down
docker volume rm blockscout_pgdata
docker compose up -d

For a partial reindex, bound the range with FIRST_BLOCK/LAST_BLOCK environment variables on a temporary basis, or use the reindex tooling in the backend repo. Document your reason — reindexing a mainnet takes days.

Backups

Logical backup:

Terminal window
docker compose exec db pg_dump -U postgres blockscout | gzip > backup-$(date +%F).sql.gz

Restore:

Terminal window
gunzip -c backup-2026-08-30.sql.gz | docker compose exec -T db psql -U postgres blockscout

Recommendations:

  • Daily logical backups, plus volume snapshots if your infrastructure supports them.
  • Keep at least 7 daily and 4 weekly backups off-box.
  • Test restores quarterly — an untested backup is not a backup.

Log management

Terminal window
docker compose logs --since 24h backend > backend.log
  • Backend logs are structured and tagged by fetcher module (Indexer.Fetcher.*) — filter on those when diagnosing gaps.
  • Rotate Docker logs (max-size, max-file in the compose logging config) to avoid disk exhaustion.

Common failure modes

SymptomCause and fix
API 500s on recent dataIndexer behind; see indexing health above
Empty internal transactionsRPC endpoint lacks trace methods; switch endpoints
Disk fullDatabase growth or unrotated logs; expand volume, rotate
Migrations fail on upgradeRestore backup, re-read release notes, retry
Frontend shows wrong network dataNEXT_PUBLIC_API_HOST points at another instance

Disaster recovery

If the database is lost: restore the latest backup, then let the indexer catch up from the backup’s block height automatically. If no backup exists, a full resync from genesis is the only path — another reason backups matter.

Next steps