Performance Guide
This guide covers configuration changes and tools to optimize the performance of a Pimcore application. Each section includes benchmark results from ApacheBench for comparison.
Before testing, set the following environment variables to replicate a production scenario:
APP_ENV=prod
APP_DEBUG=0
PIMCORE_DEV_MODE=0
PHP OPcache and JIT Compiler
OPcache stores precompiled script bytecode in shared memory, removing the need for PHP to load and parse scripts on each request. PHP 8 added Just-In-Time Compilation (JIT), which can further improve performance. The JIT compiler requires OPcache to be enabled.
By default, JIT is disabled. Enable it in your php.ini:
opcache.enable=1 # enables OPcache for PHP server
opcache.enable_cli=1 # enables OPcache for CLI mode
opcache.jit_buffer_size=256M # JIT buffer memory size. 0 disables JIT.
Benchmarks
ab -n 100 -c 20 http://localhost/en
- Before (OPcache on, JIT off): 212ms mean per request
- After (OPcache on, JIT on): 202ms mean per request
Symfony Performance Best Practices
Since Pimcore is built on Symfony, follow the Symfony Performance Best Practices.
Benchmarks
ab -n 100 -c 20 http://localhost/en
- Before: 220ms mean per request
- After: 208ms mean per request
OPcache Preloading
OPcache preloading preloads commonly used framework files and keeps the generated byte-code in memory.
Configure in php.ini:
opcache.preload_user=www-data # user that runs PHP-FPM
opcache.preload=/var/www/html/var/cache/prod/App_KernelProdContainer.preload.php
Note: memory_limit should be at least 200M when using preloading with Pimcore.
You can control which classes are preloaded using Symfony service tags container.preload and container.no_preload.
Benchmarks
ab -n 100 -c 20 http://localhost/en/shop/Products/Cars/Economy-Cars/Fiat-500~p104
- Before: 115ms mean per request
- After: 94ms mean per request
MySQL/MariaDB Optimizations
Composite Indexes
If you use Data Objects or custom database tables, configure composite indexes
so the query optimizer can use them instead of scanning the full table.
You can configure composite indexes in the class definition under General Settings.
Buffer Pool
The InnoDB buffer pool caches table and index data in memory.
Set innodb_buffer_pool_size to 70-80% of total available memory on a dedicated database server.
[mysqld]
innodb_buffer_pool_size=5G # adjust according to your data
See this discussion for guidance on choosing the right size. You can also use MySQLTuner for additional optimization suggestions.
Benchmarks
ab -n 100 -c 20 http://localhost/en/shop/Products/Cars/Economy-Cars~c547
- Before: 503ms mean per request
- After: 450ms mean per request
Pimcore Caching (Redis)
Pimcore uses a primary object cache where every element (document, asset, data object)
is cached as a serialized object.
By default, this cache uses the Doctrine database adapter and writes to the cache_items table.
For a significant performance boost, switch to the Redis adapter.
Configure Redis for the Pimcore cache pool:
# config/packages/cache.yaml
framework:
cache:
pools:
pimcore.cache.pool:
public: true
default_lifetime: 31536000 # 1 year
adapter: cache.adapter.redis_tag_aware
provider: '%env(REDIS_URL)%'
After clearing the cache (e.g. during a deployment), warm it up so users experience optimal performance even on first access:
bin/console pimcore:cache:warming
By default, pimcore:cache:warming caches 20 items per iteration and waits 2 seconds between iterations.
Adjust with --perIteration and --timeoutBetweenIteration.
Benchmarks
ab -n 100 -c 20 http://localhost/en/shop/Products/Cars~c390
- Before: 1299ms mean per request
- After: 1112ms mean per request
Full Page Cache
Pimcore's full page cache stores complete controller action responses. Subsequent requests for the same page are served directly from the cache without going through the MVC cycle.
pimcore:
full_page_cache:
enabled: true
lifetime: 120
exclude_cookie: 'pimcore_admin_sid'
exclude_patterns: '@^/test/de@'
Important: Only use sessions when necessary. The Pimcore full-page cache detects session usage and disables itself if a session is active.
Benchmarks
ab -n 100 -c 20 http://localhost/en
- Before: 141ms mean per request
- After: 129ms mean per request
Static Page Generator
Pimcore can generate static HTML files from documents. These static files are served directly by Apache/Nginx without going through the PHP/MVC cycle. This is best suited for pages with content that does not change frequently (not recommended for dynamic pages like news listings or product pages).
To enable: go to Document > Settings > Static Page Generator, check the enable checkbox, optionally define a lifetime, and save.
Benchmarks
ab -n 100 -c 20 http://localhost/en/Magazine
- Before: 161ms mean per request
- After: 7ms mean per request
In-Template Caching
Pimcore provides the pimcore_cache Twig extension for caching parts of a template
independently from the global cache.
This is useful for template sections that involve heavy computation or load many objects,
such as navigation menus.
{% set cache = pimcore_cache("main_navigation_cache", 60) %}
{% if not cache.start() %}
{% set navStartNode = document.getProperty('navigation_root') %}
{% if not navStartNode is instanceof('\\Pimcore\\Model\\Document') %}
{% set navStartNode = pimcore_document(1) %}
{% endif %}
{% set mainNavigation = app_navigation_data_links(document, navStartNode) %}
<div class="container">
...
{{
pimcore_render_nav(mainNavigation, 'menu', 'renderMenu', {
maxDepth: 2,
ulClass: {
0: 'navbar-nav menu-links ml-4 m-auto',
1: 'dropdown dropdown-menu',
'default': 'dropdown-menu dropdown-submenu'
}
})
}}
...
</div>
{% do cache.end() %}
{% endif %}
Benchmarks
ab -n 100 -c 20 http://localhost/en/Magazine
- Before: 162ms mean per request
- After: 129ms mean per request
Varnish Cache
Varnish is a caching reverse proxy placed in front of your web server. On the initial request, Varnish saves a copy of the response in memory. Subsequent requests are served directly from memory without reaching the application server.
Pimcore sends the correct headers for Varnish when the full-page cache is enabled.
Edge Side Includes (ESI)
ESI is a markup standard for managing page fragments that can be cached individually and assembled into a single page. Fragments can have their own cache policies and be shared across multiple pages.
Symfony has built-in support for ESI. See the Symfony ESI documentation. Learn more about Varnish.
ESI tags are not compatible with the Static Page Generator.