Runtimes Intermediate

Resolving PHP Maximum Execution Time Exceeded on macOS Local Environment

Troubleshoot and fix the 'PHP maximum execution time of 30 seconds exceeded' error on macOS for Homebrew and Docker setups, optimizing performance.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

Troubleshoot and fix the 'PHP maximum execution time of 30 seconds exceeded' error on macOS for Homebrew and Docker setups, optimizing performance.

This guide addresses the common "PHP maximum execution time exceeded" error encountered by developers on their macOS local development environments. While typically a symptom of inefficient code, a low max_execution_time setting can prematurely terminate legitimate long-running scripts, especially during tasks like data imports, image processing, or complex report generation. Understanding how to correctly adjust this limit and where to configure it within your macOS setup (whether using Homebrew, Docker, or other local server stacks) is crucial for smooth development.

Symptom & Error Signature

When a PHP script runs for longer than the configured max_execution_time, PHP will terminate its execution. This typically manifests in your browser as a blank page, an HTTP 500 Internal Server Error, or a partial page load. The specific error message will often appear in your web server's error logs (Apache error_log, Nginx error.log) or PHP's FPM/CLI error logs.

Here's a typical error signature you might encounter:

[timestamp] PHP Fatal error: Maximum execution time of 30 seconds exceeded in /path/to/your/script.php on line 123

Or, if caught by the web server (e.g., Nginx acting as a proxy to PHP-FPM):

[timestamp] [error] 1234#5678: *9999 upstream timed out (60: Operation timed out) while reading response header from upstream, client: 127.0.0.1, server: localhost, request: "GET /long-running-task.php HTTP/1.1", upstream: "fastcgi://unix:/var/run/php/php8.2-fpm.sock:", host: "localhost"

Root Cause Analysis

The "PHP maximum execution time of N seconds exceeded" error stems from a hard limit set within the PHP configuration (php.ini) that dictates how long a script is allowed to run. By default, this is often 30 seconds.

The underlying reasons for this error can be categorized as:

  1. Insufficient max_execution_time: The configured max_execution_time is too low for a legitimate task. This is common during initial setup or when a new feature requires more processing time than anticipated (e.g., bulk data operations, complex API calls, large file uploads with processing).
  2. Inefficient Code: The PHP script itself is poorly optimized, performing too many database queries, looping excessively, or making slow external API requests without proper timeouts or asynchronous handling.
  3. Web Server/Proxy Timeouts: Even if PHP's max_execution_time is increased, the web server (Apache, Nginx) or a proxy sitting in front of PHP-FPM might have its own timeout limits (e.g., proxy_read_timeout for Nginx, Timeout for Apache) that terminate the connection before PHP can finish.
  4. Resource Exhaustion: While not directly a timeout, a script consuming excessive memory or CPU might slow down to the point where it hits the execution time limit.

In a macOS local development environment, the most common immediate cause is often simply a default max_execution_time that is too restrictive for certain development tasks.

Step-by-Step Resolution

The resolution involves adjusting the PHP execution time limit, potentially increasing web server timeouts, and as a long-term solution, optimizing the problematic code.

1. Locate the Correct php.ini File

The critical first step is identifying the php.ini file being loaded by your PHP installation. This varies depending on how PHP is installed on your macOS system.

a. For Homebrew PHP Installations:

If you've installed PHP using Homebrew (e.g., brew install [email protected]), you can find the loaded php.ini path by running:

php --ini

This command will output several paths, typically showing a "Loaded Configuration File" and "Scan for additional .ini files in". The "Loaded Configuration File" is the one you need to edit. Example output:

Configuration File (php.ini) Path: /usr/local/etc/php/8.2
Loaded Configuration File:         /usr/local/etc/php/8.2/php.ini
Scan for additional .ini files in: /usr/local/etc/php/8.2/conf.d
Additional .ini files parsed:      (none)

In this case, you would edit /usr/local/etc/php/8.2/php.ini. The exact path will vary with your PHP version.

b. For Docker-based PHP Installations:

If you're using Docker for your local development, the php.ini file resides inside your PHP container.

First, identify your PHP container ID or name:

docker ps

Then, execute php --ini inside the container:

docker exec -it <container_name_or_id> php --ini

This will give you the path to the php.ini inside the container. You'll typically find it in /usr/local/etc/php/php.ini, /etc/php/<PHP_VERSION>/cli/php.ini, or /etc/php/<PHP_VERSION>/fpm/php.ini.

To edit it, you have a few options:

  • Directly via docker exec (temporary, not recommended for persistent changes):

    docker exec -it <container_name_or_id> vi /path/to/php.ini
    
  • Modify your Dockerfile or docker-compose.yml (recommended for persistent changes): This is the preferred method for Docker. You can map a custom php.ini from your host into the container or use a custom Dockerfile to COPY an adjusted php.ini.

    Example docker-compose.yml volume mount:

    # docker-compose.yml
    version: '3.8'
    services:
      php:
        build:
          context: .
          dockerfile: Dockerfile-php
        volumes:
          - ./php-config/php.ini:/usr/local/etc/php/php.ini # Mount custom php.ini
          - ./:/var/www/html
        # ... other configurations
    

    Then, create php-config/php.ini on your host machine.

2. Adjust max_execution_time in php.ini

Once you've located the correct php.ini, open it with a text editor.

# For Homebrew (example for PHP 8.2)
sudo nano /usr/local/etc/php/8.2/php.ini

# For Docker, if using a mounted volume, edit the host file:
nano ./php-config/php.ini

Search for the max_execution_time directive. It's usually found in the "Resource Limits" section.

; Maximum execution time of each script, in seconds
; http://php.net/max-execution-time
max_execution_time = 30

Change 30 to a higher value. For local development, 300 (5 minutes) or 600 (10 minutes) is often sufficient for most long-running tasks. Be cautious not to set an excessively high value (e.g., 0 for no limit) on production environments, as this can mask underlying performance issues or enable denial-of-service attacks.

max_execution_time = 300

Always restart your PHP-FPM service (if applicable) and/or your web server after modifying php.ini for changes to take effect. For Docker, this often means restarting the container.

3. Configure Web Server Timeouts (Nginx & Apache)

If your PHP setup uses a web server (Nginx or Apache) as a proxy for PHP-FPM, you might also need to adjust its timeout settings, as these can override PHP's max_execution_time by terminating the connection prematurely.

a. Nginx Configuration:

If Nginx is proxying requests to PHP-FPM (which is typical), edit your Nginx server block configuration. This file is usually located in /usr/local/etc/nginx/servers/your_site.conf for Homebrew Nginx, or within your Docker setup's Nginx configuration (e.g., /etc/nginx/conf.d/default.conf inside the Nginx container).

Add or modify the fastcgi_read_timeout directive within your location ~ .php$ block:

# Nginx site configuration (e.g., your_site.conf)
server {
    listen 80;
    server_name localhost;
    root /var/www/html; # Adjust as per your project's root

    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ .php$ {
        fastcgi_split_path_info ^(.+.php)(/.+)$;
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; # Adjust socket path as needed
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;

        # Add or adjust these directives
        fastcgi_read_timeout 300s; # Should be equal to or greater than php's max_execution_time
    }

    # ... other configurations
}

The fastcgi_read_timeout should be set to a value equal to or greater than your max_execution_time in php.ini. If it's lower, Nginx will timeout before PHP.

b. Apache Configuration:

For Apache, if you're using mod_php, the php.ini setting is usually sufficient. If you're using mod_proxy_fcgi to connect to PHP-FPM, or simply the default mod_php with other Apache timeouts, you might need to adjust:

Edit your Apache httpd.conf or a virtual host configuration file (e.g., httpd-vhosts.conf). These are typically found in /usr/local/etc/httpd/ for Homebrew Apache, or within your Docker setup's Apache configuration.

# Apache Virtual Host or httpd.conf
<VirtualHost *:80>
    ServerName localhost
    DocumentRoot "/path/to/your/webroot" # Adjust as needed

    <Directory "/path/to/your/webroot">
        AllowOverride All
        Require all granted
    </Directory>

    # For mod_proxy_fcgi (if connecting to PHP-FPM)
    <FilesMatch .php$>
        SetHandler "proxy:fcgi://127.0.0.1:9000" # Adjust address/port if needed
        ProxyTimeout 300 # Should be equal to or greater than php's max_execution_time
    </FilesMatch>

    # Global Apache Timeout
    Timeout 300 # This is a global timeout for client connections, adjust if needed
</VirtualHost>

After modifying Nginx or Apache configuration, you must restart the respective web server for changes to take effect.

4. Restart PHP-FPM and Web Server Services

After making configuration changes, you need to restart the relevant services.

a. For Homebrew PHP & Web Servers:

If you are running PHP-FPM via Homebrew's [email protected] service and Apache/Nginx are also installed via Homebrew:

# Restart PHP-FPM for a specific PHP version (e.g., PHP 8.2)
brew services restart [email protected]

# Restart Nginx
brew services restart nginx

# Restart Apache (if using Homebrew Apache)
brew services restart httpd
# Or if using system Apache with Homebrew PHP:
sudo apachectl restart

b. For Docker-based Environments:

If your PHP and web server are running in Docker containers, you need to restart the containers:

# If using docker-compose
docker-compose restart php nginx # Or just 'docker-compose restart' to restart all services

# If using individual Docker containers
docker restart <php_container_name_or_id>
docker restart <nginx_container_name_or_id> # Or apache_container

5. Verify the Changes

To confirm your max_execution_time has been updated, you can create a small phpinfo.php file in your web root:

<?php
phpinfo();
?>

Access this file in your browser (e.g., http://localhost/phpinfo.php). Search for max_execution_time. It should reflect your new value.

6. Optimize the PHP Code (Long-Term Solution)

While increasing timeouts can resolve the immediate error, it's often a band-aid solution. For critical and long-running tasks, consider optimizing your code:

  • Refactor Long Operations: Break down large tasks into smaller, manageable chunks.
  • Asynchronous Processing: Use message queues (e.g., RabbitMQ, Redis, Amazon SQS) and background workers (e.g., Supervisord, systemd, Laravel Queue, Symfony Messenger) to process long jobs outside of the user's web request.
  • Database Optimization: Ensure your database queries are efficient, use appropriate indexes, and avoid N+1 query problems.
  • External API Timeouts: Implement timeouts for external API calls to prevent them from hanging indefinitely.
  • Caching: Cache results of expensive operations.
  • Profiling: Use tools like Xdebug or Blackfire.io to profile your PHP code and identify bottlenecks.

Indefinitely increasing max_execution_time without addressing underlying code inefficiencies can lead to poor user experience, resource exhaustion on your server, and potential security vulnerabilities in production environments. Always prioritize code optimization over simply extending timeouts.

👨‍💻

Johnathon Wheeler

Senior Systems Architect & DevOps Engineer • Austin, TX

Connect on LinkedIn

Johnathon has over 16 years of hands-on experience designing, debugging, and scaling Linux web hosting stacks, container clusters, and high-availability database architectures. Every guide on ButItWorkedLocal is independently tested against Debian 12, Ubuntu 24.04/22.04 LTS, Rocky Linux, and Docker environments to guarantee reproducibility in production.

🛡️

Our Production Verification Guarantee

Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.