Shopware 6 Performance Blog
Shopware 6 schneller machen – die ultimative Performance-Checkliste Shopware 6 schneller machen – die ultimative Performance-Checkliste

Shopware 6 Performance Tweaks – Basic Checklist

System settings, PHP, MySQL & more – for a fast store

In this article, I will show you a structured checklist for optimizing Shopware 6 performance – especially intended for admins, DevOps or technically experienced store operators.

The basis is the official Shopware Performance Tweaks list – we will build on this, go deeper and show you what you should specifically check to reduce loading times and improve performance.

🧾 Performance checklist – at a glance

PHP

MySQL

Shopware

Advanced Tools

All points explained in detail

Latest PHP version (8.3+)

Prio: high

→ Every new PHP version brings significant performance gains – especially with object-oriented code, which Shopware makes heavy use of. PHP 8.1-8.3 contain JIT improvements, less overhead and faster function input/output.

Make sure that you only switch to PHP 8.3, for example, if your Shopware version supports the PHP version.

You can change the PHP version in the server configuration or with your hoster.

OPCache + JIT active (& configured)

Prio: high

→ OPCache stores compiled PHP scripts in RAM – this avoids constant re-parsing. The JIT compiler (“just-in-time”) translates parts directly into machine code, which speeds up CPU-intensive operations. Settings such as memory_consumption or interned_strings_buffer must be correct so that the cache is used efficiently.

You can read more detailed information here: Blog post about OPCache

I like to use the following OPCache GUI. Simply upload this folder to the public folder of the store, call /opcache-gui/index.php, check the variables & very important -> delete the folder afterwards.

Coordinate any necessary changes to the variables with the hoster.

Here is a possible configuration:

opcache.enable=1
opcache.memory_consumption=4096M
opcache.interned_strings_buffer=20
opcache.max_accelerated_files=20000
opcache.revalidate_freq=60
opcache.enable_file_override=1
opcache.fast_shutdown=1

open_basedir disabled

Prio: high

→ This directive severely restricts file paths and significantly slows down file access. Especially with plugins, templates or caches, this leads to unnecessary IO latencies – should therefore be deactivated.

open_basedir=

Redis as session handler

Prio: high

→ Shopware saves sessions in the file system by default – slow with many simultaneous users. Redis is memory-based (in-memory), high-performance and significantly reduces I/O, ideal for larger stores.

You can find out how to outsource sessions to Redis in my blog post here.

# config/packages/redis.yml
framework:
    session:
        handler_id: "redis://host:port"

Cache Compressor: zstd instead of gzip

Prio: high

→ Shopware uses Gzip (gzcompress/gzuncompress) by default to compress cache content and save memory. Gzip is widely used and reliable, but reaches its performance limits with very extensive page content or large data objects. In such cases, (de)compression can cost noticeable computing time – even for pages that have already been cached – which in turn delays delivery.

As of Shopware 6.6, Zstandard (zstd) can be configured as an alternative compression method. Zstd offers both faster compression and more efficient decompression compared to Gzip – especially for large amounts of data. This leads to shorter loading times and better overall performance. With a corresponding patch, zstd can even be retrofitted in older Shopware versions.

Here you can find an article on how to integrate zstd correctly.

# config/packages/prod/shopware.yaml
shopware:
    cache:
        cache_compression: true
        cache_compression_method: 'zstd'

Configure PHP worker

Prio: low

→ PHP-FPM manages processes that execute PHP. If too few workers are active, waiting times occur (“queueing”) – if there are too many, there is a risk of RAM shortage. The number of workers must match the load (monitoring: pm.max_children, pm.start_servers etc.).

It is best to contact your hoster for coordination. Here you can find an article from Tideways, which we are happy to use as a reference.

Use OPcache Preload if necessary

Prio: low

→ This allows a predefined set of PHP files to be preloaded directly when the PHP-FPM process is started. Saves loading time on first access – especially during warmup, deployment or cache rebuild.

In itself, the point here can already bring a lot of performance, but can also bring some complexity with it. For a cache clear, PHP would have to be restarted, which must first be agreed with the customer. We therefore do not blindly recommend this point to every customer.

Check low prio PHP configuration

Prio: low

→ Options like zend.assertions=-1, deactivated detect_unicode or activated realpath_cache avoid unnecessary checks and system calls. Not a huge gain, but helpful for fine-tuning.

zend.assertions=-1
zend.detect_unicode=0
realpath_cache_ttl=3600

You can read more detailed information here

min. MySQL 8

Prio: high

→ We recommend using at least MySQL 8 as the basis. This version not only offers significant performance improvements compared to older releases, but also many modern features that are increasingly relevant for complex Shopware stores.

These include, among others:

  • Extended support for JSON functions
  • Improved InnoDB storage engine for higher stability and speed

These innovations not only make your store more efficient, but also future-proof. MySQL 8 should therefore be considered the technical minimum for a high-performance and stable database environment.

SELECT VERSION();

If you are still using MySQL 5.6, we strongly advise you to update the version.

Check InnoDB configuration – the heart of MySQL performance

Prio: high

-> The InnoDB buffer pool is one of the most important performance factors in a MySQL database – especially for Shopware 6, which uses the InnoDB storage engine by default. It serves as a central cache for data and indexes that are loaded from the database. As soon as you perform a database query, the relevant content ends up in the buffer pool. The next time they are accessed, they can then be read directly from the working memory – which is much faster than accessing the hard disk.

What exactly does the buffer pool do?

  • Data caching: Frequently used data records are stored in RAM – ideal for recurring queries on article data, orders or customer data.
  • Index caching: Indexes are also stored temporarily. This speeds up complex SQL queries such as JOINs or filters considerably.
  • Delayed writing: Changes to data are not immediately written to the hard disk, but are first buffered. This “write-back” behavior reduces the I/O load and improves the write speed.

innodb_buffer_pool_size

You determine the size of the buffer pool via the variable innodb_buffer_pool_size in your MySQL configuration.

  • If possible, the buffer pool should completely cover the actively used data.
  • Ideally, there should also be room for growth (new orders, new articles, etc.).
  • A full comparison with the entire database size is not always necessary – especially if large tables such as the cart table contain a lot of historical but rarely used data.

💡 Practical tip: In customer projects, we check which tables are actually used frequently and adjust the buffer pool accordingly. This creates a sensible balance between performance and RAM requirements.

#example for 8GB pool size
innodb_buffer_pool_size=8589934592

innodb_log_file_size

This setting determines the size of the InnoDB transaction log files. These logs record all write processes before they are finally written to the hard disk. If the log file is too small, it has to be written more often – which in turn reduces performance.

  • With a buffer pool size of 8 GB: approx. 2 GB log size
  • Do not choose too large a size – larger logs considerably prolong the recovery time in the event of a database crash.
#example for 2GB log size
innodb_log_file_size=2147483648

innodb_buffer_pool_instances

With this setting, the buffer pool can be divided into several instances. This has advantages on systems with a lot of RAM and high parallelism – e.g. when many simultaneous database queries are processed.

  • 8 instances is a good guideline for systems with several gigabytes of RAM.
  • Important: This setting is only relevant for MySQL – for MariaDB the option has been removed or no longer has any effect.
innodb_buffer_pool_instances=8

Check sql_mode

Prio: low

The MySQL variable sql_mode controls the behavior of the database, e.g. in the event of queries or errors. In many hosting environments, the ONLY_FULL_GROUP_BY mode is active – this forces all non-aggregated columns in a SELECT query to also be in the GROUP BY clause. This is problematic for Shopware, as many internally used queries are not compatible with this.

To compensate for this, Shopware removes this mode dynamically for every query – but this creates unnecessary database load.

  • Permanently deactivate ONLY_FULL_GROUP_BY on the server side (e.g. via my.cnf)
  • Then set the following in the .env:
SQL_SET_DEFAULT_SESSION_VARIABLES=0

This avoids additional SQL queries and reduces the database load.

Check HTTP Cache configuration

Prio: high

→ The Shopware HTTP cache saves rendered pages. Additional tuning is required for logged-in users or shopping cart cases, e.g. segmentation or tagging. Saves an enormous amount of rendering effort.

You can find more detailed information here

# config/packages/prod/shopware.yaml
shopware:
    cache:
        invalidation:
            http_cache: []

Disable admin worker

Prio: high

→ In Shopware 6, the so-called admin worker is active by default. This processes tasks from the message queue in the background – but only if a user is logged into the admin area. As soon as an administrator calls up a page in the backend, the corresponding queue jobs are triggered and processed asynchronously.

However, this has some disadvantages:

  • No background processing during inactivity – the queue remains unprocessed at night or during infrequent logins.
  • Increased server load with many admin users at the same time – as every logged-in user can trigger tasks, the CPU load increases unnecessarily.

For these reasons, Shopware expressly recommends deactivating the Admin Worker and using the CLI Worker instead.

This is executed independently of the login status and offers a more stable, controllable processing of the queue – for example for emails, webhooks, order status changes or product updates.

We recommend configuring the message queue via Supervisor on the server.

# config/packages/prod/shopware.yaml
shopware:
    admin_worker:
        enable_admin_worker: false

Disable Increment-Storage

Prio: low

→ Is controlled via the database by default (e.g. event counter). Hardly used, but causes write load with every event. Can be safely deactivated.

#config/packages/prod/shopware.yaml
shopware:
    increment:
        user_activity:
            type: 'array'

        message_queue:
            type: 'array'

Disable updates of mail test data

Prio: low

→ Shopware updates test data for email tempaltes in the background after every order, for example. Pointless and computationally intensive in production – should be deactivated via YAML.

# config/packages/prod/shopware.yaml
shopware:
    mail:
        update_mail_variables_on_send: false

Set Cache-ID in .env

Prio: low

→ Shopware uses a unique cache ID for its caching mechanisms, which is automatically generated during the initial installation and stored in the database. This ID is a random hexadecimal string and usually remains constant. It is loaded from the database table app_config each time a page is called up – more precisely from the entry with the key cache-id.

Even if this database query is very small, it causes additional access to the database for each individual request – and this is exactly what can be avoided.

Retrieve the cache-id value from the database (e.g. via Adminer or SQL tool):

SELECT `value` FROM `app_config` WHERE `key` = 'cache-id';

Paste the value into your .env file:

#.env
APP_CACHE_ID=deine-cache-id-hier

Disable external APP_URL check

Prio: low

→ Each time the admin area is loaded, Shopware attempts to call itself via the APP_URL defined in the .env to check whether the configuration is correct.

If your APP_URL is set correctly, you can deactivate this check to avoid unnecessary HTTP requests – especially in productive environments.

#.env
APP_URL_CHECK_DISABLED=1

Optimize logging (Rotation + Level)

Prio: hoch

→ In earlier Shopware versions, logging was often not optimally configured in production environments. This had two undesirable side effects:

1. log files without rotation ⇒ memory problem

By default, Shopware saves all log entries in the var/log/*.log files. If no rotation is activated, these files grow continuously – and are never deleted.

In many older setups, we have seen dozens of gigabytes of log data accumulate after a year – completely unnoticed until the storage space ran out or the monitoring system sounded the alarm.

2. logging in the database ⇒ log_entry becomes huge

Another problem that is often overlooked:

Shopware writes many system events not only to files, but also to the database table log_entry.

If the log level for these events is not restricted (e.g. to error), even routine actions such as successfully sent emails are logged. Over weeks and months, this creates a huge amount of data, which:

  • inflates the database
  • slows down the backup
# config/packages/prod/monolog.yaml
monolog:
    handlers:
        main:
            level: error
            buffer_size: 30
        business_event_handler_buffer:
            level: error

use .env.local.php

Prio: low

→ Shopware reads configuration from .env. The .env.local.php is a precompiled array → significantly faster at boot time and ideal for production systems.

Create the .env.local.prod as follows:

bin/console dotenv:dump prod

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert