In this article you will understand what is the source of Magento 2 bottlenecks and how to optimize your online store.
Merchants often complain about Magento 2 platform and compare it to less advanced solutions such as WooCommerce. While minimal requirements to run WooCommerce are lower, it is not capable of achieving the same results without paid plugins.
What Magento 2 has out of the box and what WooCommerce lacks:
- multiple currencies
- multiple languages
- tier prices
- catalog rules
- advanced search engine based on ElasticSearch or OpenSearch
This is the reason why Magento 2 optimization is necessary to unlock its full potential.
Why Magento 2 is Slow?
Entropy is a fundamental law of the universe that dictates everything tends towards disorder and decay. Even in the digital realm, where we may think software is immune to the passage of time, entropy still prevails. This issue is especially true for a platform as versatile as Magento 2.
New features, third party integrations, constant import of fresh products to the catalog. It all adds up to the friction dragging back your store performance. We can all agree that machines need maintenance. The same goes for web applications - even though we can’t touch them.
Appropriate configuration is crucial for your store performance and stability.
Tweaking configuration requires only a certain amount of determination and patience. Few simple modules, updating system settings and monitoring - that’s all what is necessary.
Below is a list of steps which can boost Magento 2 performance without busting the bank.
Magento 2 System Requirements
To get the best performance out of Magento 2, it is important to ensure that you have the correct server infrastructure in place. Since Magento is a large system, proper resources allocation is crucial. The performance of Magento can be impacted by whether services like Varnish, Redis and Elasticsearch run on the same instance or have dedicated nodes.
If the database runs on the same instance as Magento, it can fight for resources and destabilize the online store.
Therefore, it is recommended that you have a dedicated server for the database to ensure that Magento 2 runs smoothly and delivers the best possible user experience.
What is the Minimum PHP Version for Magento 2.4.6?
Since Magento 2.4.4 it is required to use PHP 8.1, and Magento 2.4.6 supports both PHP 8.1 and 8.2. Thanks to the JIT compiler, PHP 8.* is faster than PHP 7.* especially for long-running tasks executed by Magento cron or indexing.
How Much RAM is Required for Magento 2 Server?
The minimum RAM requirement for Magento 2 is 2GB, but for a more comfortable experience, it's recommended to have at least 4GB RAM. The amount of RAM you need ultimately depends on whether additional services are running on separate nodes or if everything is deployed on one instance. On a weaker server, a swap file is necessary to compensate for low memory capacity.
It's important to note that Magento's deployment and compilation scripts can consume larger amounts of memory than a running shop. Additionally, indexers often spike with high stress on the server resources, so having enough RAM to handle these spikes is crucial for maintaining a stable platform.
Which Processor to Choose?
There is an equation to assess the amount of cores necessary to run Magento 2 store:
N[Cores] = (N[Expected Requests] / 2) + N [Expected Cron Processes]
A minimal server setup for hosting a Magento store should have at least two cores. However, for medium-sized stores, it is highly recommended to have at least 4 core processors to ensure efficient handling of HTTP requests and background tasks such as cron and indexing.
Use SSD Disks for Faster Online Store
To ensure optimal performance, it's highly recommended to use the most recent SSD disks available. Magento 2 reads modules and their configurations from many small files, which can be quite slow on older generation devices. SSD disks can access these files much faster, resulting in quicker load times and a smoother overall user experience.
Enable Unix Sockets for Redis Connection
Redis is a fast key-value database. In Magento, it serves as configuration and data cache storage. Customer session lives here too.
By default, Magento establishes connection to Redis using network TCP/IP protocol. In most cases, that's perfectly sufficient. Modern networks are blazing fast, providing low latency responses.
However, latency can drop even further with unix sockets. Unix sockets provide connection up to ten times faster than network on localhost. That’s because TCP IP performs a three-way handshake, which is unnecessary for services running on the same machine.
Just keep in mind that we should apply this optimization only for the Redis configured as cache storage. Depending on the scale of an ecommerce, Magento instances are often running on multiple servers. In that case, session Redis must be available as a standalone service, to keep sessions shared between application nodes.
With just one Magento instance is running alongside with single Redis service, we can use sockets for both cache and session connections.
Example Magento Configuration with Redis
// app/etc/env.php
'cache' => [
'frontend' => [
'default' => [
'backend' => 'Cm_Cache_Backend_Redis',
'backend_options' => [
'server' => '/var/run/redis/redis-server.sock', # This is an important entry
'database' => '0'
]
],
]
],
Enable socket connection for Redis and set permissions for the unix socket file that Magento PHP process can read and write to. Uncomment these lines in a default Redis config:
// /etc/redis/redis.conf
unixsocket /var/run/redis/redis-server.sock
unixsocketperm 770
After that, add Redis and PHP-FPM users to the same group and restart Redis service.
/etc/init.d/redis-server restart
If you're using Debian or Ubuntu:
sudo service redis-server restart
Redis and Transparent Huge Pages (THP)
If redis is still a bottleneck for your Magento 2 store, there is one more thing to check: Transparent Huge Pages (THP) config.
Redis documentation recommends disabling THP which means that after running the command terminal should print never
or madvise
to the standard output:
cat /sys/kernel/mm/transparent_hugepage/enabled
With enabled THP you may experience latency problems and slower responses from Redis.
In madvise
mode, the application controls whether it needs THP.
The following one-liner script turns on madvise
on Transparent Huge Pages:
echo madvise > /sys/kernel/mm/transparent_hugepage/enabled
Additional Technical Details
- Network has the major impact on a throughput well before the CPU.
- Redis favors fast CPUs with large caches and few cores. In this game Intel CPUs are currently the winners, however a third player is gaining on popularity - ARM.
- Unix sockets can achieve around 50% more throughput than the TCP/IP connections. It depends on the OS the server runs on.
Comparison of Available AWS Instances
CPU Platform | ARM | AMD | Intel |
---|---|---|---|
L1I Cache | 64KB | 64KB | 32KB |
L1D Cache | 64KB | 32KB | 32KB |
L2 Cache | 1MB | 512KB | 1MB |
L3 Cache | 32MB shared | 8MB shared per 4-core CCX | 35.75MB shared per socket |
Processors in order of appearance: ARM Graviton2, AMD EPYC 7571, Intel Xeon Platinum 8259CL.
Composer Autoloader Optimization
When application monitoring system such as New Relic screams that the most time-consuming transaction executes:
Magento\Framework\Config\Dom::_mergeNode
it means that Magento spends too much time on processing XML configuration.
The first step is to verify whether Magento cache is enabled (bin/magento cache:status
).
If the problem still persists, this is an IO related issue.
Enabling composer autoloader cache will help PHP with loading files from the disk faster:
composer dump-autoload -o --apcu
If you plan on updating the autoloader install script, run commands in the following order:
composer install --no-dev
bin/magento setup:di:compile
composer dump-autoload -o
bin/magento setup:static-content:deploy
Even if you don't face performance issues I highly recommend you to enable this option.
It’s important to run these commands in the correct order. Remember to install PHP with enabled APCU module.
Although composer provides another solution --classmap-authoritative
, it might cause trouble with Magento generated code.
We have tested it and there was an issue with a third party module where composer failed to resolve a plugin class.
Performance Tweaks for MariaDB
If you're using MariaDB, the official Magento 2 documentation recommends to configure these variables to speed up reindexing:
optimizer_switch = "rowid_filter=off"
optimizer_use_condition_selectivity = 1
You can enable query cache with query-cache-type = ON
to improve the read performance on MariaDB 10.6.
How does the query cache work? It checks for the query result to exist in the cache. Cache keys generated for examined queries are case-sensitive and comments affect the query cache key.
So these are different:
/* comment */SELECT sku FROM catalog_product_entity
/* comment2 */SELECT sku FROM catalog_product_entity
This can be disabled with query_cache_strip_comments.
Depending on your system configuration, you might need to configure the following MariaDB variables:
--query-cache-limit
--query-cache-size
--join-buffer-size
--innodb-buffer-pool-size
Magento Indexer Batch Size Tweaks
When Magento system logs contain this entry:
Memory size allocated for the temporary table is more than 20% of innodb_buffer_pool_size.
Please update innodb_buffer_pool_size or decrease batch size value (which decreases memory usages for the temporary table).
it warns you about indexers flooding the database memory.
Magento 2 indexers compute certain values which are required in real-time for many pages. For example: calculates final product prices for each currency with applied catalog rules. That’s a lot of math expressions to solve.
Indexing is a relatively heavy and resource-intensive process, for both the database and PHP. This problem arises when an online store has to process few thousands products for multiple languages.
There are two solutions:
- increase innodb_buffer_pool_size
- tweak indexers batch size
Indexers fetch multiple rows from the database. The more rows they query, the more memory consumption rises. This might cause a warning mentioned above or even PHP process being killed. Sometimes it is not possible to expand memory. In that case, you can decrease default indexer batch size. Batch size determines how many entities they process at once, for example, products. Both approaches have their pros and cons, although this is a non-linear relation of benefits. We’re trading time efficiency for memory consumption.
Here is a link to an example module which you may use as the starting point.
This module aggregates all indexers’ configuration of default Magento installation. In the perfect world, every module that introduces a new indexer should have its respective module tweaking indexer batch size.
Experiment with batch sizes until you discover the best setup. The simplest approach is to halve each value and test system behavior under new conditions. And as always: measure, don’t guess!
Do Not Enable Flat Catalog in Magento 2
If you’re running an online store on Magento 2, you’ve probably come across the option to enable flat catalog. It used to be a common practice for a long time, touted to improve performance and speed up page load times. However, this is no longer recommended and even discouraged.
So, what is the flat catalog, and why is it no longer recommended?
In Magento, product data is stored in two kinds of tables: EAV (Entity Attribute Value) - catalog_product_entity_*
(varchar
, text
, int
, decimal
, datetime
) for attributes (like color, size, etc.), and one for the core product information - catalog_product_entity
.
By default, Magento retrieves this data by joining EAV tables, which can be slow if you have many products. Flat catalog stores all the product data in a single table, removing all SQL joins that could be beneficial for stores with a lesser amount of attributes.
However, Adobe discourages the use of flat catalogs since Magento 2.1. It may cause indexation issues and degrade overall Magento performance.
Indexing is a resource-intensive process which puts the database and the PHP server under high pressure.
Introduction of Elasticsearch and OpenSearch had a great impact on the Magento product catalog speed. The most recent versions of Magento got improvements in the indexing process, making the flat catalog obsolete.
How to Disable Flat Catalog in Magento 2
To disable flat catalog in Magento 2, go to Stores -> Settings -> Configuration -> Catalog -> Catalog
.
Under Storefront section set Use Flat Catalog Category and Use Flat Catalog Product to "No".
Then, reindex your data to ensure that everything is up-to-date.
Disable Admin Tracking
By default, Magento 2 logs various activities, including admin actions. Logging consumes system resources and has an impact on the performance. Disable admin usage monitoring with Magento CLI:
bin/magento config:set admin/usage/enabled 0
This will prevent Magento from logging admin actions, reducing the overhead on the system.
HTTP/2 Instead of Asset Bundling
With an increasing number of browsers that support HTTP/2, a new way of serving web assets is available. Several new features that improve speed and data throughput. The most important for us is multiplexing.
With HTTP/1.1, we can transfer only 6 resources at once. To overcome that limitation, smart people introduced asset bundling. Multiplexing in HTTP/2 enables the browser to download more resources simultaneously.
Many JavaScript and CSS files are minified and later merged into larger files that can be reused for different pages of the website. Sometimes we may optimize images by creating image atlases. That’s usually a case for icons or small elements, shared between pages. Usually, improvements cut both ways and there is no silver bullet, a universal solution that fits all Magento stores.
Let’s structure that follows an old cliché from Sergio Leone’s western movie.
The Good
The good news is that lazy loading benefits a lot from parallel downloading. This means that you can remove inline images (if you had this optimization) because they’re increasing DOM size, and now the browser can download them concurrently.
Preload, prefetch and preconnect directives. They give hints for the browser to prepare itself better and to expect certain resources to appear on the page. The browser does know about images, styles, scripts and fonts when full website document is downloaded and ready. Thanks to the above directives, we can instruct the browser to open a new connection and start downloading resources before they appear on the screen. This can significantly reduce the time it takes to present your landing page to the user.
When you have modules with custom CMS content, a slider, for instance, then add desired directive just for the first banner image. Opening a new connection is expensive, so don’t use it for every asset if they won’t appear soon. Apply these directives for resources which are presented to the customer in the first place:
- largest contentful paint - usually that’s a home page banner
- fonts
- main product image
Loading critical CSS is available since Magento 2.4.2. It can be enabled with Magento CLI only, because this option is hidden from the dashboard in the production mode.
bin/magento config:set dev/css/use_css_critical_path 1
Then add critical.css
file to your theme:
app/design/frontend/<your_vendor_name>/<your_theme_name>/web/css/critical.css
We have same situation as with the preconnect directive. Keep it lean and neat. Stick only with these styles which are really important for the most pages.
The Bad
Performance gains depend on the amount of queried assets and their size. On the one hand, more connections mean faster download, but on the other, bundling is improving compression rate.
One bundle file has a greater chance of being compressed because there are bigger chunks of text that are repeated. However, building just one bundle is prone to poor caching. Final assets size might be larger than for fully compressed single bundles - preventing any optimization of parallel download.
Moreover, there are two additional boundaries. Chrome has a limit of 256 simultaneous connections at once. Nginx defaults to 128 concurrent downloads with http2_max_concurrent_streams option.
And The Ugly
A good bundler can still do its job and help you minify your online store’s JS and CSS. Compression won’t be as efficient as with bundles, but it is still worth the result. With tools like PhantomJS, you can scrap required JS for each page type and prepare bundle maps.
Do not forget about customers who can’t yet use HTTP/2 and allow them to download your assets with HTTP/1.1 protocol. This is a topic for a separate article with few experiments that can provide deeper insights on which approach is more suitable.
Bonus: Hybrid Solution - Create Small Bundles. This Improves Caching and Introduces Parallel downloads.
We’re left with the last question to answer: how can you determine what to include in a bundle? Split them by page type: CMS bundle, Category page bundle, Product page bundle.
The simplest approach available for native Magento is to configure the bundle size in a theme's view.xml
file:
<vars module="Js_Bundle">
<var name="bundle_size">1MB</var>
</vars>
However, Magento bundling has poor performance. As an alternative you can use Baler or prepare bundles for specific views with PhantomJS.
In one of ours Magento stores, we’re using webpack alongside with the standard Magento assets builder. This approach allowed us to override only crucial parts of the storefront and leave Magento JS intact. It was a tradeoff between a fully custom frontend and reasonable development time.
Each online store is different, and we should treat it with separate optimization formula. We encourage you to contact us because we will provide you with services tailored to your needs.
External Resources
- Redis tweaking and AWS instances comparison
- Composer autoloader optimization
- HTTP/2 optimizations
- JavaScript Bundling
- Medium article with benchmark