Most Magento 2 performance posts read the same. Enable cache, install Redis, run reindex, the end. Real stores on Adobe Commerce on cloud infrastructure (opens in new tab) break in messier ways. A 40,000-SKU B2B catalog with ten websites does not behave like a 500-SKU demo, and a Pro Production cluster during a Black Friday spike does not behave like Integration on a Tuesday.
The cloud changes the game. You do not pick the web server, you do not provision Redis, and you cannot edit php.ini directly on the node. What you can do is configure the build and deploy pipeline, set environment variables in .magento.env.yaml (opens in new tab), tune Fastly, and watch what New Relic shows you. That is where every recommendation below lives.
Sources for every number and command are linked inline. Test on Integration or Staging first. Never test these on Production.
1. Know which phase your fix belongs to
The cloud build runs in three phases. Each one has a different filesystem, different access to services, and different things you should care about. The deployment process documentation (opens in new tab) is worth reading once carefully.
- Build : container is read-only, no DB, no services.
composer install --no-dev,setup:di:compile, and static content deploy run here. Anything that produces compiled artifacts belongs here. - Deploy : site is in maintenance mode, DB and services are reachable. Schema upgrades and config flushes run here. Every second you spend in this phase is downtime for shoppers.
- Post-deploy : site is live again, traffic is flowing. Cache warmup and TTFB tests live here, not in deploy.
Variables for each phase go into the matching section of .magento.env.yaml (opens in new tab):
stage:
global:
SCD_ON_DEMAND: false
build:
SCD_STRATEGY: compact
SCD_THREADS: 4
SKIP_SCD: false
deploy:
CRON_CONSUMERS_RUNNER:
cron_run: true
max_messages: 1000
post-deploy:
WARM_UP_CONCURRENCY: 4
TTFB_TESTED_PAGES:
- "index.php"
- "index.php/customer/account/create"A common mistake: moving SCD into deploy because the build was too slow. The result is longer maintenance windows on every push. Fix the build instead.
2. Static content deploy strategy: pick compact, not quick
The default SCD strategy (opens in new tab) for cloud is standard. quick skips theme fallback resolution and breaks on stores with theme inheritance. compact deduplicates symlinks across themes and produces the smallest deploy archive, which matters when your site has three themes and ten locales.
stage:
build:
SCD_STRATEGY: compact
SCD_COMPRESSION_LEVEL: 6
SCD_MAX_EXECUTION_TIME: 3600
SCD_THREADS: 4SCD_THREADS should not exceed the CPU count of your build container. On Starter it is usually 2 to 4, on Pro it is higher. The SCD strategy guide (opens in new tab) goes into the trade-offs.
While you are here, set SCD_ON_DEMAND: false in global. On-demand SCD is for dev environments and shows up as random slow first-render hits in Production if it leaks in.
3. Indexers: schedule mode, batch size, and env.php overrides
The official guidance (opens in new tab) is clear: every indexer except Customer Grid should be in Update on Schedule. Set it once in System > Tools > Index Management.
Where cloud differs is in tuning the batch sizes. You cannot just edit app/etc/env.php on a node because the deploy phase rewrites it. Instead, put the indexer config in your .magento.env.yaml deploy stage so the deploy script merges it into the final env.php:
stage:
deploy:
CRON_CONSUMERS_RUNNER:
cron_run: true
max_messages: 1000
ENV_RELATIONSHIPS:
database: database
ELASTICSUITE_CONFIGURATION:
es_client:
servers: "elasticsearch.internal:9200"For app/etc/env.php indexer batch overrides specifically, the developer docs (opens in new tab) document the structure. On a real B2B catalog with 10 websites and 40k products, halving catalog_product_price from the default 5000 to 1000 cut reindex time from about 4 hours to under 2.
<?php
return [
'indexer' => [
'batch_size' => [
'catalog_product_price' => [
'simple' => 200,
'default' => 500,
'configurable' => 666,
],
'cataloginventory_stock' => [
'simple' => 200,
],
'catalogsearch_fulltext' => [
'partial_reindex' => 100,
'mysql_get' => 500,
'elastic_save' => 500,
],
],
],
];This goes into your repo as a CONFIG__DEFAULT__INDEXER__* style variable, or you commit the indexer block as part of your shared app/etc/env.php via the Cloud configuration management (opens in new tab) workflow. If the deploy log shows “Memory size allocated for the temporary table is more than 20% of innodb_buffer_pool_size”, the batch is too big for your MySQL instance.
4. Fastly is your full page cache. Configure it properly.
On Adobe Commerce Cloud, Fastly (opens in new tab) is in front of every Production and Staging environment. Origin shielding, custom VCL, image optimization, and the WAF all run through it. Hit ratio under 80% on category and CMS pages is the canonical sign that something upstream is wrong.
A short Fastly hygiene checklist that catches most issues:
- Set
Stale While Revalidateto at least7200seconds andStale If Errorto86400. Configure in Admin > Stores > Settings > Configuration > Advanced > System > Full Page Cache > Fastly Configuration. - Enable Origin shielding (opens in new tab) and pick a POP close to your origin.
- Add custom VCL through the Fastly module rather than via the Fastly UI directly. The module stores your VCL in version control, the UI does not.
- Purge surgically.
cache:cleanon the cloud will not purge Fastly. Use surrogate keys and theStores > Configuration > Advanced > System > Full Page Cache > Fastly Configuration > Quick Purge(opens in new tab) controls instead.
For the Fastly WAF on Adobe Commerce (opens in new tab), the rule set ships with sensible defaults but blocks legitimate API traffic on some integrations. Whitelist your ERP and PIM IP ranges through the support portal before going live.
5. Redis: two services, not one
The cloud lets you provision multiple Redis instances per the Redis service configuration (opens in new tab). The Adobe-recommended pattern is one Redis for sessions, one for the default cache. Mixing them means a session write can evict block cache entries.
Define both in .magento/services.yaml:
mysql:
type: mysql:10.4
disk: 5120
redis:
type: redis:7.0
redis-session:
type: redis:7.0Connect them in .magento.app.yaml:
relationships:
database: "mysql:mysql"
redis: "redis:redis"
redis-session: "redis-session:redis"Then point Magento at the right one in .magento.env.yaml:
stage:
deploy:
REDIS_USE_SLAVE_CONNECTION: true
REDIS_BACKEND: '\Magento\Framework\Cache\Backend\RemoteSynchronizedCache'
SESSION_CONFIGURATION:
redis:
bot_first_lifetime: 60
bot_lifetime: 7200
disable_locking: 1
max_concurrency: 20
max_lifetime: 2592000
min_lifetime: 60
save: redisFor Pro Production, Adobe provisions the separate instance on request through a support ticket (opens in new tab). On Pro Integration and Starter, you do it yourself via services.yaml.
Check the live config from SSH:
magento-cloud ssh -e production "redis-cli -h redis.internal info | grep version"The Redis service docs (opens in new tab) have the full command list.
6. PHP tuning on the cloud: where to actually put settings
You cannot SSH in and edit php.ini. Edits go through one of three places:
.magento.app.yaml(opens in new tab): PHP version, extensions, runtime.php.iniat the repo root: per-environment overrides formemory_limit,realpath_cache_size, OPcache.- Environment variables in
.magento.env.yaml(opens in new tab): cloud-specific behavior.
A sane php.ini baseline for Production:
memory_limit = 2G
realpath_cache_size = 10M
realpath_cache_ttl = 7200
opcache.memory_consumption = 512
opcache.max_accelerated_files = 60000
opcache.validate_timestamps = 0
opcache.consistency_checks = 0
opcache.enable_cli = 1opcache.validate_timestamps = 0 is the one people get wrong. With it set to 1, PHP stats every file on every request. On a Magento 2 codebase that is tens of thousands of stat calls per request. Set it to 0 and rely on the post-deploy cache flush, which already runs as part of the deployment process (opens in new tab).
For xdebug on cloud, do not load it as a permanent extension. Toggle it per session with the ece-tools CLI (opens in new tab):
magento-cloud ssh -e staging "php /app/vendor/magento/ece-tools/bin/ece-tools wd:xdebug:enable"
magento-cloud ssh -e staging "php /app/vendor/magento/ece-tools/bin/ece-tools wd:xdebug:disable"The Adobe docs note that an active xdebug module, even without an active session, can add up to 30% to page response time. There is no reason for it to be loaded on Production at all.
7. MySQL: Galera, slow query log, primary keys
Pro projects run on a Galera Cluster (opens in new tab). The cluster is great for read scaling and terrible for slow writes; one slow write blocks the whole cluster’s commit phase. The database performance issues guide (opens in new tab) has the full triage workflow.
The first three things to check on any slow site:
magento-cloud ssh -e production
# inside the SSH session:
mysql -h database.internal -u user -p magento
> SHOW FULL PROCESSLIST;Anything sitting in Query state for more than two seconds is a candidate. Run EXPLAIN and EXPLAIN ANALYZE against it. On Pro you also get the Percona Toolkit (opens in new tab); the slow query log location is documented in the log locations guide (opens in new tab):
pt-query-digest --type=slowlog /var/log/mariadb/slow-queries.log | head -100Tables without a primary key are a quiet killer. InnoDB invents a hidden 6-byte global primary key for any table without one, and that global key serializes writes across all tables that share it. Find them with the query from the database performance guide (opens in new tab):
SELECT table_catalog, table_schema, table_name, engine
FROM information_schema.tables
WHERE (table_catalog, table_schema, table_name) NOT IN (
SELECT table_catalog, table_schema, table_name
FROM information_schema.table_constraints
WHERE constraint_type = 'PRIMARY KEY'
)
AND table_schema NOT IN ('information_schema', 'pg_catalog');Add the missing keys through declarative schema (opens in new tab) in your custom module’s db_schema.xml:
<constraint xsi:type="primary" referenceId="PRIMARY">
<column name="entity_id"/>
</constraint>While you are there, audit for duplicate indexes. The same database performance page has the SQL, and Pro projects can run pt-duplicate-key-checker from the Percona Toolkit. On any site that has been through two or three upgrades I usually find at least one redundant composite index sitting next to a covering one.
8. Cron on cloud: consumers runner and unlocking stuck jobs
Cron on cloud is declared in .magento.app.yaml (opens in new tab). The default cron group is fine for most sites:
crons:
cronrun:
spec: "*/1 * * * *"
cmd: "php -dmemory_limit=-1 bin/magento cron:run"The consumers runner is what actually drains the message queue. Configure it in the deploy stage of .magento.env.yaml:
stage:
deploy:
CRON_CONSUMERS_RUNNER:
cron_run: true
max_messages: 1000
consumers: []consumers: [] means run all of them. If your queue is dominated by one heavy consumer like inventory.reservations.cleanup and it blocks the others, list the consumers you want explicitly so the runner can spin separate processes per consumer.
When a deploy aborts mid-cron, the cron_schedule table can have jobs stuck in running. The ece-tools CLI (opens in new tab) ships with two commands for this:
magento-cloud ssh -e production "php vendor/magento/ece-tools/bin/ece-tools cron:unlock"
magento-cloud ssh -e production "php vendor/magento/ece-tools/bin/ece-tools cron:kill"The same reference also documents cron:disable and cron:enable, which is the safe pattern when you need to run a long manual reindex without cron stepping on it.
9. Defer everything that can be deferred
The cloud has the same set of “do this later” toggles as on-premises, configured the same way through Admin. The ones that matter most under load:
- Asynchronous email notifications (opens in new tab): Admin > Stores > Configuration > Sales > Sales Emails > General Settings. Order placement stops waiting on SMTP.
- Asynchronous order data processing (opens in new tab): Stores > Configuration > Advanced > Developer > Grid Settings > Asynchronous indexing. Keeps storefront write contention out of the order grid path during sales spikes.
- Async config save :
magento-cloud ssh -e production "bin/magento setup:config:set --config-async 1", then start thesaveConfigProcessorconsumer. If saving a system config takes 30 seconds or times out on a multi-store site, this is the fix. - Deferred stock update (opens in new tab): Stores > Configuration > Catalog > Inventory > Product Stock Options > Use Deferred Stock Update. Only safe when Backorders are on.
For cloud-specific deferred behavior, also check the global variables reference (opens in new tab) for things like CONSUMERS_WAIT_FOR_MAX_MESSAGES.
10. Post-deploy: warm the cache, test the TTFB
The post-deploy stage runs after the site is back live, so it is the right place for cache warmup. The post-deploy variables guide (opens in new tab) documents the full set. A working baseline:
stage:
post-deploy:
WARM_UP_CONCURRENCY: 4
WARM_UP_PAGES:
- "index.php"
- "category:*:1"
- "cms-page:home:*"
- "product:24-MB01:1"
TTFB_TESTED_PAGES:
- "index.php"
- "index.php/customer/account/create"
- "index.php/checkout/cart"The TTFB results go into the cloud log so you have a deploy-over-deploy comparison:
[2026-06-20 20:42:22] INFO: TTFB test result: 0.313s {"url":"https://staging-xxx.us-4.magentosite.cloud/customer/account/create","status":200}If your post-deploy phase is hanging, the most common cause is WARM_UP_PAGES pointing to a URL Fastly is refusing to cache. The post-deploy variables reference (opens in new tab) has the full regex syntax for entity types and store filters.
11. Images: Fastly IO, not custom resizing
Fastly Image Optimization (Fastly IO) handles every product image on the cloud once you enable it. The Fastly IO configuration guide (opens in new tab) walks through the Admin steps. The defaults are reasonable:
- Auto WebP: Yes
- Default WebP quality: 85
- Default JPEG quality: 85
- Default JPEG format controls: Auto
- Allow upscaling: No
- Resize filter: Lanczos3
Upload your source images at the largest resolution you need and let Fastly IO produce the variants. A 4 MB hero image will tank LCP no matter what you do to CSS, but a 4 MB source through Fastly IO at quality 85 with Auto WebP usually lands under 200 KB for the visible viewport.
Run a Lighthouse audit (opens in new tab) and a PageSpeed Insights (opens in new tab) check on the actual cloud URL. The two should roughly agree. If lab data looks great but the Chrome UX Report (opens in new tab) field data is bad, trust the field.
12. New Relic: this is free with cloud, use it
Every Adobe Commerce Cloud project gets New Relic APM (opens in new tab) on Production and Staging. Pro projects also get New Relic Infrastructure (opens in new tab) and Log Management (opens in new tab). The license is bundled, you do not need to install anything.
What to actually look at:
- Transactions tab, sorted by Most Time Consuming. The top three usually account for half the traffic budget.
- Databases tab. Anything taking more than 5% of total time is a candidate for an
EXPLAIN. - App Map for third-party API call latency. A single 800 ms call to a tax service on every cart page can be invisible until you see it on the map.
- Errors inbox for traces of exceptions that the application is swallowing.
Enable the Managed alerts for Adobe Commerce policy (opens in new tab). It ships with sensible thresholds based on Adobe’s reference workload. Hook deploy events into Track deployments (opens in new tab) so you can see performance regressions land exactly when they ship.
The one caveat: New Relic on cloud is sandboxed. You cannot send data from external servers into your provisioned account, and only services called from inside the Commerce app appear in traces.
13. Site-Wide Analysis Tool
The Site-Wide Analysis Tool (SWAT) (opens in new tab) is still available for Adobe Commerce Cloud customers (it was decommissioned for on-premises in April 2024). It runs 24/7 against your stack and surfaces:
- Tech stack version compliance and EOL warnings
- Configuration recommendations with a SWAT Health Index Score (opens in new tab)
- A list of installed third-party extensions (opens in new tab) and which ones have known issues
- A patch index integrated with the Quality Patches Tool (opens in new tab), so you see which
m2-hotfixesyou should apply for your version
Access it from the Adobe Commerce Support portal (opens in new tab). Most teams I have worked with did not know they had it, or had it but never opened the dashboard. The Patches tab alone can save a day of triage on a stuck site.
14. Quality Patches Tool: stop carrying patches in m2-hotfixes
The Quality Patches Tool (QPT) (opens in new tab) is bundled with cloud. Search for patches by your Magento version, apply them through composer, and skip the old m2-hotfixes directory pattern that bit-rots across upgrades.
magento-cloud ssh -e staging "vendor/bin/magento-patches-list"
magento-cloud ssh -e staging "vendor/bin/magento-patches-apply MDVA-12345"ACSD-62577 (opens in new tab) is a recent example for storefront search performance specifically. SWAT will tell you which patches it recommends for your install.
15. The magento-cloud CLI commands you actually use
Install the magento-cloud CLI (opens in new tab) once, then almost all triage runs from your laptop:
# list environments and their status
magento-cloud environment:list -p <project-id>
# tail the deploy log on production
magento-cloud log deploy -e production --tail
# tail the cloud log (build + deploy + post-deploy summary)
magento-cloud log cloud -e production --tail
# open a tunnel to all backend services
magento-cloud tunnel:open -e staging
# remote MySQL query without leaving your laptop
magento-cloud db:sql -e staging "SHOW FULL PROCESSLIST"
# flush the application cache
magento-cloud ssh -e production "bin/magento cache:flush"
# run a one-off ece-tools command
magento-cloud ssh -e staging "vendor/bin/ece-tools wd:diagnose"The ece-tools CLI reference (opens in new tab) lists all 34 ece-tools commands. The ones I actually use:
ece-tools buildandece-tools deploy: reproduce the cloud pipeline locally for debugging.ece-tools cron:unlockandcron:kill: clean up stuck jobs.ece-tools db-dump: produce a sanitized backup with--remove-definers.ece-tools wd:xdebug:enableandwd:xdebug:disable: temporary debug sessions.ece-tools env:config:show: dump the resolved environment configuration so you can verify what the pipeline thinks it is doing.
16. The eight-item checklist for the next deploy
If you have an hour and want to know whether any of this is broken on your cloud site right now:
magento-cloud ssh -e production "bin/magento deploy:mode:show"returnsproduction.- All indexers except Customer Grid are on Update by Schedule and the consumers runner is running.
php -i | grep opcache.validate_timestampsreturns0.- xdebug is not loaded on Production.
magento-cloud ssh -e production "php -m | grep -i xdebug"returns nothing. - Two separate Redis services exist in
.magento/services.yaml, one for sessions, one for default cache. - Fastly hit rate on category pages is above 80%. Check from the Fastly stats panel.
- No tables in the DB without a primary key. Run the SQL from section 7 against the cloud DB tunnel.
- New Relic is reporting transactions, and the Managed alerts policy is enabled.
If you can answer yes to all eight, the site is in better shape than most production cloud installs. Anything that fails is a candidate for next sprint.
What this list does not cover
Three areas that need their own write-ups. PWA Studio and headless storefronts behave differently from Luma and Hyva. B2B-specific issues like the customer-group price index time bomb (opens in new tab) deserve a longer piece on their own. And custom modules: if your slowest endpoint is Vendor_Custom/checkout/process, none of the above will help until that code is profiled. Blackfire (opens in new tab) is bundled with Adobe Commerce Cloud on Pro and is the right tool for that work.
Performance work on cloud is the same set of knobs as on-premises, exposed through different files. .magento.env.yaml instead of php.ini. Fastly instead of self-managed Varnish. New Relic instead of a homegrown APM. The discipline does not change: measure, change one thing, measure again. The rest is patience.
References
- Adobe Commerce on cloud infrastructure overview (opens in new tab)
- Performance Best Practices guide (opens in new tab)
- Configuration best practices (opens in new tab)
- Software recommendations (opens in new tab)
- Hardware recommendations (opens in new tab)
- Deployment flow (opens in new tab)
- Backend performance best practices (opens in new tab)
- Frontend performance best practices (opens in new tab)
- Database performance issue resolution (opens in new tab)
- Image optimization (opens in new tab)
- Indexer optimization (developer) (opens in new tab)
.magento.env.yamlconfiguration (opens in new tab)- Post-deploy variables (opens in new tab)
- Redis service setup (opens in new tab)
- Fastly image optimization (opens in new tab)
- New Relic service overview (opens in new tab)
- ECE-Tools CLI reference (opens in new tab)
- Site-Wide Analysis Tool (opens in new tab)
- Quality Patches Tool (opens in new tab)
Jigar Karangiya