Devnet From Existing Data
This guide explains how to run a local dev chain using an existing Mainnet or Testnet data directory. This setup is useful when you want local testing with real chain data without syncing from scratch.
Always work on a copy of the data directory. Never modify your original Mainnet or Testnet data directly.
1. Copy the Existing Data Directory
Copy the source data directory to a new location for the dev chain:
cp -r /path/to/ckb-data /path/to/ckb-dev-fork
cd /path/to/ckb-dev-fork
All commands below assume the current directory is the copied directory.
2. Get the Source Chain Spec File
Download the chain spec file that matches your copied data:
- Mainnet:
https://github.com/nervosnetwork/ckb/blob/develop/resource/specs/mainnet.toml - Testnet:
https://github.com/nervosnetwork/ckb/blob/develop/resource/specs/testnet.toml
For example, for Mainnet:
curl -L -o mainnet.toml \
https://raw.githubusercontent.com/nervosnetwork/ckb/develop/resource/specs/mainnet.toml
3. Initialize the Dev Chain and Import the Source Spec
ckb init --chain dev --import-spec ./mainnet.toml --force
If you copied Testnet data, replace mainnet.toml with testnet.toml.
4. Update specs/dev.toml
Set Dummy Proof-of-Work for local development. The [params] section differs between Mainnet and Testnet — use the matching block below.
genesis_epoch_length (together with the epoch reward fields) participates in the genesis cellbase reward calculation, which determines the genesis block hash. If the value here does not match the value the source chain was launched with, the node will refuse to start with chainspec error: ChainSpec: genesis hash mismatch.
Mainnet
Mainnet was launched with genesis_epoch_length = 1743, so this value must be preserved:
[params]
genesis_epoch_length = 1743
cellbase_maturity = 0
permanent_difficulty_in_dummy = true
[pow]
func = "Dummy"
Testnet
The bundled Testnet spec has no [params] section and was launched with the default genesis_epoch_length = 1000. Do not add genesis_epoch_length here — leaving it unset lets it fall back to the default and keeps the genesis hash consistent:
[params]
cellbase_maturity = 0
permanent_difficulty_in_dummy = true
[pow]
func = "Dummy"
cellbase_maturity = 0makes locally mined cellbase outputs immediately spendable, which is convenient for development.permanent_difficulty_in_dummy = truekeeps the difficulty constant when running withDummyPoW. Its default isfalse, which would let difficulty be recalculated from the dummy block timestamps and swing wildly once you start mining locally; the bundledresource/specs/dev.tomltherefore enables it by default and the same is recommended here.
5. First Run Requires Spec-Check Flags
The copied database still records the original chain spec hash, so the first startup must include:
ckb run --skip-spec-check --overwrite-spec
After the first successful run, ckb run can be used normally.
Troubleshooting
If you see a log like init_snapshot Spec(GenesisMismatch(...)), the running spec and database spec do not match. Ensure:
- You imported the correct source chain spec.
- The first run uses
--skip-spec-check --overwrite-spec. - You are operating in the copied data directory, not the original one.