Canister migration
Moving a canister to a different subnet is sometimes necessary: the canister was deployed to the wrong subnet, geographic or replication requirements have changed, or you need to consolidate canisters for efficient inter-canister calls. This guide covers both migration paths depending on whether you can accept a new canister ID.
When to migrate
Section titled “When to migrate”Consider migrating a canister when:
- Wrong subnet: the canister was deployed to an unintended subnet. See Subnet selection for how to target subnets at deployment time.
- Geographic requirements: a subnet in a specific region is now required for data residency compliance.
- Replication needs: moving to a larger subnet (such as the fiduciary subnet) for stronger fault tolerance.
- Colocation: consolidating canisters onto the same subnet to reduce inter-canister call latency.
Choosing your approach
Section titled “Choosing your approach”Your options depend on whether the canister ID can change:
| Approach | State | Canister ID | Source canister | Complexity |
|---|---|---|---|---|
| Snapshot transfer | Preserved | New ID | Retained | Moderate |
| Full migration | Preserved | Preserved | Deleted | Advanced |
Snapshot transfer is the simpler path and is appropriate when you can accept a new canister ID. Create a new canister on the desired subnet, transfer state via snapshots, and switch over. The source canister is retained and can be deleted afterward.
Full migration is required when the canister ID must be preserved. The canister ID must not change when:
- Threshold signatures (tECDSA / tSchnorr): The IC derives signing keys by cryptographically binding them to the calling canister’s principal. Any Bitcoin or Ethereum addresses derived from those keys are permanently tied to the original canister ID. Changing the ID means losing access to those signing keys and any assets they control.
- vetKeys: vetKey derivation includes the canister’s principal. A new ID produces entirely different decryption keys, making previously encrypted data permanently inaccessible.
- External references: Other canisters, frontends, or off-chain systems that reference the canister by ID will break. This includes Internet Identity: users who authenticated via a canister-ID-based domain (for example,
<canister-id>.icp0.io) will lose access to their sessions.
Migrating without preserving the canister ID
Section titled “Migrating without preserving the canister ID”Use this approach when you can accept a new canister ID. The source canister remains on its original subnet until you explicitly delete it; there is no irreversible step and no minimum cycle requirement.
1. Create a target canister
Section titled “1. Create a target canister”Create a new canister on the desired subnet. The --detached flag creates the canister without recording it in your project configuration, which is useful here since this is a temporary migration target:
icp canister create --detached -e ic --subnet <target-subnet-id>Note the canister ID printed in the output. Add --quiet to print only the ID, which is useful for scripting.
2. Transfer state via snapshots
Section titled “2. Transfer state via snapshots”See Canister snapshots for full details on resuming interrupted transfers.
# Stop and snapshot the source canistericp canister stop my-canister -e icicp canister snapshot create my-canister -e ic
# Download the snapshot locallyicp canister snapshot download my-canister <snapshot-id> -o ./migration-snapshot -e ic
# Upload and restore on the target canistericp canister snapshot upload <target-id> -i ./migration-snapshot -n icicp canister snapshot restore <target-id> <new-snapshot-id> -n ic3. Copy settings
Section titled “3. Copy settings”Snapshots capture the Wasm module and memory, but not canister settings. Check the source canister’s current settings and apply any non-default values to the target:
icp canister settings show my-canister -e ic
# Apply non-default settings to the target canistericp canister settings update <target-id> \ --compute-allocation 10 \ --freezing-threshold 604800 \ -n icRun icp canister settings update --help for a full list of available settings.
4. Switch over
Section titled “4. Switch over”Start the target canister:
icp canister start <target-id> -n icThe source canister is still stopped on its original subnet. Manage it before updating the project mapping, while my-canister still refers to it:
# Delete it if no longer neededicp canister delete my-canister -e icUpdate your project to point my-canister to the new ID. icp-cli stores canister IDs in .icp/data/mappings/<environment>.ids.json (mainnet) or .icp/cache/mappings/<environment>.ids.json (local). Edit the file:
{ "my-canister": "<target-id>"}Update any other canisters, frontends, or off-chain systems that reference the old canister ID.
Migrating with the canister ID
Section titled “Migrating with the canister ID”Use this approach when the canister ID must be preserved. This adds an ID migration step using icp canister migrate-id, which moves the canister ID from the source to the target on the new subnet.
Important:
icp canister migrate-idmoves only the canister ID. It does not transfer state, settings, or cycles. If you skip the preparation steps below, the canister’s Wasm module, memory, and stable memory will be lost. The source canister is permanently deleted and its cycles are burned when the migration completes.
How the ID migration works
Section titled “How the ID migration works”icp canister migrate-id tells the NNS migration canister to:
- Rename the target canister to have the source canister’s ID
- Update the IC routing table so the source canister ID now resolves to the target’s subnet
- Delete the source canister from its original subnet; all remaining cycles are burned
- Restore the source canister’s original controllers on the target
After this process:
- Source canister: permanently deleted; its cycles are burned and its ID now lives on the target’s subnet
- Target canister: continues on the same subnet under the source canister’s ID, with the state, cycles, and settings it had before migration (controllers are replaced by those restored from the source)
- Target canister’s original ID: ceases to exist permanently
Because the target canister’s state is what survives, you must transfer state via snapshots before running migrate-id.
1. Create a target canister
Section titled “1. Create a target canister”Create a new canister on the desired subnet:
icp canister create --detached -e ic --subnet <target-subnet-id>Note the canister ID from the output. Immediately top up the target canister with enough cycles for ongoing operation. The source canister’s cycles are burned during migration and are not transferred:
icp canister top-up <target-id> --amount 5T -n ic2. Transfer state via snapshots
Section titled “2. Transfer state via snapshots”Stop the source canister, create a snapshot, download it, upload it to the target, and restore it:
# Stop and snapshot the source canistericp canister stop my-canister -e icicp canister snapshot create my-canister -e ic
# Download the snapshot locallyicp canister snapshot download my-canister <snapshot-id> -o ./migration-snapshot -e ic
# Upload the snapshot to the target canistericp canister snapshot upload <target-id> -i ./migration-snapshot -n ic
# Restore on the target (use the new snapshot ID from the upload output)icp canister snapshot restore <target-id> <new-snapshot-id> -n icAfter restoring, the target has the same Wasm module, memory, and stable memory as the source.
Delete the snapshot on the target. The migrate-id command requires the target to have no snapshots before it will proceed:
icp canister snapshot delete <target-id> <new-snapshot-id> -n icFor large canisters, downloads and uploads may take time. If interrupted, resume with --resume. See Canister snapshots for details.
3. Copy settings
Section titled “3. Copy settings”Snapshots capture the Wasm module and memory, but not canister settings. Controllers are automatically restored from the source during ID migration, but other settings must be copied manually:
icp canister settings show my-canister -e ic
# Apply non-default settings to the target (controllers are restored automatically; do not copy them)icp canister settings update <target-id> \ --compute-allocation 10 \ --freezing-threshold 604800 \ --wasm-memory-limit 2GiB \ -n ic4. Stop the target canister
Section titled “4. Stop the target canister”Both canisters must be stopped before the ID migration. The source is already stopped from step 2, so only the target needs stopping:
icp canister stop <target-id> -n ic5. Migrate the canister ID
Section titled “5. Migrate the canister ID”Run the migration. The --replace flag accepts canister names or principals:
icp canister migrate-id my-canister --replace <target-id> -e icThe command validates prerequisites (different subnets, both stopped, sufficient cycles, no snapshots on target), asks for confirmation (skip with -y), adds the NNS migration canister as a controller of both canisters, initiates the migration, and polls for completion.
Cycles warning: The source canister requires a minimum cycle balance before migration can proceed. All remaining cycles on the source are burned when it is deleted. If the source has a large cycle balance, consider reducing it before migrating. The command warns you if the balance is high enough to warrant attention.
6. Start and verify
Section titled “6. Start and verify”Start the canister to resume operation:
icp canister start my-canister -e icVerify the canister is on the expected subnet by querying the NNS Registry canister:
icp canister call rwlgt-iiaaa-aaaaa-aaaaa-cai get_subnet_for_canister \ '(record { "principal" = opt principal "<canister-id>" })' --query -n ic7. Clean up
Section titled “7. Clean up”The NNS migration canister is added as a controller during ID migration and is not automatically removed. Remove it if you want a clean controller set:
# Check current controllersicp canister settings show my-canister -e ic
# Remove the NNS migration canistericp canister settings update my-canister --remove-controller sbzkb-zqaaa-aaaaa-aaaiq-cai -e icDelete the local snapshot directory once you have verified the migration succeeded:
rm -rf ./migration-snapshotHandling interruptions
Section titled “Handling interruptions”If the migrate-id command is interrupted or times out (the default timeout is 12 minutes), the migration continues on the network. Use --resume-watch to reconnect:
icp canister migrate-id my-canister --replace <target-id> --resume-watch -e icThis skips validation and initiation and resumes polling migration status. To exit early without waiting, use --skip-watch and then --resume-watch later to verify completion.
Troubleshooting
Section titled “Troubleshooting””Canister is not ready for migration”
Section titled “”Canister is not ready for migration””The canister has not finished preparing. Wait a few seconds and retry.
”Canisters are on the same subnet”
Section titled “”Canisters are on the same subnet””migrate-id requires canisters on different subnets. Create a new target on the desired subnet:
icp canister create --detached -e ic --subnet <target-subnet-id>“Target canister has snapshots”
Section titled ““Target canister has snapshots””Delete all snapshots on the target before running migrate-id:
icp canister snapshot list <target-id> -n icicp canister snapshot delete <target-id> <snapshot-id> -n icInsufficient cycles on source
Section titled “Insufficient cycles on source”The source canister must meet a minimum cycle balance for migration. Top it up:
icp canister top-up my-canister --amount 1T -e icMigration timed out
Section titled “Migration timed out”The 12-minute timeout does not cancel the migration. Use --resume-watch to continue monitoring:
icp canister migrate-id my-canister --replace <target-id> --resume-watch -e icNext steps
Section titled “Next steps”- Subnet selection: Choose the right subnet at deployment time to avoid needing to migrate
- Canister snapshots: Full reference for creating, downloading, uploading, and restoring snapshots
- Canister settings: Settings that snapshots do not capture and that must be copied manually
- Cycles management: Understand cycle costs before and after migration