Run a bulk data migration
This guide is a step-by-step operational reference for running a data migration from an Adobe Commerce PaaS or on-premises installation to Adobe Commerce as a Cloud Service using the bulk data migration tool. Actual configuration values and environment-specific details vary depending on your setup.
Before you begin, confirm that you have completed every item in the Customer readiness checklist and verified API access with the Migration service access guide.
Prerequisites
- Docker and Docker Compose must be installed on the machine where you run the migration.
- The user running the migration must have permission to execute
dockeranddocker compose(or the legacydocker-compose) commands. On Linux, the user must be in thedockergroup. On macOS and Windows, Docker Desktop must be running and accessible. The migration CLI invokes Docker repeatedly, and permission errors here block the run. - Core configuration must be consistent between source and target before you run the migration. Core configuration data, such as store settings and system configuration, is not migrated by this tool. Set it up on the target independently and align it with the source before migration.
Set up the tool package
Set up the environment for the bulk data migration:
-
Extract the contents of
ccsaas-migration-tools.tar.gz. -
Run all commands from the extracted
ccsaas-migration-toolsfolder, wherebin/consolelives. -
Ensure the folder is writable for logs, cache, Composer, and generated files.
Change ownership of all files and subfolders under that directory to the operating system user who runs the migration, so the tool can read and write consistently. For example, on Linux:
chown -R <user>:<group> <project-root>. -
Create the
.envand.my.cnffiles in the project root by copying the example files (.example.envto.envand.my.cnf.exampleto.my.cnf), and then fill in the values described in the following sections.
Example configuration files
The .example.env and .my.cnf.example files in the repository root are the starting point for your configuration. Copy each file to its working name and fill in the required values.
.example.env.envMAGENTO_CLOUD_CLI_TOKEN when id= is set in .my.cnf). Full variable list is available in the .env file..my.cnf.example.my.cnf[section] layouts for on-premises MySQL and PaaS (id=project:environment). The [section] name must match SOURCE_CONNECTION_NAME in .env. Fields include user, password, host, port, database, and id= for PaaS.Configure the environment file
The .env file in the project root is the migration and extraction configuration. It drives the CLI pipeline, including source and target URLs, OAuth, the remote CDMS connection, SaaS and IMS authentication, and other switches.
https://example.com instead of https://example.com/.Edit the .env file and set at least the following values correctly. For the full list of supported variables, refer to the inline annotations in .example.env.
SOURCE_INSTANCE_URL=https://<source-host>
SOURCE_INSTANCE_GRAPHQL_URL=https://<source-host>/graphql
SOURCE_INSTANCE_REST_URL=https://<source-host>/rest
SOURCE_INSTANCE_CONSUMER_KEY=<consumer_key>
SOURCE_INSTANCE_CONSUMER_SECRET=<consumer_secret>
SOURCE_INSTANCE_ACCESS_TOKEN=<access_token>
SOURCE_INSTANCE_ACCESS_TOKEN_SECRET=<access_token_secret>
Configure source OAuth credentials
These four values sign requests from the migration tool to the source store APIs. To obtain them, open the source Admin and go to System > Extensions > Integrations. Create or open an integration, and then copy the values into .env:
SOURCE_INSTANCE_CONSUMER_KEY=<consumer_key>
SOURCE_INSTANCE_CONSUMER_SECRET=<consumer_secret>
SOURCE_INSTANCE_ACCESS_TOKEN=<access_token>
SOURCE_INSTANCE_ACCESS_TOKEN_SECRET=<access_token_secret>
Set the Cloud CLI token
.my.cnf. If the SOURCE_CONNECTION_NAME section contains an id= line (for example, id=project:production), the source is Adobe Commerce on Cloud and MAGENTO_CLOUD_CLI_TOKEN is required. For on-premises sources with no id=, this token is not needed and tunnel setup is skipped.-
Go to
https://accounts.magento.cloudand sign in. -
Click on your profile image, and select Account Settings.
-
Go to the API Tokens section.
-
Select Create an API token, give it a descriptive name, and copy the generated token.
-
Set the token in
.env:code language-text MAGENTO_CLOUD_CLI_TOKEN=<your_magento_cloud_api_token>
Align Commerce Admin settings
Before migration, ensure the following settings are consistent between source and target.
Configure target SaaS and IMS credentials
These are the Adobe Commerce as a Cloud Service IMS and API settings for the target. You need the tenant ID, organization ID, IMS OAuth Server-to-Server credentials, and the correct IMS host for your environment. Coordinate with your Adobe team for organization, tenant, and profile access. Do not attempt to infer or estimate sensitive values.
Generate IMS credentials
Use the Adobe Developer Console. You need Developer or Admin access on the Adobe organization to create projects. A basic user login is not enough to add APIs.
-
Create a project, or open an existing one, and then select Add API.
-
Choose Adobe Commerce as a Cloud Service and continue.
-
Select OAuth Server-to-Server as the authentication type and continue.
-
Select the product profile that your Adobe team expects for this tenant, and then select Save configured API.
-
In the project sidebar, open OAuth Server-to-Server (or Credentials), and then copy the client ID and client secret into
.envasADOBE_IMS_CLIENT_IDandADOBE_IMS_CLIENT_SECRET.
The IMS token endpoint (ADOBE_IMS_URL) must match the credential’s environment.
ADOBE_IMS_URLhttps://ims-na1-stg1.adobelogin.comhttps://ims-na1.adobelogin.comna1 in these URLs represents the region where your target instance is provisioned. Replace it with the appropriate region identifier if your instance is provisioned in a different region.ADOBE_IMS_META_SCOPES must match the scopes provisioned on that credential. The .example.env file includes the full comma-separated scope string as a reference. Change it only if Adobe instructs you to.
Map Adobe I/O credentials to the environment file
In Developer Console, the OAuth Server-to-Server values are presented as a client ID and a client secret, corresponding to the following JSON structure:
{
"client_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
"client_secret": "xxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
Map them into .env (example placeholders):
TARGET_ORG_ID=<org_id>@AdobeOrg
ADOBE_IMS_URL=https://ims-na1.adobelogin.com
ADOBE_IMS_CLIENT_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxx
ADOBE_IMS_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxx
ADOBE_IMS_META_SCOPES=AdobeID,openid,additional_info.projectedProductContext
The SaaS API hosts differ between pre-production and production. TARGET_INSTANCE_REST_URL and TARGET_INSTANCE_GRAPHQL_URL must use the same Commerce API environment as your migration, either pre-production or production. Do not mix one tier with the other tier’s CDMS or tenant.
TARGET_INSTANCE_*_URLhttps://na1-sandbox.api.commerce.adobe.com/{tenantId}https://na1.api.commerce.adobe.com/{tenantId}na1 in these URLs represents the region where your target instance is provisioned. Replace it with the appropriate region identifier if your instance is provisioned in a different region.TARGET_TENANT_ID=<tenant_id>
TARGET_ORG_ID=<org_id>@AdobeOrg
ADOBE_IMS_URL=https://ims-na1.adobelogin.com
ADOBE_IMS_CLIENT_ID=<client_id>
ADOBE_IMS_CLIENT_SECRET=<client_secret>
ADOBE_IMS_META_SCOPES=AdobeID,openid,additional_info.projectedProductContext
TARGET_INSTANCE_REST_URL=https://na1-sandbox.api.commerce.adobe.com/{tenantId}
TARGET_INSTANCE_GRAPHQL_URL=https://na1-sandbox.api.commerce.adobe.com/{tenantId}/graphql
For production SaaS hosts, replace na1-sandbox with na1 in both TARGET_INSTANCE_* URLs. Use the matching ADOBE_IMS_URL for that tier, as shown in the previous table.
Set the CDMS endpoint
Point the migration tool at the CDMS API host that matches the environment you are migrating to. Set CDMS_HOST (and typically CDMS_PORT=443) in .env. Use one host, either pre-production or production, not both.
CDMS_HOSThttps://commerce-data-migration-service-preprod-external.adobe.iohttps://commerce-data-migration-service-prod-external.adobe.ioSet or uncomment the block that matches your run:
# Pre-production CDMS
CDMS_HOST=https://commerce-data-migration-service-preprod-external.adobe.io
CDMS_PORT=443
# Production CDMS (use for prod cutover only)
# CDMS_HOST=https://na1.api.commerce.adobe.com
# CDMS_PORT=443
Set the store code
STORE_CODE is the store view code used by the migration tool for source instance REST API calls, synthetic test customer creation, and data cleanup. It is also sent as the x-store-code header during the loading phase.
STORE_CODE defaults to default in .example.env. Verify that this matches your source instance’s default store view code. To check, in the source Admin go to Stores > All Stores and look at the Code column for the store view that should be used. If the code shown there is not default, update STORE_CODE in .env to match.
Configure the database connection file
The .my.cnf file supplies MySQL connection settings for the extraction side of the migration tool. Create it by copying .my.cnf.example to .my.cnf in the project root. The section name must match SOURCE_CONNECTION_NAME in .env.
For an on-premises or self-hosted source:
[<connection-name>]
user=<db_user>
password='<db_password>'
host=<db_host>
port=3306
database=<db_name>
For an Adobe Commerce on Cloud source:
[<connection-name>]
id=<project_id>:<environment>
The id= field tells the tool that the source is PaaS and triggers tunnel setup using MAGENTO_CLOUD_CLI_TOKEN. The project_id and environment values are available in the Cloud Console or through the magento-cloud project:list and magento-cloud environment:list commands.
Prepare the network and instances
HTTP Basic Auth in front of the store can block API and tool traffic. Ensure it is disabled for the source URL used by the migration, or that the tool’s paths are permitted, so REST and GraphQL requests can reach the store.
Maintain source database stability during extraction
While the tool extracts data from the source database, no other processes should write to it. Concurrent writes can result in an inconsistent snapshot.
- Stop cron on the source, and any operating-system schedulers that run
bin/magentoor other writers, for the extraction window, or ensure they cannot run during extraction. - Review other integrations, such as ERP, OMS, PIM, custom jobs, and third-party APIs that write to the same database. Pause or block writes for the extraction window so nothing mutates tables while extraction runs.
- This complements maintenance mode and tunnel or database access. Together they reduce storefront and API traffic. Cron and integrations are separate sources of writes that you must control explicitly.
Target
If the target catalog must be cleared before migration, delete products in Admin in small batches, for example 200 at a time, to avoid duplicate catalog conflicts and bulk-delete timeouts.
Build and run the migration
Work from the extracted project directory with write access.
Keep the session alive over SSH
If you connect over SSH, a dropped network can kill your shell and interrupt a long migration. The GNU screen command keeps the session alive on the server:
screen -S migration # new session named "migration"
# run ./bin/console commands here; when you want to disconnect without stopping work:
# press Ctrl+A, release, then press d # detach
screen -ls # list sessions
screen -x migration # reattach to "migration"
You can also use tmux if it is available on the server.
Build the Docker image
Build the Docker image used by bin/console, which contains PHP, the CLI, and dependencies. Run this before the first run, or after Dockerfile or base image changes.
./bin/console build
Start the backing services
Start the Docker Compose backing services for the tool, such as the local test database and, when enabled in .env, optional local services. The exact services depend on your configuration. Run this after a successful build and before the shell, migration, or phased commands.
./bin/console start
Initialize the CLI container
Start the CLI container once so the entrypoint can finish setup, such as a Composer install if needed, against your mounted project. Run this once before the first migration run in a fresh environment.
./bin/console shell
exit
Run the migration
The tool supports two migration approaches. Choose the one that fits your use case.
Single-phase migration
No maintenance mode is required on the source instance. Run the full migration pipeline with a single command:
./bin/console migration
The command runs all pipeline steps automatically, end to end, in the following order.
- Configuration check — validates environment variables and tool setup.
- Environment initialization — starts Docker services, opens cloud tunnels (if applicable), and runs unit tests.
- Integration tests and CDMS initialization — runs integration tests and initializes the CDMS API connection.
- Create migration — registers the migration with CDMS and waits for target schema analysis. The migration ID is saved to
.migration_id. - Functional tests and test data generation — runs functional tests and generates synthetic test data on the source for integrity verification (if enabled).
- Data extraction — extracts data from the source instance.
- Load to target — loads extracted data into the target Adobe Commerce as a Cloud Service instance. Staging views are cleaned up on the source and source test data is removed through REST in parallel with the load.
- Data integrity verification — triggers checksum verification and runs local API verification tests. Results are logged, and failures do not stop the pipeline.
- Test data cleanup on target — removes synthetic test data from the target instance.
- Process results — generates a migration summary and optionally downloads artifacts from storage.
Use this option when no maintenance window is required, which is typical for end-to-end dry runs, dev or sandbox environments, or any migration where the source can remain live during extraction.
Multi-phase migration with maintenance mode
Maintenance mode is required on the source instance to ensure data consistency during extraction. The migration is split into distinct phases that you must run in order.
./bin/console commands run from the migration tool project root. The bin/magento maintenance:* commands run on the source Adobe Commerce application server, through SSH to the install root, or through the Admin. The tool does not issue Magento maintenance commands on your behalf.migration:before-maintenancemigration:during-maintenancemigration:cleanup (optional)Phase 1 — Before maintenance (source is live)
Run while the source instance is live and accepting traffic. REST and GraphQL access to the source must be fully available. Do not enable maintenance mode before this phase completes.
Return to the server root and run:
./bin/console migration:before-maintenance
- Configuration check — validates environment variables and tool setup.
- Environment initialization — starts Docker services, opens PaaS cloud tunnels (if applicable), and runs unit tests.
- Integration tests and CDMS initialization — runs integration tests and initializes the CDMS API connection.
- Create migration — registers the migration with CDMS and waits for target schema analysis. The migration ID is saved to
.migration_id. - Functional tests — runs functional tests against the live source.
- Test data generation — creates synthetic test customers and orders on the source for integrity verification (if enabled).
Phase 2 — Enable maintenance mode (manual)
Enable maintenance mode on the source and pause all activities that write to or impact the database, including scheduled jobs, third-party integrations, order processing, and media asset synchronization.
On the source Commerce server (install root), run:
bin/magento maintenance:enable
Phase 3 — During maintenance (source is frozen)
Run with the source instance in maintenance mode. The source must remain frozen for the entire duration of this phase. Do not disable maintenance mode until Phase 3 completes successfully.
./bin/console migration:during-maintenance
- Cloud tunnel setup — for Adobe Commerce on Cloud source instances, reopens cloud tunnels and verifies database connectivity. Skipped automatically for on-premises instances.
- Data extraction — extracts data from the frozen source instance.
- Staging view cleanup — removes staging views from the source using a direct database connection (safe under maintenance mode).
- Load to target — loads extracted data into the target Adobe Commerce as a Cloud Service instance and waits for completion.
- Data integrity verification — triggers CDMS checksum verification and runs local API verification tests. Results are logged, and failures do not stop the pipeline.
- Test data cleanup on target — removes synthetic test data from the target instance.
- Process results — generates a migration summary and optionally downloads artifacts from storage.
Phase 4 — Disable maintenance mode (manual, conditional)
This phase disables maintenance mode, re-enabling traffic to the source instance. This step is required before running the cleanup phase, because cleanup communicates with the source through REST and fails with HTTP 503 if maintenance mode is still active.
On the source Commerce server, run:
bin/magento maintenance:disable
Phase 5 — Cleanup (optional, source must be live)
Remove the synthetic test customers and orders created in Phase 1 from the source instance through REST. This phase can run only after maintenance mode is disabled.
SKIP_TEST_DATA_CREATION=true is set in .env, because no test data was created.Return to the server root and run:
./bin/console migration:cleanup
- Database connection setup — for Adobe Commerce on Cloud source instances, reopens cloud tunnels. For on-premises instances, establishes and verifies direct database connectivity.
- Source REST cleanup — removes synthetic test customers and orders from the source through the REST API.
Resume or re-run a migration
The migration tool tracks progress using a .migration_id file in the project root. This file is created automatically when a new migration starts and records the current migration identifier.
Resume after a failure
If a migration run fails or is interrupted, re-run the same command to resume from the last successful step (extraction, loading, or verification) rather than restarting from scratch. Already-completed steps are skipped automatically.
migration:during-maintenance phase, the source must remain in maintenance mode throughout. If the source was taken out of maintenance or data changed between runs, the resumed migration can produce inconsistent results.Start a fresh migration
To discard a previous run and start a completely new migration, delete the .migration_id file before you start your next migration:
rm .migration_id
If .migration_id exists and the previous migration already completed, the tool prints a message saying the migration is already done and advises you to delete the file.
Review logs and debug
All migration logs are written to the logs/ directory in the project root and are organized into timestamped subdirectories:
logs/
2026-03-23_14-30-00/ ← one directory per run
index.log ← main pipeline log (start here)
...
index.logis the main pipeline orchestration log. If a step failed, it shows which script exited with a non-zero code and why.- Per-step logs, such as
09b_run_load.logand11_verify_data_integrity_local.log, contain detailed output for each phase.