Keep a self-hosted instance healthy: upgrades, reindexing, backups, and recovery.
Upgrading
Docker Compose deployments:
cd blockscout/docker-composedocker compose pulldocker compose up -dThe backend runs pending database migrations automatically on startup. Watch the logs during the first boot after an upgrade:
docker compose logs -f backendSource 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
curl http://localhost:4000/api/v2/main-page/indexing-statusA 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:
- Check backend logs for JSON-RPC errors (rate limits, missing trace methods).
- Check database disk space — a full disk stalls indexing silently.
- Check RPC node sync status (
eth_syncingon 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:
# Full resync (destroys indexed data)docker compose downdocker volume rm blockscout_pgdatadocker compose up -dFor 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:
docker compose exec db pg_dump -U postgres blockscout | gzip > backup-$(date +%F).sql.gzRestore:
gunzip -c backup-2026-08-30.sql.gz | docker compose exec -T db psql -U postgres blockscoutRecommendations:
- 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
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-filein the compose logging config) to avoid disk exhaustion.
Common failure modes
| Symptom | Cause and fix |
|---|---|
| API 500s on recent data | Indexer behind; see indexing health above |
| Empty internal transactions | RPC endpoint lacks trace methods; switch endpoints |
| Disk full | Database growth or unrotated logs; expand volume, rotate |
| Migrations fail on upgrade | Restore backup, re-read release notes, retry |
| Frontend shows wrong network data | NEXT_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
- Monitoring — catch problems before users do
- Operations Command Reference