Skip to content

Migrate a Docker node from v1 to v2

This page is for node operators who already run a Waterfall Mainnet node on the first-generation Docker image (registry.waterfall.network/waterfall/protocol/docker:mainnet) and want to move to the second-generation image (registry.waterfall.network/waterfall/protocol/docker:2-mainnet).

The migration keeps your validators and your synchronized database. You do not need to re-sync from a snapshot and you do not need to exit and re-activate your validators.

If you are setting up a new node instead, go straight to Run Waterfall Node Mainnet in Docker (v2).

Am I running v1?

Run this against your container:

docker exec -it wf ls /opt/wf/sh

If the listing contains import.sh, export.sh and label.sh, you are already on v2 and nothing needs to be done. If those files are missing, you are on v1.

What changes

v1 (:mainnet) v2 (:2-mainnet)
Volumes three: logs, gwat, coordinator one: the whole data directory
Validator list coordinator/validator_keys/deposit_data.json config/config.json
Stake delegation not supported supported
Key import/export not supported import.sh, export.sh
Validator labels not supported label.sh
Custom ports partially — helper scripts still assume the defaults fully — the scripts read the ports back out of the environment
status.sh node and validator basics adds client versions, chain heads, labels, delegation rules and an on-chain comparison
restore.sh restore.sh "<mnemonic>" restore.sh "<mnemonic>" <count>
Container lifetime --rm: the container is discarded on every stop --restart unless-stopped: it survives stops and reboots

The blockchain database format is unchanged, which is why the migration is an image swap rather than a re-sync.

Before you start

Have your mnemonic phrase

The mnemonic you saved when you first ran add.sh new-mnemonic is the only way to recover your validators if something goes wrong. Confirm you still have it before touching the node.

Note how your current node is started. The v1 instructions used three volumes:

-v $PWD/.wf/logs:/opt/wf/data/logs -v $PWD/.wf/gwat:/opt/wf/data/gwat -v $PWD/.wf/coordinator:/opt/wf/data/coordinator

All three live under ~/.wf, so v2's single volume covers exactly the same data. If you mounted them from somewhere else, collect them under one directory before you continue — v2 needs gwat, coordinator and the new config directory to sit side by side in the same volume.

Migration

  1. Check the node's current state and write down how many validators it has:

    docker exec -it wf /opt/wf/sh/status.sh
    

  2. Stop the node and remove the container. Your data lives in the volumes, not in the container, so nothing is lost — removing it is what lets step 4 start a new one from the v2 image.

    docker stop wf
    docker rm wf
    

  3. Pull the v2 image:

    docker pull registry.waterfall.network/waterfall/protocol/docker:2-mainnet
    

  4. Start the node again, this time with one volume and the v2 image:

    cd ~
    
    docker run --platform linux/amd64 --name wf -d --restart unless-stopped -p 4000:4000 -p 13000:13000 -p 12000:12000/udp -p 30303:30303 -p 30303:30303/udp -p 9545:9545 -p 9546:9546 -v $PWD/.wf:/opt/wf/data registry.waterfall.network/waterfall/protocol/docker:2-mainnet
    

  5. Wait about a minute, then check the status:

    docker exec -it wf /opt/wf/sh/status.sh
    

Your validators should be listed exactly as before, now with the additional v2 fields:

Main:
  Node ID: 4b5f2d921489551941b5e48a370ef858957a64e698c14b06036f27774cbc8283
  The node is synchronized.
  Coordinator version: c89c4f77
  Verifier version: 5307e7d1
  Coordinator peers: 18
  Verifier peers: 17
  Coordinator head: 11550210
  Verifier head: 11550208

Validator #0:
  Label: null
  Coordinator public key: 0x815a8c79297dc73338a7d4f3d2a1f447e019b8050735bc95331993b743ce815ad1a57a6fbdaa77efcfa99f08e3d08096
  Verifier address: 0x791e365e05abca9ca08647287c813dd2355c889e

  Can Withdraw: 0x30c35895fe0f7768a261b5326e4332cbb4556ba3
  Receives rewards: 0x30c35895fe0f7768a261b5326e4332cbb4556ba3=100%
  Can Exit: 0x30c35895fe0f7768a261b5326e4332cbb4556ba3
  Receives stake: 0x30c35895fe0f7768a261b5326e4332cbb4556ba3=100%
  Compare with state: true

  Coordinator status: active_ongoing
  Stake: 32000.000000000 WATER
  Coordinator balance: 32009.661205673 WATER

  Verifier status: active
  Verifier balance: 0.07782009910185 WATER

That is the whole migration. The validator indexes, the coordinator public keys and the verifier addresses do not change.

What happens automatically

On the first start, the v2 image copies your validator list from the v1 location into the new one:

coordinator/validator_keys/deposit_data.json  →  config/config.json

The original file is left in place untouched. A new config directory appears in your data directory next to gwat and coordinator; from now on it holds the validator list, your delegation rule files, and automatic backups named config_bak_<timestamp>_<pid>.json (the ten most recent are kept).

Your coordinator wallet, your verifier keystore and the password files are used as they are — nothing is re-imported and no keys are regenerated.

The Node ID changes

status.sh will report a different Node ID than before. This is normal and unrelated to the migration: both v1 and v2 regenerate the node key on every start.

After migrating

Commands whose arguments changed

restore.sh now requires the number of validators to restore:

# v1
docker exec -it wf /opt/wf/sh/restore.sh "<mnemonic>"

# v2
docker exec -it wf /opt/wf/sh/restore.sh "<mnemonic>" <count>

Without the count the command stops with Please provide the count of validators to restore.

Everything else keeps the same arguments: add.sh, deposit.sh, status.sh, withdraw.sh, transfer.sh, exit.sh and rm.sh are called exactly as before. add.sh and status.sh accept new optional arguments, but the old forms still work.

rm.sh now clears the validator list too

In v2, rm.sh also removes config/config.json and its backups, because that is where the validator list now lives. Your own files in ~/.wf/config — delegation rule files and imported key bundles — are left alone.

New capabilities

With v2 in place you can now use:

Delegation rules apply only to validators created after the migration — they are part of the deposit transaction, so an already-activated validator cannot be given rules retroactively.

Rolling back

If you need to go back to v1, stop and remove the container, then start it again with the v1 image and the three original volumes:

docker stop wf
docker rm wf
cd ~
docker run --platform linux/amd64 --name wf -d --restart unless-stopped -p 4000:4000 -p 13000:13000 -p 12000:12000/udp -p 30303:30303 -p 30303:30303/udp -p 9545:9545 -p 9546:9546 -v $PWD/.wf/logs:/opt/wf/data/logs -v $PWD/.wf/gwat:/opt/wf/data/gwat -v $PWD/.wf/coordinator:/opt/wf/data/coordinator registry.waterfall.network/waterfall/protocol/docker:mainnet

The database and the validators you had before the migration are unaffected — v2 does not modify coordinator/validator_keys/deposit_data.json.

Validators added while on v2 do not roll back

v2 records new validators in config/config.json only; it never writes back to the v1 file. A validator you created with add.sh or import.sh after migrating will not be visible to v1, even though its keys are in the wallet. If you have added validators, use restore.sh "<mnemonic>" <count> on v2 rather than rolling back.

Troubleshooting

status.sh says Keys weren't generated after the upgrade. The v2 image did not find coordinator/validator_keys/deposit_data.json. This almost always means the volume is mounted at the wrong place. Check that the single volume points at the directory that contains gwat and coordinator:

ls ~/.wf
coordinator gwat logs

Stop the container, correct the -v argument, and start it again. No data is lost by a wrong mount — nothing has been written yet.

coordinator-validator shows FATAL in docker logs. Expected while the node has no validator keys. Once config/config.json is in place the process starts on the next add.sh, import.sh or node restart.

The node starts syncing from scratch. The gwat and coordinator directories are not visible inside the container. Same cause as above — check the volume path before letting it run.