Your three points are essentially correct, with two important additions and one ordering fix.
1. coinbase.txt — you are right, nothing to back up.
It is a write-only artifact. Nothing in XDPoSChain or XinFin-Node ever reads it; it is only
written by mainnet/start-node.sh, and only on first initialisation (the if [ ! -d
/work/xdcchain/XDC/chaindata ] branch). On every restart the else branch runs and the file is
never touched again.
Why it is empty: the script extracts the address with XDC account new ... | awk -F '[{}]' '{print $2}'. Since XDPoSChain v2.5.0 the command prints Public address of the key: 0x…without curly braces (older versions printed Address: {xdc…}, which is why you used to see content). So the awk gets an empty string and an
empty line is written. It is a cosmetic script bug, not a missing key.
The coinbase/signer is derived at runtime from the keystore, never from that file: Etherbase() falls back to the first account in the keystore (eth/backend.go), and StartStaking() does XDPoS.Authorize(eb, wallet.SignHash) — if the key were missing you would
get signer missing and the node would not seal at all.
Practical note: because of the same awk bug, on a freshly initialised node the --unlock value
is empty and empty entries are skipped, so the account is not unlocked on the very first run.
Restart the container once after a fresh init.
Two things I would still keep: the address itself (it is in the keystore file name UTC--…--<address>, or XDC account list --datadir /work/xdcchain, or eth.coinbase) — you
need it for KYC/masternode registration and for post-migration verification.
2. The keystore file — critical, but not sufficient on its own.
a) Also migrate the password file.mainnet/.pwd on the host is mounted into the container
as /work/.pwd and is used for --password. A fresh clone ships it empty. If your key was
created with a non-empty password, the new server must have the same .pwd, otherwise the
account stays locked and the node cannot sign. Back up: xdcchain/keystore/UTC--*and mainnet/.pwd.
b) Make sure there is exactly one keystore file. The start script unlocks XDC account list | head -n 1 and the node picks the etherbase from the first account in the
keystore. If you let the new server generate its own key first and then add yours, you end up
with two and the node may choose the un-registered one. Best practice: copy your keystore + .pwdbefore the first docker compose up. If it already generated one, delete the
auto-generated UTC--… file, then restart.
3. .env — yes, back it up.
It holds no secret material and can be rebuilt with ./start-wizard.sh mainnet, but INSTANCE_NAME / CONTACT_DETAILS are what appear on the stats page, so restoring it saves
time.
One correction to the procedure
Stop the old node before the new one starts with the same key. Two nodes signing/voting with
the same key at the same time produce duplicate signatures; XDPoS v2 has a forensics path that
detects exactly this, and penalties are recorded in the checkpoint headers (the address then
shows up as SLASHED). Recommended order:
cd XinFin-Node/mainnet && docker compose down on the old server (and keep it down).
On the new server: install, cp env.example .env, fill it in, then place your UTC--… in
xdcchain/keystore/ and your .pwd in mainnet/ — before the first start.
docker compose up -d, let it sync. (Optional speed-up: instead of re-syncing, copy the whole
xdcchain directory from the stopped old node — that also keeps XDC/nodekey, though the p2p
key is not part of your masternode identity.)
Verify:
./xdc-attach.sh
> eth.coinbase // must be your xdc… address
> eth.getCandidateStatus(eth.coinbase, "latest") // expect { status: "MASTERNODE", … }
> XDPoS.GetSigners() // your address should be listed
> net.peerCount / eth.blockNumber // peers > 0, height advancing
Only after the new node is confirmed signing should you touch anything else on the old box.
Note that the masternode stake and the KYC live on chain and are bound to the address, not to
the server, so nothing needs to be redone as long as the address is unchanged.
So, in short: must back up = keystore UTC--… + .pwd; should back up = .env + a
record of your coinbase address; not needed = coinbase.txt, chaindata, nodekey.
Thank you very much for your detailed and helpful explanation.
Before asking this question, I had already tested the migration process about four times using my own procedure, and each time the migration completed successfully and the node continued receiving rewards without any issues.
That said, I’m very glad I was able to confirm my understanding and the migration process again here, especially regarding coinbase.txt, the keystore directory, and the .pwd file, as well as the importance of fully stopping the old node before starting the new one.
Your explanation has helped me feel even more confident about the process. I really appreciate you taking the time to explain everything so carefully.
Thank you again for your support, Daniel.
For further actions, you may consider blocking this person and/or reporting abuse
Your three points are essentially correct, with two important additions and one ordering fix.
1. coinbase.txt — you are right, nothing to back up.
It is a write-only artifact. Nothing in XDPoSChain or XinFin-Node ever reads it; it is only
written by
mainnet/start-node.sh, and only on first initialisation (theif [ ! -dbranch). On every restart the/work/xdcchain/XDC/chaindata ]
elsebranch runs and the file isnever touched again.
Why it is empty: the script extracts the address with
XDC account new ... | awk -F '[{}]' '{print $2}'. Since XDPoSChain v2.5.0 the command printsPublic address of the key: 0x…without curly braces (older versions printedAddress: {xdc…}, which is why you used to see content). So the awk gets an empty string and anempty line is written. It is a cosmetic script bug, not a missing key.
The coinbase/signer is derived at runtime from the keystore, never from that file:
Etherbase()falls back to the first account in the keystore (eth/backend.go), andStartStaking()doesXDPoS.Authorize(eb, wallet.SignHash)— if the key were missing you wouldget
signer missingand the node would not seal at all.Practical note: because of the same awk bug, on a freshly initialised node the
--unlockvalueis empty and empty entries are skipped, so the account is not unlocked on the very first run.
Restart the container once after a fresh init.
Two things I would still keep: the address itself (it is in the keystore file name
UTC--…--<address>, orXDC account list --datadir /work/xdcchain, oreth.coinbase) — youneed it for KYC/masternode registration and for post-migration verification.
2. The keystore file — critical, but not sufficient on its own.
a) Also migrate the password file.
mainnet/.pwdon the host is mounted into the containeras
/work/.pwdand is used for--password. A fresh clone ships it empty. If your key wascreated with a non-empty password, the new server must have the same
.pwd, otherwise theaccount stays locked and the node cannot sign. Back up:
xdcchain/keystore/UTC--*andmainnet/.pwd.b) Make sure there is exactly one keystore file. The start script unlocks
XDC account list | head -n 1and the node picks the etherbase from the first account in thekeystore. If you let the new server generate its own key first and then add yours, you end up
with two and the node may choose the un-registered one. Best practice: copy your keystore +
.pwdbefore the firstdocker compose up. If it already generated one, delete theauto-generated
UTC--…file, then restart.3. .env — yes, back it up.
It holds no secret material and can be rebuilt with
./start-wizard.sh mainnet, butINSTANCE_NAME/CONTACT_DETAILSare what appear on the stats page, so restoring it savestime.
One correction to the procedure
Stop the old node before the new one starts with the same key. Two nodes signing/voting with
the same key at the same time produce duplicate signatures; XDPoS v2 has a forensics path that
detects exactly this, and penalties are recorded in the checkpoint headers (the address then
shows up as
SLASHED). Recommended order:cd XinFin-Node/mainnet && docker compose downon the old server (and keep it down).cp env.example .env, fill it in, then place yourUTC--…inxdcchain/keystore/and your.pwdinmainnet/— before the first start.docker compose up -d, let it sync. (Optional speed-up: instead of re-syncing, copy the wholexdcchaindirectory from the stopped old node — that also keepsXDC/nodekey, though the p2p key is not part of your masternode identity.)Note that the masternode stake and the KYC live on chain and are bound to the address, not to
the server, so nothing needs to be redone as long as the address is unchanged.
So, in short: must back up = keystore
UTC--…+.pwd; should back up =.env+ arecord of your coinbase address; not needed = coinbase.txt, chaindata, nodekey.
Thank you very much for your detailed and helpful explanation.
Before asking this question, I had already tested the migration process about four times using my own procedure, and each time the migration completed successfully and the node continued receiving rewards without any issues.
That said, I’m very glad I was able to confirm my understanding and the migration process again here, especially regarding
coinbase.txt, thekeystoredirectory, and the.pwdfile, as well as the importance of fully stopping the old node before starting the new one.Your explanation has helped me feel even more confident about the process. I really appreciate you taking the time to explain everything so carefully.
Thank you again for your support, Daniel.