> For the complete documentation index, see [llms.txt](/llms.txt).

Import sync bootstraps a new node from **published bootstrap data** — a pre-synchronized database, signed by its publishers — instead of syncing the whole chain from peers. RSKj downloads the bootstrap data, checks it against signatures from keys it already trusts, loads it into a fresh database, and then continues syncing normally from that point.

It is a **one-time operation you run once, on a new node**, before putting it into service.

:::danger[Import sync erases the database every time it runs]

Enabling import does not mean "import if needed". Every time a node starts with import enabled, it **deletes its database directory** and downloads the bootstrap data again — even if the node was fully synced a minute earlier.

Run it as a one-off from the command line, and never leave `database.import.enabled = true` in a configuration file. A node whose config has it enabled will wipe itself on every restart, including restarts you did not ask for, such as a reboot or a crash recovery.

:::

## At a glance

1. Check your [prerequisites and free space](#before-you-start) — the database is sized for a full node, and the download needs temporary space of its own.
2. Optionally, [check which height the import will land you on](#check-where-import-sync-will-land-you), and how large the download is, before committing to it.
3. [Run the node once with `--import`](#run-the-import). RSKj downloads the published bootstrap data, checks that [enough trusted signers agree on it](#how-bootstrap-data-is-trusted), and loads it into a fresh database. There is nothing for you to download, and the flag takes no file path.
4. [Restart the node without the flag](#confirm-it-worked-then-restart-without-the-flag) and confirm it is syncing on from the imported height.
5. [Delete the leftover files](#clean-up-temporary-files) from the temporary directory.

If anything fails along the way, look the message up under [Common failures](#common-failures).

## Who this page is for

These instructions cover running the node from the **JAR file**, on **Mainnet** or **Testnet**.

|                                    | Import sync                                                                                                                                                                                        |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| JAR (`java -cp … co.rsk.Start`)    | Supported — this page                                                                                                                                                                              |
| Docker                             | Supported, with a different procedure — see [Docker](#docker)                                                                                                                                      |
| Ubuntu package / `systemd` service | Supported, with a different procedure — see [Ubuntu package](#ubuntu-package)                                                                                                                      |
| Regtest                            | Not supported. No bootstrap data is published for Regtest, and because the database is erased before the import is attempted, running `--import` there deletes the Regtest database and then fails |

## Before you start

- **Java 17 JDK** and **RSKj `VETIVER-9.0.4` or later** — see [Setup node using Java](/node-operators/setup/installation/java/). Earlier versions cannot read the bootstrap data published today, and will fail the import.
- **Disk for the database.** Size the data directory for a full node, per the [minimum requirements](/node-operators/setup/requirements/) — not for the size of the download.
- **Disk for temporary files.** RSKj downloads the bootstrap archive and extracts it into the JVM's temporary directory: `/tmp` on Linux, a per-user directory under `/var/folders` on macOS. To see the exact path yours will use, run `java -XshowSettings:properties -version 2>&1 | grep java.io.tmpdir`. Both files exist there at the same time, so size the temporary space for **the archive plus the larger file extracted from it**, on top of the database itself. To check that size before committing to the download, see [Check where import sync will land you](#check-where-import-sync-will-land-you).

## How bootstrap data is trusted

Bootstrap data is published by several independent signers. RSKj ships, per network, the source URL and the **public keys** it will accept, so a default installation needs no configuration to use import sync.

| Network | `database.import.url`                                  |
| ------- | ------------------------------------------------------ |
| Mainnet | `https://import.mainnet.rskcomputing.net/dbs/mainnet/` |
| Testnet | `https://import.testnet.rskcomputing.net/dbs/testnet/` |

Each signer publishes its own index under that URL, at a path named for its public key:

**Mainnet:**

```text
https://import.mainnet.rskcomputing.net/dbs/mainnet/<TRUSTED-PUBLIC-KEY>/index.json
```

**Testnet:**

```text
https://import.testnet.rskcomputing.net/dbs/testnet/<TRUSTED-PUBLIC-KEY>/index.json
```

Every entry in an index carries a block `height`, the path to the archive, its `hash`, and the signer's signature over that entry.

RSKj will only import bootstrap data that **enough trusted signers agree on**: the same height, with the same hash, correctly signed by each. The threshold is a majority of the keys you have configured, and never fewer than two — with the three keys shipped for each network, that is two. A height offered by too few signers is ignored, however recent it is. Among the heights that meet that bar, RSKj takes the highest.

Every configured signer's index must also be **reachable**. RSKj fetches all of them before it compares anything, so one index that cannot be fetched or parsed stops the import, even when the signers that did respond would have met the threshold. The threshold decides how many signers must agree; it does not make a missing publisher optional. If a publisher is down, wait for it to come back and run the import again.

The trusted keys are long, and they are the one thing worth taking from the JAR you are about to run rather than from this page, since that is the authoritative answer for your version:

**Mainnet:**

```shell
unzip -p <PATH-TO-THE-RSKJ-JAR> config/main.conf | sed -n '/import {/,/}/p'
```

**Testnet:**

```shell
unzip -p <PATH-TO-THE-RSKJ-JAR> config/testnet.conf | sed -n '/import {/,/}/p'
```

## Check where import sync will land you

**Import sync does not leave you at the chain tip.** It leaves you at the newest height that enough signers agree on, and the node then syncs the remaining blocks from peers in the normal way. Depending on how recent the published data is, that remainder can still be substantial: import sync shortens the initial sync, it does not remove it.

You can see exactly which height you would land on before downloading anything. Substitute the three trusted keys for your network, then:

**Mainnet:**

```shell
mkdir -p ~/rskj-index/mainnet && cd ~/rskj-index/mainnet

URL=https://import.mainnet.rskcomputing.net/dbs/mainnet/

for KEY in \
  <TRUSTED-KEY-1> \
  <TRUSTED-KEY-2> \
  <TRUSTED-KEY-3>
do
  curl -sS -o "index-$KEY.json" "$URL$KEY/index.json"
done

jq -s '(((length / 2) | floor) + 1 | if . < 2 then 2 else . end) as $required
       | [.[].dbs[] | {height, hash}] | group_by(.height)
       | map({height: .[0].height, agreeing: ([group_by(.hash)[] | length] | max)})
       | map(select(.agreeing >= $required)) | max_by(.height)' index-*.json
```

**Testnet:**

```shell
mkdir -p ~/rskj-index/testnet && cd ~/rskj-index/testnet

URL=https://import.testnet.rskcomputing.net/dbs/testnet/

for KEY in \
  <TRUSTED-KEY-1> \
  <TRUSTED-KEY-2> \
  <TRUSTED-KEY-3>
do
  curl -sS -o "index-$KEY.json" "$URL$KEY/index.json"
done

jq -s '(((length / 2) | floor) + 1 | if . < 2 then 2 else . end) as $required
       | [.[].dbs[] | {height, hash}] | group_by(.height)
       | map({height: .[0].height, agreeing: ([group_by(.hash)[] | length] | max)})
       | map(select(.agreeing >= $required)) | max_by(.height)' index-*.json
```

This reproduces the node's own selection rule and prints the height it would choose, along with how many signers agree on it. The threshold is derived from the number of index files you fetched, so the query stays correct if you configure a different set of keys.

Each network gets its own directory because the final `jq` reads every `index-*.json` it finds. Index files are named after the signer's key, and the two networks use different keys, so running both checks in one directory leaves six files there rather than overwriting three. That breaks the query rather than giving a wrong answer: the threshold is derived from the file count, so six files require four agreeing signers, more than either network has, and the query prints `null`. With a partial mix of the two networks in one directory, it can instead return a height from the wrong network.

:::warning[The newest entry in an index is not necessarily the one you get]

Do not read the last entry of a single index and assume that is your landing height. Signers publish independently, so the most recent entries may be offered by only one of them — and those can never be selected. The query above applies the same threshold RSKj does.

:::

Once you know the height, you can check how large the download will be. Run this in the same directory, so `$URL` and the index files still refer to the network you just checked. Any of the agreeing signers' indexes will do, since they publish the same archive:

```shell
DB=$(jq -r --argjson h <HEIGHT> '.dbs[] | select(.height == $h) | .db' index-<TRUSTED-KEY>.json)
curl -fsSI "$URL$DB" | grep -i content-length
```

Use that figure to size your temporary space, as described in [Before you start](#before-you-start).

## Run the import

Run this on a node that is **not** already running, with no valuable database in place — the first thing it does is erase the database directory for the selected network.

**Mainnet:**

```shell
java -Xmx4G -cp <PATH-TO-THE-RSKJ-JAR> co.rsk.Start --import
```

**Testnet:**

```shell
java -Xmx4G -cp <PATH-TO-THE-RSKJ-JAR> co.rsk.Start --testnet --import
```

The `--import` flag sets `database.import.enabled` for that run only. It supplies no URL and no keys of its own — those come from the network configuration shipped in the JAR. `-Xmx4G` is the heap recommended for this command in [Setup node using Java](/node-operators/setup/installation/java/); left out, the JVM sizes the heap from physical RAM instead.

The import prints nothing to the console. It writes to `logs/rsk.log`, relative to the directory you ran the command from. A successful import logs there, in order:

```text
Bootstrap data downloaded
Bootstrap data hash checked
Bootstrap data extracted
Detected bootstrap-data v2 (chunked) format
Bootstrap-data v2 imported <blocks> blocks, <values> long values and <nodes> state nodes in <n> ms
Bootstrap data has successfully been imported in <n> mills
```

Other subsystems log in between; what matters is that these six appear in this order. Older bootstrap data logs `Detected bootstrap-data v1 (legacy) format` in place of the two `v2` lines.

The node then continues into normal operation and starts importing blocks from peers, beginning just above the imported height.

Both stages take time: the download depends on your connection, and the load that follows depends on the machine. Neither is instant, and the whole import is expected to take minutes rather than the hours a full sync would.

## Confirm it worked, then restart without the flag

Once you see the import succeed and blocks being processed, **stop the node and start it again without `--import`**. This is the step that turns a one-off import into a normally operating node.

**Mainnet:**

```shell
java -cp <PATH-TO-THE-RSKJ-JAR> co.rsk.Start
```

**Testnet:**

```shell
java -cp <PATH-TO-THE-RSKJ-JAR> co.rsk.Start --testnet
```

Check that it is serving and that it kept the imported data:

```shell
curl -sS http://localhost:4444 -X POST -H "Content-Type: application/json" \
  --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
```

```text
{"jsonrpc":"2.0","id":1,"result":"0x…"}
```

The result is the latest synced block in hexadecimal. If it is at or above the height you identified earlier and climbing, the import held and the node is syncing the remainder from peers. If it comes back at zero, the node did not keep the imported database — check that you started it without `--import` and against the same network.

### Clean up temporary files

RSKj leaves the downloaded archive and its extracted contents behind in the temporary directory — together, the archive plus the larger file extracted from it. They are not needed once the import has completed.

```shell
TMP=$(java -XshowSettings:properties -version 2>&1 | sed -n 's/.*java.io.tmpdir = //p')
ls -d "$TMP"/import*
```

Remove anything left there.

## Common failures

Expand the message you are seeing

| Message                                                                                                                              | What it means                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| ------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Failed to download and parse index from <url>`                                                                                      | A signer's index could not be fetched or parsed. **This stops the import even if the other signers already agree**, so it is often an outage at the publisher rather than a problem on your side. Your database has already been erased at this point. Check network access to the import URL, and that you have not overridden `database.import.url` with something unreachable. If the URL is right and reachable for the other keys, wait for that publisher to recover and retry.                                                                 |
| `Downloaded files doesn't contain enough entries for a common height`                                                                | No single height is offered, with a matching hash, by the required majority of trusted signers. This is what you see if `database.import.trusted-keys` has been narrowed below the threshold, or if the indexes have no height in common. An unreachable signer does **not** produce this message; it fails earlier, with `Failed to download and parse index from <url>`.                                                                                                                                                                            |
| `Not enough valid signatures: selected height <n> doesn't have enough trustworthy sources: <x> of <y>`                               | A height looked agreed-upon, but too few signatures actually verified against the trusted keys. The bootstrap data is not trustworthy. Do not work around this by lowering the requirement.                                                                                                                                                                                                                                                                                                                                                           |
| `Failed to create a temporary directory. Please start again the import process`                                                      | RSKj could not create its working directory under the JVM's temporary directory. This happens before anything is downloaded. Check that `java.io.tmpdir` exists and is writable by the user running the node — a read-only or otherwise restricted temporary directory is the usual cause.                                                                                                                                                                                                                                                            |
| `File: <path> does not match with expected hash: <hash>`                                                                             | The downloaded archive does not match the hash the signers committed to. Usually an incomplete or corrupted download; remove the temporary files and retry.                                                                                                                                                                                                                                                                                                                                                                                           |
| `Error downloading bootstrap data from <url>. Please start again the import process`                                                 | The archive could not be retrieved, or could not be written to disk. Check network access, and check free space in the temporary directory — a download that fills the filesystem fails here. If it persists, the published archive may be missing from the location its index advertises.                                                                                                                                                                                                                                                            |
| `The file is corrupted or incomplete. Please start again the import process`                                                         | The archive downloaded and matched its hash, but could not be unpacked. Check free space in the temporary directory first: running out during extraction produces this message even though the archive itself is fine. If space is adequate and it persists, the published archive is faulty — report it rather than working around it.                                                                                                                                                                                                               |
| `Error trying to read bootstrap data contents. Please start again the import process`                                                | The downloaded archive could not be read back while verifying its hash — this happens before extraction, not after. Usually a truncated or removed temporary file, or an I/O error on the filesystem holding the temporary directory. Retry the import.                                                                                                                                                                                                                                                                                               |
| `Error reading bootstrap data from <path>` or `Error reading bootstrap-data v2 from <path>`                                          | The extracted `bootstrap-data.bin` could not be read. Check that the temporary directory still holds it and that nothing is cleaning temporary files while the node runs.                                                                                                                                                                                                                                                                                                                                                                             |
| Any message starting `Bootstrap-data v2 ` (bad magic, unsupported version, chunk length, section order, truncated or trailing bytes) | The extracted file is not intact. Check first that you are on `VETIVER-9.0.4` or later, then delete the temporary files and run the import again. If it repeats, the published archive is faulty — report it rather than working around it.                                                                                                                                                                                                                                                                                                           |
| `Configuration has less trusted sources than the minimum required <n> of 2`                                                          | Fewer than two trusted keys are configured, and two is the floor however few you configure. Restore the shipped keys for the network. This is a warning at startup, not the failure itself — the run continues and then fails on one of the messages above.                                                                                                                                                                                                                                                                                           |
| `java.lang.OutOfMemoryError`                                                                                                         | Note this one appears **on the terminal, not in `logs/rsk.log`** — if the log stops after `Bootstrap data extracted`, check the terminal before assuming the node is still working. `Java heap space` means the load stage ran out of heap: raise `-Xmx` above the 4G used above and run the import again. `Required array size too large` means the node is older than `VETIVER-9.0.4` and cannot read the bootstrap data published today; raising `-Xmx` will not help, so upgrade instead. Either way, a retry downloads the bootstrap data again. |

## Migrating from LevelDB to RocksDB

If your node still runs on **LevelDB**, migrate it to **RocksDB**. LevelDB is deprecated and will be removed in a future release; a node that opens a LevelDB database logs a warning saying so on every start. RocksDB is the default for any node set up from scratch.

Changing [`keyvalue.datasource`](/node-operators/setup/configuration/reference#keyvaluedatasource) requires starting with an empty database directory, because an existing database cannot be reopened under a different engine. Running with `--import` gets you there, which is why import sync is described as a way to switch:

```shell
java -Xmx4G -Dkeyvalue.datasource=rocksdb -cp <PATH-TO-THE-RSKJ-JAR> co.rsk.Start --import
```

Import sync is not a database conversion, though. It **discards your current database** and replaces it with the published bootstrap data, so a node that had synced to the tip comes back at the imported height and has to sync the difference again. If you would rather keep the history you already have, convert the database in place with [`DbMigrate`](/node-operators/setup/configuration/cli/#dbmigrate) instead — it downloads nothing.

The one-shot rule still applies. Use `--import` on the command line for the switchover, and do not leave import enabled in configuration afterwards.

## Docker and the Ubuntu package

Import sync works under both, but neither runs the node the way the procedure above assumes. That procedure ends with *stop the node and start it again without the flag* — and these two restart the node for you.

So run the import as a **one-off invocation**, and keep `--import` out of anything that restarts: a `docker run` in a compose file, a `systemd` unit, or `database.import.enabled = true` in a configuration file will erase and re-download the database on every restart and every reboot, indefinitely.

### Docker

The image passes its arguments through to the node, so `--import` can be given to `docker run`. Two things differ from the JAR:

- **The volume has to be writable by the node.** The node runs as the unprivileged `rsk` user, and `/var/lib/rsk/.rsk` does not exist in the image — so Docker creates the mount point owned by `root`, and the import fails on its first write with `dbKind.properties (No such file or directory)`. Create the volume and hand it to `rsk` once, before importing.
- **Set a maximum heap.** The image sets `DEFAULT_JVM_OPTS="-Xms4G"`, which is the *initial* heap, not a ceiling. Without `-Xmx` the JVM still sizes the maximum from the memory the container is given, so set both.

```shell
docker volume create rsk-data
docker run --rm -u 0 -v rsk-data:/var/lib/rsk/.rsk \
  --entrypoint chown rsksmart/rskj:latest -R rsk:rsk /var/lib/rsk/.rsk
```

Then import, mounting that volume at the `rsk` user's home so the database lands at `/var/lib/rsk/.rsk/<network>/database`:

**Mainnet:**

```shell
docker run --rm -v rsk-data:/var/lib/rsk/.rsk \
  -e DEFAULT_JVM_OPTS="-Xms4G -Xmx4G" \
  rsksmart/rskj:latest --import
```

**Testnet:**

```shell
docker run --rm -v rsk-data:/var/lib/rsk/.rsk \
  -e DEFAULT_JVM_OPTS="-Xms4G -Xmx4G" \
  rsksmart/rskj:latest --testnet --import
```

The container does not exit when the import finishes — the node carries on into normal operation. Watch for `Bootstrap data has successfully been imported` in the logs, stop the container, then start your usual container against the same volume **without** `--import`.

The archive is downloaded and unpacked inside the container, under `/tmp`, which is the container's own writable layer rather than the volume. Allow room for the archive and the larger file extracted from it at the same time, on top of the space the database needs in the volume.

### Ubuntu package

The package installs the JAR at `/usr/share/rsk/rsk.jar` and its configuration in `/etc/rsk`, and runs the node as the `rsk` user. Stop the service, run the same JAR once with `--import`, then start the service again:

```shell
sudo service rsk stop
sudo -u rsk java -Xmx4G -Dlogback.configurationFile=/etc/rsk/logback.xml -cp /usr/share/rsk/rsk.jar co.rsk.Start --import
sudo service rsk start
```

Three details matter here:

- **Run it as the `rsk` user**, as above. The service runs as `rsk`, so an import run as `root` or as your own account leaves behind a database the service cannot read.
- **There is no configuration flag to pass, and no network flag either.** RSKj reads `/etc/rsk/node.conf` on its own when that file exists, exactly as the service does. That file is a symlink to the network you chose at installation, and it sets the database location — `/var/lib/rsk/database/<network>`. See [switching networks](/node-operators/setup/configuration/switch-network) if you need to change it.
- **Stop the import once it reports success.** As with Docker, the node continues into normal operation rather than exiting; interrupt it after `Bootstrap data has successfully been imported`, then hand the node back to the service. The `-Dlogback.configurationFile` option above is what the service uses, so the import logs to `/var/log/rsk/rsk.log`. Without it the log goes to `./logs/rsk.log` in whatever directory you ran the command from.

## Related

- [Setup node using Java](/node-operators/setup/installation/java/)
- [`database.import` configuration reference](/node-operators/setup/configuration/reference#databaseimport)
- [CLI flags](/node-operators/setup/configuration/cli/)
- [Minimum hardware requirements](/node-operators/setup/requirements/)
