Most Magento 2 performance posts read the same. Enable cache, install Redis, run reindex, the end. Real stores on Adobe Commerce on cloud infrastructure 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, 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 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:
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 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 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 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 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 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 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 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 Purgecontrols instead.
For the Fastly WAF on Adobe Commerce, 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. 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. 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 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: PHP version, extensions, runtime.php.iniat the repo root: per-environment overrides formemory_limit,realpath_cache_size, OPcache.- Environment variables in
.magento.env.yaml: 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.
For xdebug on cloud, do not load it as a permanent extension. Toggle it per session with the ece-tools CLI:
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. 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 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; the slow query log location is documented in the log locations guide:
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:
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 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. 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 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: Admin > Stores > Configuration > Sales > Sales Emails > General Settings. Order placement stops waiting on SMTP.
- Asynchronous order data processing: 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: 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 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 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 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 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 and a PageSpeed Insights check on the actual cloud URL. The two should roughly agree. If lab data looks great but the Chrome UX Report 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 on Production and Staging. Pro projects also get New Relic Infrastructure and Log Management. 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. It ships with sensible thresholds based on Adobe’s reference workload. Hook deploy events into Track deployments 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) 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
- A list of installed third-party extensions and which ones have known issues
- A patch index integrated with the Quality Patches Tool, so you see which
m2-hotfixesyou should apply for your version
Access it from the Adobe Commerce Support portal. 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) 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 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 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 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 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 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
- Performance Best Practices guide
- Configuration best practices
- Software recommendations
- Hardware recommendations
- Deployment flow
- Backend performance best practices
- Frontend performance best practices
- Database performance issue resolution
- Image optimization
- Indexer optimization (developer)
.magento.env.yamlconfiguration- Post-deploy variables
- Redis service setup
- Fastly image optimization
- New Relic service overview
- ECE-Tools CLI reference
- Site-Wide Analysis Tool
- Quality Patches Tool



Leave a Comment