For the complete documentation index, see llms.txt. This page is also available as Markdown.

Installing CMA in an existing Magento project

CMA does not have to own your project from day one. If you already have a Magento 2 checkout — cloned from your team's repository, migrated from another Docker setup, or unpacked from a customer handover - you can add CMA to it and keep everything that is already there.

This page covers adding CMA to a Magento 2 project. If you are coming from our older magento-docker setup, read Converting legacy Docker setup to CMA instead - it covers the ScandiPWA-specific steps as well.

What changes when CMA manages your project

In a self-managed setup you own the infrastructure: you install and version-match every service, write the configs, pick the ports, and wire env.php by hand. CMA replaces all of that with a managed, per-project Docker stack derived from the Magento version in your composer.json. Your code, your database content, and your own scripts stay yours.

Area
You manage in a vanilla setup
CMA manages for you

Services

Install and configure nginx, PHP-FPM, a database, Redis, a search engine, a mail catcher, SSL, optionally Varnish

One start runs the full stack as containers: nginx, two PHP-FPM (one with Xdebug), MariaDB, Redis, ElasticSearch or OpenSearch, MailDev, an SSL-terminating proxy, optional Varnish

Service versions

Track Adobe's requirements matrix; upgrade each component when Magento bumps it

Every service version is pinned to your Magento version — a Magento upgrade swaps the whole stack for you

PHP

Install the right PHP and ~28 extensions per project; switch versions between projects

A prebuilt container image matching your Magento version; your host PHP is irrelevant

Ports

Hardcode host ports, resolve clashes between projects

Assigned automatically, cached per project, bound to 127.0.0.1; several CMA projects run side by side

Wiring

Hand-edit env.php hosts and base URLs after every change

env.php and the base URLs are rewritten to point at the containers on every start

Daily commands

docker exec, docker logs, mysql client incantations

npm run cli (a shell with magento, composer, mariadb aliases), exec, logs, status

Debugging

Toggle Xdebug ini and restart PHP-FPM

A second PHP-FPM container with Xdebug is always ready; enabled per request via browser cookie, or debugphp for CLI

IDE setup

Hand-written launch.json and PhpStorm config

.vscode/ and .idea/ are generated and kept in sync with the assigned ports

What CMA decides for you

The convenience comes with opinionated defaults. These are the ones an existing project is most likely to notice:

  • The database is fixed. MariaDB, database name magento, user magento/magento, root password scandipwa. None of it is configurable - relevant if your dump references another schema name. npm run status prints the credentials and ports.

  • An admin user is ensured on every start. If the user from cma.js (default admin / scandipwa123) is missing - including right after a database import — it is created.

  • Two-factor auth is disabled by adding markshust/magento2-module-disabletwofactorauth as a dev dependency - this edits your composer.json and composer.lock. Composer plugin approvals (allow-plugins) may also be written back to composer.json.

  • All outgoing mail goes to the bundled MailDev mail catcher; the SMTP settings in your database are overwritten on every start. Nothing is ever delivered for real.

  • The block_html and layout caches are disabled on every start (and full_page too while Varnish is off - which it is by default).

  • There is no cron. CMA neither installs a crontab nor runs Magento's cron. Indexers, queued emails, and every other scheduled job run only when you run bin/magento cron:run yourself, e.g. through npm run exec.

  • env.php is re-serialized when CMA rewrites it, so comments and custom formatting in that file are lost.

  • Generated service configs are disposable. They live in node_modules/.create-magento-app-cache/ and are regenerated on every start — customize them through the template options in cma.js, not by editing the generated files.

Need a start without the Magento-side changes? npm run start -- --skip-setup skips the whole setup phase — the env.php rewrite, admin user, 2FA module, cache and SMTP changes — for that run.

Before you start

Your project should be a Magento 2 root — the folder that holds composer.json, bin/magento and app/etc.

Make sure you have:

  • All the prerequisites installed for your platform (Docker, Node.js 16 or newer, and the OS packages listed there).

  • Composer credentials for repo.magento.com, either as an auth.json file in the project root or in the COMPOSER_AUTH environment variable. CMA checks for both and will prompt you if neither is present.

  • Your Magento version in the list of supported versions.

  • Any previous stack for this project stopped — if it was running under another Docker setup or native services, shut those down first so the two do not compete for containers and volumes.

1

Add CMA to the project

Point the generator at your existing project instead of a new folder name:

CMA detects that the folder is not empty and switches to adopt mode. In this mode it:

  • merges package.json — the CMA scripts and the @scandipwa/magento-scripts dependency are added, and everything you already have (your own scripts, dependencies, indentation) is kept as-is;

  • creates cma.js, AGENTS.md, CLAUDE.md and README.md only if they do not exist yet;

  • refreshes its own agent instructions under .claude/magento-scripts/ and .agents/magento-scripts/;

  • installs the Node dependencies.

At the end it prints exactly what it created, what it merged, and what it left alone. The full list of files CMA may add, with commit guidance, is in What CMA adds to your project below.

If your package.json already defines a script CMA also wants — most commonly start, used by frontend tooling — CMA does not overwrite it. It reports the clash instead, and you can reach that command through npx magento-scripts start.

Doing it by hand

If you would rather not run the generator, install the package directly:

Then add the scripts to your package.json:

2

Review cma.js

cma.js is the project's configuration file. The generator creates it from a default template — it does not inspect your project — so review it before the first start:

Three values are worth checking:

  • edition — the template defaults to community. If your composer.json requires magento/product-enterprise-edition, set it to enterprise; CMA fails fast when the two contradict.

  • adminuri — written into backend.frontName in app/etc/env.php on every start. If your project uses a custom admin path, set it here first.

  • user / password — the admin account CMA creates when missing. Change them if the defaults are not acceptable even locally.

3

First run

How you run CMA for the first time depends on whether you have a database dump.

With a database dump

Skip start and go straight to the import — import-db is self-contained: it stops running containers, assigns ports, starts the services, and waits for MariaDB itself.

Beyond importing (see Importing database for the details), it also adapts the dump for local use: it asks before dropping and recreating the magento database, removes a custom admin path in favor of adminuri from cma.js, points the search-engine configuration at the local container, and offers to strip customers, orders and admin users from the imported data. Base URLs are handled automatically — both import-db and every later start set them to match the assigned ports.

Without a dump

With an empty database, the first start installs a blank Magento.

What happens on the first start

  1. Reads your Magento version from magento/product-community-edition (or -enterprise-edition) in composer.json, falling back to composer.lock. You are not asked to pick a version.

  2. Adjusts composer.json if needed — adds the repo.magento.com repository and magento/composer-root-update-plugin when they are missing.

  3. Runs composer install when the installed state is incomplete or out of sync with composer.lock; skipped when everything already lines up.

  4. Generates service configs into node_modules/.create-magento-app-cache and starts the containers on free ports.

  5. Rewrites app/etc/env.php to point at those containers (see below).

  6. Sets up the database based on what setup:db:status reports: everything up to date — nothing runs; schema changes pending — setup:upgrade; Magento reports it is not installed at all (a non-empty database without a valid installation state) — setup:install followed by setup:upgrade.

Every start — first or not — also applies the managed defaults described in What CMA decides for you: the admin user, the 2FA module, the cache and SMTP changes, and the base URLs.

When the start finishes, CMA prints the storefront, admin and MailDev URLs. npm run status shows them again at any time, along with the database credentials; npm run cli opens a shell inside the stack with magento, composer and mariadb ready as aliases.

What happens to app/etc/env.php

If the file exists, CMA edits it in place rather than replacing it, but several keys are rewritten on every start:

Key
What CMA does

backend.frontName

set to magento.adminuri from cma.js

db.connection.default.host

pointed at the MariaDB container

session.redis.host / .port

pointed at the Redis container (when session.save is redis)

cache.frontend.default.backend_options

server and port pointed at the Redis container

http_cache_hosts

replaced with CMA's Varnish host, or removed when Varnish is disabled

cache.persisted-query

added or removed based on composer.lock

The file is re-serialized on rewrite, so comments and custom formatting in it do not survive.

What CMA adds to your project

Path
Purpose
Commit it?

cma.js

project configuration

yes

package.json

CMA scripts and dependency

yes

README.md

project readme with the CMA commands

yes

AGENTS.md, CLAUDE.md, .claude/, .agents/

AI agent instructions for the CMA commands

yes

node_modules/.create-magento-app-cache/

generated nginx / PHP / Varnish / MariaDB configs

no

.vscode/launch.json

XDebug launch configuration

your call

.idea/

PhpStorm PHP, XDebug and database settings

your call

.git/hooks/post-checkout

warns on composer.json / composer.lock changes after a branch switch

n/a

README.md, AGENTS.md, CLAUDE.md and cma.js are created only when missing; the .claude/magento-scripts/ and .agents/magento-scripts/ directories are CMA-managed and refreshed on every run. If your project uses husky or another hook manager, the git hook goes into that manager's directory instead of .git/hooks; an existing post-checkout hook is never overwritten.

Make sure node_modules is ignored in your .gitignore — that is where CMA keeps its generated service configs.

Things to watch out for

Version constraints must be exact. CMA looks up your version string against its list of supported versions. ^2.4.7 works (the caret is stripped) and 2.4.7-p3 works, but a constraint like ~2.4.7 or >=2.4.6 fails with No config found for magento version ~2.4.7. Pin an exact supported version in composer.json.

Edition mismatches abort the start. CMA fails fast when composer.json requires both editions at once, or when the edition there contradicts edition in cma.js — see Review cma.js.

A start script may already be taken. CMA never overwrites it — see the hint in Add CMA to the project.

vendor/ is validated against composer.lock. Packages that are in vendor/ but not in the lock file — a manually dropped module, for instance — make CMA treat the folder as corrupted and re-run composer install.

Last updated