v2 → v3 Offline Migration
Follow a single-node cluster example through cold backup, planning, migration, startup, acceptance, and cutover.
This walkthrough migrates users, credentials, conversations, and messages from original v2 into a fresh v3 single-node cluster. Follow the steps in order: back up → write a plan → migrate → start and test → cut over.
For multi-node clusters, use the three-node plan in the migration reference. Include every source node and run the migration centrally on one machine; do not take just one node's data from a multi-node cluster.
The environment for this example
This example uses a separate Linux migration machine that also runs v3 after migration. It excludes plugins and external integrations. Rehearse in isolation first.
| Item | Example value |
|---|---|
| Original v2 | Original v2.2.5-20260422, single-node cluster, node ID 1001 |
| Original business DB shard count | 8; replace with the actual value from the old configuration |
| Stopped v2 server | SSH hostname v2-a, complete data directory /opt/wukongim/data |
| Cold backup on the migration machine | /srv/v2-snapshots/node1001/data |
| Migration working directory | /srv/wkmigrate; must be a new directory |
| Target v3 | Node ID 1, RPC address 127.0.0.1:7001 |
| Target data directory | /srv/wkmigrate/targets/node1; created by the migration tool |
Replace the v2 hostname, original data path, node ID, and shard count with your actual values. You can keep the remaining example paths. Run the commands in a Bash terminal on the migration machine with permission to write under /srv.
Confirm three things before starting:
- All sources are stopped and the target is a fresh empty cluster. v3 cannot open v2 data directories directly, and online incremental migration is not supported.
- Follow wkcli installation to obtain matching
wkcliandwukongimbinaries and verify the delivered checksums. The current documentation version isv3.0.0-beta.21. For other v2 versions, custom builds, plugins, or external integrations, read the compatibility requirements first. - Allow disk space for complete cold backups, workspaces, the archive, target data, and pre-start snapshots. Rehearse to measure peak space and total duration before scheduling the production maintenance window.
1. Take a complete cold backup
On the original deployment, stop business write ingress, wait for log application, topology changes, and notification queues to drain, then stop v2 gracefully and disable automatic restarts. Preserve the old binaries, configuration, and environment variables. The directory copied below must no longer be changing. Do not copy a running database.
On the migration machine, run:
umask 077
mkdir -p /srv/v2-snapshots/node1001/data
rsync -a --numeric-ids --partial \
root@v2-a:/opt/wukongim/data/ /srv/v2-snapshots/node1001/data/After copying succeeds, verify the copy:
rsync -anc --numeric-ids --delete --itemize-changes \
root@v2-a:/opt/wukongim/data/ /srv/v2-snapshots/node1001/data/Pass condition: exit code 0 and no file differences. Here, -n makes verification a dry run that deletes nothing. Keep the trailing /; include the whole directory and hidden files. If you already have a complete cold backup, copy from that backup without restarting v2.
You can restore original v2 service after taking the rehearsal backup. For production cutover, stop writes and all sources again, take a fresh backup, and repeat the remaining steps in new working directories. Writes made during rehearsal do not automatically enter the earlier migration output.
2. Save the migration plan
First check the tools:
wkcli version --output json
wukongim version --output jsonBoth binaries must have identical version, commit, and build_source values. Then create the working directory. If /srv/wkmigrate already exists, choose a new path and update the following paths consistently; do not overwrite an earlier attempt.
mkdir /srv/wkmigrate && mkdir -p /srv/wkmigrate/{reports,work,targets}Save this as /srv/wkmigrate/plan.json, updating the source node, shard count, and creation timestamp for this attempt:
{
"version": 1,
"source_commit": "a888f89533d0e7d1b2030e06504ca97f1ad891d4",
"sources": [
{"node_id": 1001, "data_dir": "/srv/v2-snapshots/node1001/data", "shard_count": 8}
],
"target": {
"cluster_id": "migration-v3-new",
"created_at": "2026-09-12T00:00:00Z",
"slot_count": 12,
"hash_slot_count": 256,
"replicas": 1,
"channel_replicas": 1,
"nodes": [
{"node_id": 1, "addr": "127.0.0.1:7001", "data_dir": "/srv/wkmigrate/targets/node1"}
]
}
}source_commit selects the supported v2 reader schema. Keep this value; it is not a guessed image version. The target uses 12 physical Slots, 256 hash slots, and one replica. All paths are absolute paths on the migration machine and must not contain one another. Do not create targets/node1 beforehand.
This plan does not enable deduplication, CMD/stream exclusion, or sequence renumbering. If compatibility checks block migration, use the troubleshooting section below rather than applying data-discard policies just to pass validation.
3. Run migration and reach offline_verified
Keep v3 stopped. Run this command block once on the migration machine. A failed stage stops the block immediately; reports are saved under reports.
(
set -e
umask 077
wkcli migrate prepare --plan /srv/wkmigrate/plan.json \
--workspace /srv/wkmigrate/work/prepare \
> /srv/wkmigrate/reports/prepare.json 2> /srv/wkmigrate/reports/prepare.stderr
wkcli migrate export --plan /srv/wkmigrate/plan.json \
--workspace /srv/wkmigrate/work/prepare --archive /srv/wkmigrate/source-archive \
> /srv/wkmigrate/reports/export.json 2> /srv/wkmigrate/reports/export.stderr
wkcli migrate import --plan /srv/wkmigrate/plan.json \
--workspace /srv/wkmigrate/work/import --archive /srv/wkmigrate/source-archive \
> /srv/wkmigrate/reports/import.json 2> /srv/wkmigrate/reports/import.stderr
wkcli migrate verify --plan /srv/wkmigrate/plan.json \
--workspace /srv/wkmigrate/work/verify --archive /srv/wkmigrate/source-archive \
> /srv/wkmigrate/reports/verify.json 2> /srv/wkmigrate/reports/verify.stderr
cat /srv/wkmigrate/reports/verify.json
)| Stage | Purpose | Pass condition |
|---|---|---|
prepare | Check cold backups and prepare conversion | Exit code 0, report says prepared |
export | Seal original data into an archive | Exit code 0, archive contains COMPLETE |
import | Generate v3 data directories | Exit code 0, report says imported |
verify | Independently compare target data against the original archive | Exit code 0, report says offline_verified |
cutover_ready: false is expected. Offline data verification has passed; runtime and business acceptance still follow. Preserve the cold backups, archive, plan, and all reports.
4. Start v3 and test real business behavior
First startup changes the databases. Save a snapshot of the verified targets before they have ever started. In this example, check that the snapshot path does not exist, then run:
test ! -e /srv/wkmigrate/targets-before-start && \
cp -a /srv/wkmigrate/targets /srv/wkmigrate/targets-before-start && \
wukongim config init --config /srv/wkmigrate/wukongim.tomlConfirm the snapshot copy succeeded, and save the administrator password displayed during initialization. Edit the generated wukongim.toml, changing these two values while preserving all other settings and generated secrets:
| Configuration location | Change to |
|---|---|
data_dir under [node] | "/srv/wkmigrate/targets/node1" |
id under [cluster] | "migration-v3-new" |
Check that generated values match the plan: node.id=1, cluster.listen_addr="127.0.0.1:7001", cluster.nodes=[{id=1,addr="127.0.0.1:7001"}], initial_slot_count=12, hash_slot_count=256, slot_replica_n=1, and channel_replica_n=1. Keep gateway.token_auth_on=true. If original v2 used whitelistOffOfPerson=false, also set message.person_whitelist_enabled=true.
Confirm that no leftover WK_ environment variables override these values, then validate and start in the foreground:
wukongim config validate --config /srv/wkmigrate/wukongim.toml && \
wukongim -config /srv/wkmigrate/wukongim.tomlKeep that terminal open. In another terminal on the migration machine, check:
curl --fail http://127.0.0.1:5001/readyzAfter /readyz succeeds, test these four areas with your actual client or the Chat Demo. Default listeners use loopback addresses; for remote access, use SSH forwarding as described in Linux deployment.
- Login works: use the original UID, Token, and matching
device_flag. Incorrect tokens must be rejected. Do not register again and overwrite original credentials. - Old data matches: check early, recent, and cross-page person/group history, including payloads, message IDs, sequences, conversation lists, and read/unread state.
- Messaging continues: send new messages on isolated rehearsal data. Check receipt, unread counts, retry deduplication, and new sequences greater than the channel's current tail.
- Data survives restart: stop v3 gracefully, start it again, and recheck history, new messages, and unread state.
Write acceptance in this example is for rehearsal. Production migration also requires acceptance, but run write tests on isolated copies and keep production targets free of business writes until cutover. Started directories cannot be verified as the initial import state or overwritten by another import.
5. Cut over production
After rehearsal, repeat migration during the production maintenance window using fresh complete cold backups and new working directories. Complete offline verification and runtime/client acceptance. Keep original v2 writes stopped. Run v3 under a service manager following Linux deployment, confirming it uses the verified configuration and the same data directory. Then point business backend and client connection/routing endpoints to v3 and gradually restore traffic.
If you enabled sequence renumbering from the reference, also handle client history caches and sync cursors during cutover. Preserve login credentials and handle unsent messages and drafts first. The tool does not clear client caches automatically.
v2 and v3 must never accept writes for the same business simultaneously. Before v3 accepts new production writes, you can close v3 ingress and restore original v2 data, binaries, and routing using the rehearsed procedure. Afterward, switching directly back to old v2 would lose new data. The tool has no reverse incremental v3 → v2 migration.
If a step fails
For example, if prepare fails, read these two files first:
cat /srv/wkmigrate/reports/prepare.stderr
cat /srv/wkmigrate/reports/prepare.jsonUse the corresponding filenames for other stages. Preserve the failed attempt and logs. Do not rerun the entire block and overwrite reports, delete source data, or alter markers to force a pass.
| Situation | Next action |
|---|---|
| Target directory already exists | Check whether it belongs to an earlier migration. Use a new path for a first attempt; follow the reference for retries |
| Plugin, duplicate message, conversation, or replica conflicts | Read the report and resolve each issue using the migration reference |
No output for a long time, or context canceled | Check stage progress in .stderr, process status, disk space, and outer timeouts. Do not start a second concurrent migration |
| Multi-node, Docker, or deployment to other servers | Use the plans, directory distribution, and startup configuration in the migration reference |
The migration reference contains full retry conditions, plugin policies, data transformations, and business acceptance checks. Consult it for the situation you encounter.