pfSense: Optimizing PHP-FPM for Long-Term Web UI Performance
I’m sharing these PHP-FPM tweaks for pfSense publicly to solicit feedback and help anyone exploring similar optimizations. There may be more efficient methods or edge cases I haven’t covered. I welcome any suggestions, improvements, or alternative approaches.
pfSense’s Web UI runs on PHP-FPM, and although the defaults work well out of the box, a few targeted tweaks can make a stable environment even more performative over days, weeks, and months of uptime.
Over the weekend, I dove into pfSense’s PHP-FPM boot templates to discover exactly where those settings live. This article is not part 2 of My $300 pfSense Firewall Appliance – Part 1: Unboxing series. Consider it a small side quest.
Below are four key areas to tune: OPCode caching, PHP.ini timeouts, input-vars limit, and the PHP-FPM process manager.
Note: These recommendations apply only to pfSense 2.7.x on appliances with 4 GB or more of RAM. Use at your own risk!
Where is PHP-FPM tuned for pfSense?
pfSense keeps all (as far as I’ve found) of its PHP-FPM tuning logic in /etc/rc.php_ini_setup
. Below is a brief overview of the tweaks I’ve made to that file and why they matter.
# ─── opcache section ───────────────────────────────────────── - OPCACHEMEMSIZE="50" + OPCACHEMEMSIZE="100" # ─── php.ini section ─────────────────────────────────── - max_execution_time = 900 - request_terminate_timeout = 900 - max_input_time = 1800 - max_input_vars = 5000 + max_execution_time = 300 + request_terminate_timeout = 300 + max_input_time = 600 + max_input_vars = 10000 # ─── PHP-FPM pool for >1 GB RAM ──────────────────────────── - PHPFPMREQ=5000 + PHPFPMREQ=0
Let’s go over the changes. From shell type and enter vi /etc/rc.php_ini_setup
and compare your settings with the recommendations below.
1. Increase OPCache Size
By default, pfSense allocates a max of 50 MB for PHP’s OPCache on systems with at least 1 GB of RAM. On an appliance with plenty of RAM, you can increase that to avoid unwanted cache pruning and keep compiled scripts in memory longer:
# ── in /etc/rc.php_ini_setup ── if [ "$AVAILMEM" -gt "784" ]; then OPCACHEMEMSIZE="100" fi
With 8 GB installed, a 100 MB of PHP OPCache cache costs almost nothing but reduces the likelihood of OPCache restarts. Especially useful once you add more pfSense packages, and there are many useful packages available.
2. Tighten PHP-ini Timeouts, Raise Input Vars
pfSense’s default PHP timeouts let scripts run (and hang) for 15–30 minutes. You can safely trim them for WebUI tasks, and double the max_input_vars
to handle larger config forms:
max_execution_time = 300 request_terminate_timeout = 300 max_input_time = 600 max_input_vars = 10000
Here’s my reasoning (if I find any bugs over time, I will be sure to update):
- Lowering max_execution_time and request_terminate_timeout to 5 minutes is more than enough for any legitimate WebUI task to complete, while making it faster to clean up stuck processes.
- Dropping max_input_time to 10 minutes keeps input processing generous but avoids unnecessarily long lockups on stalled uploads or bad sessions.
- Doubling max_input_vars to 10,000 prepares the system to handle larger config saves without hitting the default input variable limits, especially if you add more firewall rules, NAT settings, or packages like pfBlockerNG.
3. Use ‘pm static’ for max performance
In a previous article on PHP-FPM performance tuning, I wrote:
“I have pm.max_requests set extremely high because this is a production server with no PHP memory leaks. You can use pm.max_requests = 0 with static if you have 110% confidence in your current and future PHP scripts. However, it’s recommended to restart scripts over time.”
So I have my pfSense pm.max_requests=0
(PHPFPMREQ). However, I would just recommend increasing the default ‘5000’ limit (frequency of restarts/clearing process cache). A fixed (“static”) pool of PHP-FPM workers avoids on-the-fly spawning latency of hitting max requests. I updated the template for systems with more than >1 GB of memory from:
PHPFPMREQ=5000
Changed to:
PHPFPMREQ=50000 fi
Next, this is the block of code responsible for setting the process manager to ondemand
, dynamic
or static
:
if [ $REALMEM -lt 350 ]; then /bin/cat >> /usr/local/lib/php-fpm.conf <<EOF pm = ondemand pm.process_idle_timeout = $PHPFPMIDLE pm.max_children = $PHPFPMMAX pm.max_requests = $PHPFPMREQ EOF elif [ $REALMEM -gt 1000 ]; then /bin/cat >> /usr/local/lib/php-fpm.conf <<EOF pm = dynamic pm.process_idle_timeout = $PHPFPMIDLE pm.max_children = $PHPFPMMAX pm.start_servers = $PHPFPMSTART pm.max_requests = $PHPFPMREQ pm.min_spare_servers=1 pm.max_spare_servers= $PHPFPMSPARE EOF else /bin/cat >> /usr/local/lib/php-fpm.conf <<EOF pm = static pm.max_children = $PHPFPMMAX pm.max_requests = $PHPFPMREQ EOF
To switch to static
process manager, I changed this line:
elif [ $REALMEM -gt 1000 ]; then
to this:
elif [ $REALMEM -gt 100000 ]; then
4. Putting It All Together
After restarting PHP-FPM, verify your settings. To confirm which values actually made it into your running config, inspect the files after PHP-FPM restart:
Check the generated php-ini
grep -E 'opcache.memory_consumption|max_execution_time|request_terminate_timeout|max_input_time|max_input_vars' /usr/local/etc/php.ini
Check the active PHP-FPM pool file
grep -E 'pm =' /usr/local/lib/php-fpm.conf
You should see pm = static
—showing which manager mode is in use.
Verify via the PHP-FPM status page
curl -k https://127.0.0.1/status
Look for “process manager: static” and your max_children
value. Note: That the PHPFPMIDLE
(pm.process_idle_timeout) setting only applies on dynamic
and ondemand
, it’s by design ignored by static
pm. The static process manager then keeps the number of running processes most and stable without any overhead.
If you have PHP-FPM crashes or otherwise unstable Web UI, avoid these tweaks.
Next Steps:
• If you install heavy packages or multiple admins using the Web UI concurrently (unlikely), consider raising PHPFPMMAX
to 10–12.
• Revisit and increase PHPFPMREQ
after stability is proven. Longer lifespans mean less cache churn but more risk of memory leaks.
With OPCache, tightened timeouts, generous input limits, and a static worker pool, your pfSense PHP-FPM setup will deliver reliable, long-term performance. If login today and then two weeks later, you will benefit from cache. With the default ondemand and dynamic it means after an idle timeout of a few mins to 1 hour, things reset and clear.
Making these pfSense PHP and PHP-FPM tweaks permanent
Any edits you make under /etc/rc.php_ini_setup
or to the generated files in /usr/local
will get blown away the next time pfSense gets updated. To make your tweaks stick, put them into a patch or a boot-time script that’s stored in your config—pfSense will reapply it every reboot or upgrade, revert if broken after update:
Install the System Patches package.
Create a new patch targeting src/etc/rc.php_ini_setup
.
Paste your diffs (the changes to OPCACHEMEMSIZE, php-ini values, the REALMEM thresholds, etc.):
--- etc/rc.php_ini_setup +++ etc/rc.php_ini_setup @@ -92,7 +92,7 @@ -if [ "$AVAILMEM" -gt "784" ]; then - OPCACHEMEMSIZE="50" +if [ "$AVAILMEM" -gt "784" ]; then + OPCACHEMEMSIZE="100" fi @@ -115,7 +115,7 @@ -max_execution_time = 900 -request_terminate_timeout = 900 -max_input_time = 1800 -max_input_vars = 5000 +max_execution_time = 300 +request_terminate_timeout = 300 +max_input_time = 600 +max_input_vars = 10000 @@ -170,7 +170,7 @@ -elif [ ${REALMEM} -gt 1000 ]; then - PHPFPMMAX=8 - PHPFPMIDLE=60 - PHPFPMSTART=2 - PHPFPMSPARE=7 - PHPFPMREQ=5000 +elif [ ${REALMEM} -gt 1000 ]; then + PHPFPMREQ=500 fi @@ -200,1 +200,1 @@ -elif [ $REALMEM -gt 1000 ]; then +elif [ $REALMEM -gt 100000 ]; then
Save and Apply.
The patch lives inside your config.xml
, so it’s automatically reapplied on upgrades. Using System Patches in pfSense is designed to be safe and transparent. Failed patches are clearly flagged, do not silently break your system. This makes the process upgrade-safe and user-friendly. Some of the things I’m enjoying already about pfSense.