Resolving ‘PHP maximum execution time of 30 seconds exceeded’ in WSL2 Ubuntu

Fix PHP max execution time timeouts in WSL2 Ubuntu. Learn to adjust php.ini, Nginx, and FPM configurations for long-running scripts.


Fix PHP max execution time timeouts in WSL2 Ubuntu. Learn to adjust php.ini, Nginx, and FPM configurations for long-running scripts.

When developing or hosting PHP applications within your Windows Subsystem for Linux 2 (WSL2) Ubuntu environment, you might encounter a frustrating timeout error: "PHP maximum execution time of 30 seconds exceeded." This typically occurs when a PHP script, such as a large data import, complex report generation, or image processing task, takes longer than its default allotted time to complete. Rather than waiting indefinitely, the PHP engine terminates the script, preventing resource exhaustion but often leading to a broken user experience or failed background process.

This guide will walk you through diagnosing and resolving this common issue by adjusting timeout settings across PHP, PHP-FPM, and Nginx in your WSL2 Ubuntu setup, ensuring your long-running scripts execute successfully.

Symptom & Error Signature

Users typically experience a blank page, an HTTP 504 Gateway Timeout error in their browser, or a direct fatal error message if running the script via CLI. Here are the common log entries and browser outputs you might encounter:

Browser Error (Nginx 504 Gateway Timeout):

<html>
<head><title>504 Gateway Time-out</title></head>
<body>
<center><h1>504 Gateway Time-out</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>

Nginx Error Log (/var/log/nginx/error.log):

2023/10/27 10:30:45 [error] 1234#1234: *123 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 172.20.10.5, server: yourdomain.test, request: "GET /long_process.php", upstream: "fastcgi://unix:/var/run/php/php8.1-fpm.sock:", host: "yourdomain.test"

PHP-FPM Log (/var/log/php/php8.1-fpm.log or similar):

[27-Oct-2023 10:30:45] WARNING: [pool www] child 1234, script '/var/www/html/long_process.php' (request: "GET /long_process.php") execution timed out (30.123 sec), terminating
[27-Oct-2023 10:30:45] WARNING: [pool www] child 1234 exited on signal 9 (SIGKILL) after 30.123 seconds from start

Direct PHP CLI Output:

PHP Fatal error: Maximum execution time of 30 seconds exceeded (tried to allocate 20480 bytes) in /var/www/html/long_process.php on line 15

Root Cause Analysis

The "maximum execution time exceeded" error is a safeguard mechanism implemented across different layers of your web stack to prevent runaway scripts from consuming excessive server resources. Several components contribute to the overall execution time limit:

  1. PHP's max_execution_time: This is the most direct cause. By default, PHP scripts are allowed 30 seconds to run. This setting is defined in php.ini and aims to terminate scripts that get stuck in infinite loops or take too long, freeing up PHP workers.

  2. PHP-FPM's request_terminate_timeout: When using PHP-FPM (FastCGI Process Manager), this setting in the FPM pool configuration (www.conf or a custom pool file) dictates how long PHP-FPM will wait for a child process to finish processing a request. If request_terminate_timeout is set lower than or equal to PHP's max_execution_time, PHP-FPM might terminate the script even if PHP itself hasn't hit its internal limit. A value of 0 means 'off', allowing PHP's max_execution_time to be the sole governor.

  3. Web Server (Nginx) fastcgi_read_timeout: Nginx acts as a reverse proxy, forwarding requests to PHP-FPM. The fastcgi_read_timeout directive in Nginx determines how long Nginx will wait for a response from the FastCGI server (PHP-FPM). If a PHP script takes longer than this Nginx timeout, Nginx will close the connection and return a 504 Gateway Timeout error, even if the PHP script is still running and hasn't hit its max_execution_time or request_terminate_timeout. This is a very common scenario.

  4. Resource Exhaustion: While not a direct timeout, scripts that consume excessive CPU or memory can slow down to the point where they are more likely to hit the predefined time limits. Insufficient memory_limit in php.ini can also cause script termination.

In a WSL2 environment, these layers behave identically to a native Linux server. Diagnosing the exact point of failure often involves checking logs from Nginx and PHP-FPM in conjunction.

Step-by-Step Resolution

To resolve the "maximum execution time exceeded" error, you need to adjust timeout settings in a coordinated manner across PHP, PHP-FPM, and Nginx. Always remember to restart the relevant services after making configuration changes.

Prerequisites:

  • Access to your WSL2 Ubuntu terminal.
  • sudo privileges.
  • Knowledge of your PHP version (e.g., php8.1). You can check this with php -v.

1. Adjust PHP's max_execution_time

First, increase the primary PHP execution time limit.

  1. Locate the php.ini for PHP-FPM: The path typically follows /etc/php/<version>/fpm/php.ini. For example, for PHP 8.1, it would be /etc/php/8.1/fpm/php.ini.

    sudo nano /etc/php/8.1/fpm/php.ini
    

    Replace 8.1 with your PHP version if different.

  2. Modify max_execution_time: Find the line max_execution_time = 30 and change 30 to a higher value, such as 180 (3 minutes) or 300 (5 minutes). Choose a value that accommodates your script's needs, but avoid excessively long times without proper justification.

    max_execution_time = 180
    
  3. Restart PHP-FPM: After saving the php.ini file, you must restart the PHP-FPM service for the changes to take effect.

    sudo systemctl restart php8.1-fpm
    

    Again, replace 8.1 with your PHP version.

If you also run PHP scripts via the command line (e.g., php artisan commands), you might need to adjust the max_execution_time in /etc/php/<version>/cli/php.ini as well. For CLI scripts, setting max_execution_time = 0 (meaning no time limit) is common.

2. Configure PHP-FPM's request_terminate_timeout

Next, ensure PHP-FPM doesn't prematurely terminate the request.

  1. Locate the PHP-FPM pool configuration file: The default pool configuration for web requests is usually /etc/php/<version>/fpm/pool.d/www.conf.

    sudo nano /etc/php/8.1/fpm/pool.d/www.conf
    

    Adjust the PHP version as needed.

  2. Modify request_terminate_timeout: Find the line ;request_terminate_timeout = 0. Uncomment it (remove the leading semicolon) and set its value to be equal to or greater than the max_execution_time you set in php.ini. If 0, it means no timeout from FPM side, and max_execution_time takes precedence. For consistency, it's often set to match.

    request_terminate_timeout = 180
    

    A value of 0 means PHP-FPM will not set a timeout and will rely solely on PHP's max_execution_time. This is often a good option for clarity. However, if your PHP processes hang without hitting max_execution_time (e.g., a deadlock), FPM's timeout can act as a secondary safety net.

  3. Restart PHP-FPM: Even if you only changed www.conf, a full PHP-FPM service restart is necessary.

    sudo systemctl restart php8.1-fpm
    

3. Increase Nginx fastcgi_read_timeout

This is a critical step, as Nginx often times out before PHP or PHP-FPM.

  1. Locate your Nginx site configuration file: Your site's configuration is typically found in /etc/nginx/sites-available/your_site_name.conf.

    sudo nano /etc/nginx/sites-available/your_site.conf
    

    Replace your_site.conf with the actual name of your Nginx configuration file for the relevant site.

  2. Add or modify fastcgi_read_timeout: Inside the location ~ .php$ block (or the appropriate location block that handles PHP processing), add or modify the fastcgi_read_timeout directive. This value must be equal to or greater than your max_execution_time and request_terminate_timeout values to ensure Nginx waits long enough for PHP to respond.

    location ~ .php$ {
        include snippets/fastcgi-php.conf; # Common include for PHP setup
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock; # Adjust PHP version and socket path
        fastcgi_read_timeout 180; # Must be >= PHP's max_execution_time and FPM's request_terminate_timeout
        # Optional: Increase buffer sizes for large outputs
        fastcgi_buffers 16 16k;
        fastcgi_buffer_size 32k;
    }
    
  3. Test Nginx configuration: Before reloading, always test Nginx's configuration syntax to catch errors.

    sudo nginx -t
    

    You should see test is successful. If there are errors, correct them before proceeding.

  4. Reload Nginx: Apply the new Nginx configuration. A reload is usually sufficient.

    sudo systemctl reload nginx
    

    If reload doesn't seem to apply changes, a restart might be necessary, but reload is generally preferred to avoid dropping active connections.

Ensure that no higher-level Nginx configuration (e.g., in http or server blocks) sets a fastcgi_read_timeout to a lower value, as location-specific directives override global ones, but a forgotten global setting could still interfere. If you have a proxy setup (e.g., Nginx proxying to another Nginx or Docker container), you might also need to adjust proxy_read_timeout.

4. (Optional) Address CLI Scripts

If your timeout occurs when running PHP scripts directly from the command line (e.g., php artisan migrate, Composer commands), you need to adjust the php.ini specific to the CLI.

  1. Locate the CLI php.ini:

    sudo nano /etc/php/8.1/cli/php.ini
    
  2. Modify max_execution_time and memory_limit: For CLI scripts, it's common to set max_execution_time = 0 to disable the time limit, as CLI scripts often run as background jobs without user interaction. You might also want to increase memory_limit for heavy CLI tasks.

    max_execution_time = 0
    memory_limit = 512M # Or higher, depending on your needs
    

    No service restart is required for CLI php.ini changes; they take effect immediately on the next CLI execution.

5. Verify Your Changes

To confirm your changes are effective, you can create a simple PHP script designed to exceed the old timeout.

  1. Create a test script (e.g., test_timeout.php) in your web root:

    <?php
    // It's good practice to log for debugging
    error_log("Starting long script execution at " . date('Y-m-d H:i:s'));
    
    // Try to set time limit to 0 if running in browser and allowed by php.ini
    // For CLI, php.ini is typically preferred for max_execution_time = 0
    set_time_limit(0); 
    
    $sleep_duration = 120; // Sleep for 2 minutes (120 seconds)
    
    echo "Script will sleep for " . $sleep_duration . " seconds...<br>";
    flush(); // Try to send output immediately (won't work if output buffering is heavily used)
    
    sleep($sleep_duration);
    
    echo "Script completed after " . $sleep_duration . " seconds!";
    error_log("Finished long script execution at " . date('Y-m-d H:i:s'));
    ?>
    
  2. Access the script via your browser: http://yourdomain.test/test_timeout.php If configured correctly, the script should complete after 120 seconds, displaying "Script completed after 120 seconds!". If you still get a timeout, check your Nginx and PHP-FPM logs for specific errors.

6. Consider Application-Level Optimizations

While increasing timeouts resolves the immediate error, it's crucial to acknowledge that very long execution times often indicate potential inefficiencies in the application's code.

  • Optimize Database Queries: Long-running scripts frequently involve heavy database operations. Profile your queries and ensure appropriate indexing.
  • Refactor Long Loops/Processes: Break down large tasks into smaller, manageable chunks.
  • Utilize Asynchronous Processing/Queue Systems: For truly long-running operations (e.g., sending bulk emails, generating large reports, processing video), consider offloading them to background jobs using message queues (e.g., Redis Queue, RabbitMQ, Beanstalkd) and dedicated workers. This frees up the web server for immediate requests.
  • Batch Processing: Instead of processing thousands of items in one go, process them in smaller batches.

By systematically adjusting these configurations and considering application-level optimizations, you can effectively manage PHP execution timeouts in your WSL2 Ubuntu development and hosting environment.