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
-
Check the node's current state and write down how many validators it has:
docker exec -it wf /opt/wf/sh/status.sh -
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 -
Pull the v2 image:
docker pull registry.waterfall.network/waterfall/protocol/docker:2-mainnet -
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 -
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:
- Stake delegation — split rewards, stake, exit and withdrawal rights between several addresses.
- Key import — bring in keys generated by
wf-cli. - Key export — write the validator list out for backup.
- Validator labels — group validators and
filter
status.shby group. - Custom ports that the helper scripts follow.
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.