Jump to content

Recommended Posts

Hello,

I have a few configuration or understanding problems with the OPcache configuration. I'm trying to cache all the scripts that are running on my webserver which are about 25 small Wordpress sites. 

Somehow the memory usage is immediately full. It does not matter, if I set the memory size to 1023 MB, 4095 MB, 8191 MB or, 32767 MB (PHP 8.4.12). Also the memory usage of the OPcache is not matching with the RAM usage of the server. The server has 12 vCPU and 64 GB of RAM running Debian 12.4.

Each Wordpress page has about 2-3 visitors per hour, so nearly no traffic and load. If I get the cached scripts with opcache_get_status(true) and sum the memory consumption of each script, I'm reaching about 110 MB. There is only PHP-FPM 8.4.12 installed and all Wordpress sites use the same pool.

In the opcache_get_status output the hits count and cached script count should be much higher, since only two out of 25 Wordpress sites are partially cached.

In attachment the phpinfo OPcache settings.

Is this an OPcache issue, misconfiguration or know behavior? Does anyone have an idea on how to solve that or maybe I should configure my server in another way? As far as I know I can only make one PHP-FPM pool per PHP version on the same server, without any special chroot configuration.

I tried to read many posts from different forums, but unfortunately I'm not getting anywhere. Also asking AI did not really help.

This is my PHP-FPM pool config:

php_admin_flag[opcache.enable]=1
php_admin_flag[opcache.enable_cli]=1
php_admin_value[opcache.restrict_api]="{DOCUMENT_ROOT}"
php_admin_value[opcache.memory_consumption]=4096
php_admin_value[opcache.interned_strings_buffer]=128
php_admin_value[opcache.max_accelerated_files]=131071
php_admin_flag[opcache.validate_timestamps]=1
php_admin_value[opcache.revalidate_freq]=60
php_admin_flag[opcache.save_comments]=1
php_admin_flag[opcache.fast_shutdown]=1
php_admin_value[opcache.max_wasted_percentage]=5

Thanks!

opcache.jpg

Link to comment
https://forums.phpfreaks.com/topic/330514-opcache-configuration-issues/
Share on other sites

Why are you enabling the opcache for cli?  There is really no point in doing that for multiple reasons.

Set the amount of memory to be what you believe you need.  Start with a low value like 128 and increase it if your cache hit #'s are low.  What is important is the cache hit #'s.  

From the information you provided, opcache is using an mmap file, and theoretically that can arbitrarily large, but it would be defeating the entire purpose of opcache, which is to be an in memory cache.  

As for php-fpm, typically, you setup a pool for each site.  It sounds like you are sharing a pool for a lot of different sites.  So the following setting doesn't make sense, given what you've stated in regards to your environment.

php_admin_value[opcache.restrict_api]="{DOCUMENT_ROOT}"

First of all, that is not how you reference environment variables in the php-fpm conf file.  At minimum it would need to be:  "${DOCUMENT_ROOT}".

Trying a dynamic setting like this doesn't make sense and I've not seen it used, as the very existence of DOCUMENT_ROOT depends on what is sent to php-fpm.

If the sites are all in a particular location like /var/www/... then I would suggest that setting to be "/var/www".  

In debugging this issues the first thing I would suggest is to remove that setting entirely, and then re-enable it once you are seeing opcache results you expect.

Hi, thanks for your fast reply.

I enabled opcache.enable_cli for testing, since I did not know anymore what to try...

Even if I have one pool per domain, it seems that the OPcache is shared between all pools that use the same PHP version. If I check on one domain with the PHP code "opcache_get_status(true);" then I see also scripts from other PHP-FPM pools.

Quote

From the information you provided, opcache is using an mmap file, and theoretically that can arbitrarily large, but it would be defeating the entire purpose of opcache, which is to be an in memory cache.  

I used the default configuration, how can I make sure, that OPcache is using the memory and not a mmap file?

 

Somehow the information got lost, that I'm using Froxlor. Froxlor is creating me one pool per site and it swaps the variable

{DOCUMENT_ROOT}

with the path of each corresponding www path, like /var/customers/webs/customer1, /var/customers/webs/customer2, /var/customers/webs/customer3 and so on.

Under /etc/php/8.4/fpm/pool.d/ I have a configuration for each site, that looks like this:

;PHP-FPM configuration for "domain1.tld" created on 2025.09.15 21:47:27
[domain1.tld]
listen = /var/lib/apache2/fastcgi/1-customer1-domain1.tld-php-fpm.socket
listen.owner = customer1
listen.group = customer1
listen.mode = 0660
user = customer1
group = customer1
pm = ondemand
pm.max_children = 32
pm.process_idle_timeout = 10
pm.max_requests = 600
;chroot = /var/customers/webs/customer1/domain1.tld/
security.limit_extensions = .php
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /var/customers/tmp/customer1/
env[TMPDIR] = /var/customers/tmp/customer1/
env[TEMP] = /var/customers/tmp/customer1/
php_admin_value[upload_tmp_dir] = /var/customers/tmp/customer1/


php_admin_flag[allow_url_fopen] = On
php_admin_flag[allow_url_include] = Off
php_flag[asp_tags] = Off
php_value[auto_append_file] =
php_value[auto_prepend_file] =
php_value[default_charset] = "UTF-8"
php_admin_value[disable_functions] = curl_exec,curl_multi_exec,exec,passthru,pcntl_alarm,pcntl_async_signals,pcntl_exec,pcntl_fork,pcntl_get_last_error,pcntl_getpriority,pcntl_setpriority,pcntl_signal_dispatch,pcntl_signal_get_handler,pcntl_signal,pcntl_sigprocmask,pcntl_sigtimedwait,pcntl_sigwaitinfo,pcntl_strerror,pcntl_wait,pcntl_waitpid,pcntl_wexitstatus,pcntl_wifcontinued,pcntl_wifexited,pcntl_wifsignaled,pcntl_wifstopped,pcntl_wstopsig,pcntl_wtermsig,popen,proc_close,proc_get_status,proc_nice,proc_open,proc_terminate,shell_exec,show_source,system
php_flag[display_errors] = Off
php_flag[display_startup_errors] = Off
php_admin_flag[enable_dl] = Off
php_value[error_reporting] = E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE
php_admin_flag[expose_php] = Off
php_admin_flag[file_uploads] = On
php_flag[html_errors] = On
php_admin_flag[ignore_repeated_errors] = Off
php_admin_flag[ignore_repeated_source] = Off
php_value[include_path] = ".:/usr/share/php/"
php_flag[log_errors] = On
php_admin_flag[log_errors] = On
php_value[log_errors_max_len] = 1024
php_flag[mail.add_x_header] = Off
php_value[max_execution_time] = 30
php_admin_value[max_input_time] = 60
php_admin_value[memory_limit] = 256M
php_admin_value[open_basedir] = "/var/customers/webs/customer1/domain1.tld:/var/customers/tmp/customer1:/usr/share/php:/tmp"
php_admin_value[output_buffering] = 4096
php_admin_value[post_max_size] = 32M
php_admin_value[precision] = 14
php_admin_flag[register_argc_argv] = Off
php_admin_flag[report_memleaks] = On
php_admin_value[sendmail_path] = "/usr/sbin/sendmail -t -i -f [email protected]"
php_value[session.auto_start] = 0
php_value[session.cookie_domain] =
php_value[session.cookie_lifetime] = 0
php_value[session.cookie_path] = /
php_admin_value[session.gc_divisor] = 1000
php_admin_value[session.gc_probability] = 0
php_value[session.name] = PHPSESSID
php_value[session.serialize_handler] = php
php_flag[session.use_cookies] = 1
php_flag[short_open_tag] = On
php_value[upload_max_filesize] = 32M
php_admin_value[variables_order] = "GPCS"
php_admin_value[session.save_path] = /var/customers/tmp/customer1/

php_admin_flag[opcache.enable]=1
php_admin_value[opcache.restrict_api]="/var/customers/webs/customer1/domain1.tld/"
php_admin_value[opcache.memory_consumption]=4095
php_admin_value[opcache.interned_strings_buffer]=256
php_admin_value[opcache.max_accelerated_files]=16229
php_admin_flag[opcache.validate_timestamps]=1
php_admin_value[opcache.revalidate_freq]=60
php_admin_flag[opcache.save_comments]=1
php_admin_value[opcache.max_wasted_percentage]=5

Here a second example to see the difference:

;PHP-FPM configuration for "domain2.tld" created on 2025.09.15 21:47:27
[domain2.tld]
listen = /var/lib/apache2/fastcgi/1-customer2-domain2.tld-php-fpm.socket
listen.owner = customer2
listen.group = customer2
listen.mode = 0660
user = customer2
group = customer2
pm = ondemand
pm.max_children = 32
pm.process_idle_timeout = 10
pm.max_requests = 600
;chroot = /var/customers/webs/customer2/domain2.tld/
security.limit_extensions = .php
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /var/customers/tmp/customer2/
env[TMPDIR] = /var/customers/tmp/customer2/
env[TEMP] = /var/customers/tmp/customer2/
php_admin_value[upload_tmp_dir] = /var/customers/tmp/customer2/


php_admin_flag[allow_url_fopen] = On
php_admin_flag[allow_url_include] = Off
php_flag[asp_tags] = Off
php_value[auto_append_file] =
php_value[auto_prepend_file] =
php_value[default_charset] = "UTF-8"
php_admin_value[disable_functions] = curl_exec,curl_multi_exec,exec,passthru,pcntl_alarm,pcntl_async_signals,pcntl_exec,pcntl_fork,pcntl_get_last_error,pcntl_getpriority,pcntl_setpriority,pcntl_signal_dispatch,pcntl_signal_get_handler,pcntl_signal,pcntl_sigprocmask,pcntl_sigtimedwait,pcntl_sigwaitinfo,pcntl_strerror,pcntl_wait,pcntl_waitpid,pcntl_wexitstatus,pcntl_wifcontinued,pcntl_wifexited,pcntl_wifsignaled,pcntl_wifstopped,pcntl_wstopsig,pcntl_wtermsig,popen,proc_close,proc_get_status,proc_nice,proc_open,proc_terminate,shell_exec,show_source,system
php_flag[display_errors] = Off
php_flag[display_startup_errors] = Off
php_admin_flag[enable_dl] = Off
php_value[error_reporting] = E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE
php_admin_flag[expose_php] = Off
php_admin_flag[file_uploads] = On
php_flag[html_errors] = On
php_admin_flag[ignore_repeated_errors] = Off
php_admin_flag[ignore_repeated_source] = Off
php_value[include_path] = ".:/usr/share/php/"
php_flag[log_errors] = On
php_admin_flag[log_errors] = On
php_value[log_errors_max_len] = 1024
php_flag[mail.add_x_header] = Off
php_value[max_execution_time] = 30
php_admin_value[max_input_time] = 60
php_admin_value[memory_limit] = 256M
php_admin_value[open_basedir] = "/var/customers/webs/customer2/domain2.tld:/var/customers/tmp/customer2:/usr/share/php:/tmp"
php_admin_value[output_buffering] = 4096
php_admin_value[post_max_size] = 32M
php_admin_value[precision] = 14
php_admin_flag[register_argc_argv] = Off
php_admin_flag[report_memleaks] = On
php_admin_value[sendmail_path] = "/usr/sbin/sendmail -t -i -f [email protected]"
php_value[session.auto_start] = 0
php_value[session.cookie_domain] =
php_value[session.cookie_lifetime] = 0
php_value[session.cookie_path] = /
php_admin_value[session.gc_divisor] = 1000
php_admin_value[session.gc_probability] = 0
php_value[session.name] = PHPSESSID
php_value[session.serialize_handler] = php
php_flag[session.use_cookies] = 1
php_flag[short_open_tag] = On
php_value[upload_max_filesize] = 32M
php_admin_value[variables_order] = "GPCS"
php_admin_value[session.save_path] = /var/customers/tmp/customer2/

php_admin_flag[opcache.enable]=1
php_admin_value[opcache.restrict_api]="/var/customers/webs/customer2/domain2.tld/"
php_admin_value[opcache.memory_consumption]=4095
php_admin_value[opcache.interned_strings_buffer]=256
php_admin_value[opcache.max_accelerated_files]=16229
php_admin_flag[opcache.validate_timestamps]=1
php_admin_value[opcache.revalidate_freq]=60
php_admin_flag[opcache.save_comments]=1
php_admin_value[opcache.max_wasted_percentage]=5

 

1. Yes all pools share the same opcache.  Apparently you are supporting different versions of PHP.  The opcache library is matched to the version of PHP.

16 hours ago, mr-manuel said:

I used the default configuration, how can I make sure, that OPcache is using the memory and not a mmap file?

It is going to use mmap.  That is not a problem.  Setting the opcache to be absurdly large is what you want to avoid.   

From what you've stated, you have 64g of memory and 20+ wordpress sites, many of which see little traffic.  So, allocating 4g is probably going to be ok.

 

Ok, thanks. What I'm trying to understand is why not more scripts are cached, if I have plenty of memory available? Currently I do not make it over 5.000, even if there are a lot more than 5.000 scripts (which is about 2,5 Wordpress sites). Do you have recommendations on how to troubleshoot that? I'm out of ideas.

Only scripts that are accessed are cached.  If your sites are not very active and only get a few hits to the home page for example, it may not be accessing that many of the script files that make up the WordPress sites.

I've never had to really deal with Opcache much.  I just enabled it and it seems to be working just fine on the sites I have using it.  I setup this Opcache Gui that I can use to check on the status of the cache.

19 hours ago, kicken said:

Only scripts that are accessed are cached.  If your sites are not very active and only get a few hits to the home page for example, it may not be accessing that many of the script files that make up the WordPress sites.

I've never had to really deal with Opcache much.  I just enabled it and it seems to be working just fine on the sites I have using it.  I setup this Opcache Gui that I can use to check on the status of the cache.

This also began as a question about memory usage.

Otherwise, I agree with Kicken -- opcache just works for the most part.  I realize you are running a hosting business, so your goals are different from the average site.  I assumed you already understood that it caches scripts once they have been run. 

It also will evict scripts using a FIFO strategy, if you end up having a cache size that is smaller than the sum of all the sites you're hosting.   In your case you have two different PHP versions, so there's an opcache for each.

What it's actually storing is the "compiled" version of the script.  PHP source gets turned into the "Op Code" version of the script that is run by the PHP virtual machine, hence the name "Op Cache'.  There is a way of "preloading", but it would be complicated for you to get set up in your environment, and under the circumstances I would not recommend using it.

I 2nd the Opcache gui app.  It is a bit of a pain to get setup in some situations, and you have to secure it yourself using nginx.  Beyond the statistics, you can clear the cache and seeing the full list of cached scripts can be very helpful.

Just for completeness, here's the list of options I'm aware of:

Tuner is more of a diagnostic/recommendation tool from what I can see, but might be worth trying out.

One last suggestion for you would be that you disable validate_timestamps, rather than trying to minimize it with timing.  For production wordpress setups, there's no reason to check the files.   You'd probably want to have some automation of a clearing of the op_cache any time you upgrade a wordpress install for a customer (including plugins), but we don't know how you handle that for your customers.

A made a few more tests, also setting php_admin_flag[opcache.validate_timestamps]=0 and php_admin_value[opcache.revalidate_freq]=3600 in the PHP-FPM pool, but I never have more then 4.000 scripts cached. Also not after some days or if I open multiple domains to see if the scripts are cached. Even if it's not accessing all scripts of a domain, at least one script should be cached. In the cached scripts list there are no more than 2-3 domains.

I still don't get it that I'm the only one with that problem. I tried now also to setup a complete new server, but there I have the same issue. If I assume that this is a bug, how should I move forward?

If you set validate_timestamps=0, then there is no need for or use of opcache.revalidate_freq.

You should also look at increasing your opcache.interned_strings_buffer size.

It sounds like the cache is resetting regularly.  This is a complicated issue I don't have time to go into right now, but I'll try and have some suggestions for you.  

Did you install Opcache Gui somewhere, as suggested, so you can look at what is going on within opcache?

So here is what happens with Opcache:

  • If opcache.memory_consumption fills OR opcache.max_accelerated_files is exceeded, opcache will restart.
  • Unless -- opcache.max_wasted_percentage threshold is not hit

What happens in that case, is that Opcache will basically be stuck, and no new files can be added to the cache.  I don't think that is what is happening to you, but it's important to know.

This is why you need the opcache gui, so you can determine if you are hitting the cache restart condition, which it sounds like you are.  You can't use cli php to make opcache api calls.   So you need to go through an analysis phase as you watch the cache(s).  

At the start of the discussion, I think you indicated that you were filling up all the cache memory you were providing, and perhaps in your hosting situation that is actually the case, but I think you need to come to that conclusion through analysis. 

Hopefully you understand that the total amount of allocated process memory for php-fpm + nginx + (any thing else you have running on the server that needs to be in-memory, like mysql) + memory allocated to opcache, can not exceed the physical memory you have in the box.  In my experience you might want to have some swap enabled for truly exceptional situations or to get the box through nightly cron jobs where some processes kick in that only happen once a day/week, but otherwise, you simply can not allocated so much opcache memory that you exceed your available server memory.  If you do, things performance will likely go down the drain.  So this determination and tuning of the opcache is something you want to work through methodically, but testing, increasing (with php-fpm restarts any time you change any php parameters) and examination of the opcache state using the opcache gui.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.