TechEarl

PHP Memory Limit: How to Fix 'Allowed Memory Size Exhausted'

Increase the PHP memory_limit via php.ini, .htaccess, ini_set(), the -d CLI flag, PHP-FPM pool config, or wp-config.php. Covers the modern PHP 8 defaults, the OPcache caveat, and the Composer/PHPUnit/WordPress out-of-memory patterns.

Ishan KarunaratneIshan Karunaratne⏱️ 14 min readUpdated
Increase php memory_limit via php.ini, .htaccess, ini_set, the -d CLI flag, PHP-FPM, or wp-config.php. Modern PHP 8.x defaults, OPcache caveat, and the Composer/PHPUnit/WordPress OOM patterns.

The Fatal error: Allowed memory size of N bytes exhausted error means the script tried to allocate more memory than the PHP memory_limit directive permits. The fix is to raise that limit — but where you raise it depends on which PHP runtime (web SAPI, FPM pool, or CLI) you're hitting and how your app is deployed (Apache + mod_php, Nginx + PHP-FPM, command-line cron, Composer, WordPress, Laravel artisan). Below: every override mechanism in order from "single script" to "server-wide", with the modern PHP 8.x defaults, the OPcache caveat that explains why your php.ini change "doesn't take effect", and the specific patterns for Composer out-of-memory, PHPUnit, WP-CLI, and Laravel artisan migrations.

What is the PHP memory_limit and how do I increase it?

The PHP memory_limit is the maximum amount of memory a single PHP script is allowed to allocate. The PHP 8.x default is 128M (changed from -1/unlimited in older versions). When a script exceeds it, PHP emits Fatal error: Allowed memory size of N bytes exhausted (tried to allocate X bytes) and terminates. To raise the limit you can: set memory_limit = 256M in php.ini (server-wide), ini_set('memory_limit', '256M') at the top of a PHP file (per-script), php_value memory_limit 256M in .htaccess (per-directory under Apache), php_admin_value[memory_limit] = 256M in a PHP-FPM pool config (per-pool under Nginx/FPM), or php -d memory_limit=256M script.php on the command line. The most-common-by-runtime: php.ini for web SAPI, FPM pool config for production FPM, the -d flag for one-off CLI invocations.

Try it with your own values

Set your target memory limit, PHP version, and the script name once. Shell and config examples below update so you can copy and run.

Jump to:

PHP 8.x defaults and shorthand

SettingDefault in PHP 8.xNotes
memory_limit (web SAPI)128MSet in the bundled php.ini-production
memory_limit (CLI SAPI)128MSet in php-cli.ini on Debian-family distros
-1 (unlimited)not the defaultWas the default in PHP 5.0; valid since PHP 5.1.0

PHP accepts shorthand suffixes (since 5.1.0, current in every supported version):

  • K or k — kibibytes (1024 bytes)
  • M or m — mebibytes
  • G or g — gibibytes
  • No suffix — bytes

So 1G, 1024M, 1048576K, and 1073741824 are all equivalent. 1G is the readable form.

memory_limit = -1 means unlimited (let the OS decide). Useful for Composer or PHPUnit but never for web-facing PHP — a runaway script can starve the entire server.

Find the right php.ini

Before editing anything, find which php.ini is actually loaded — this is the source of "I changed it but nothing happened" half the time.

bash
# From CLI
php --ini

# Output:
# Configuration File (php.ini) Path: /etc/php/:php_version/cli
# Loaded Configuration File:         /etc/php/:php_version/cli/php.ini
# Scan for additional .ini files in: /etc/php/:php_version/cli/conf.d
# Additional .ini files parsed:      /etc/php/:php_version/cli/conf.d/10-mysqlnd.ini, ...

For the web SAPI (the one your browser hits), put <?php phpinfo(); in a test file, load it through the web server, and look for "Loaded Configuration File". It's almost always a DIFFERENT file from the CLI php.ini — e.g., /etc/php/8.4/fpm/php.ini for FPM, /etc/php/8.4/apache2/php.ini for mod_php.

Edit the right one. On most Debian/Ubuntu setups there are at least two (cli + fpm or cli + apache2). On Docker, the file is typically inside the container at /usr/local/etc/php/php.ini.

Method 1: php.ini (server-wide)

The canonical way. Edit the loaded php.ini and set:

ini
memory_limit = 256M

Then reload the SAPI that's serving requests:

bash
# Apache + mod_php
sudo systemctl reload apache2

# PHP-FPM (Nginx or Apache + FPM proxy)
sudo systemctl reload php:php_version-fpm

CLI changes take effect on the next php invocation — no reload needed.

A subtle note: most production php.ini files inherit a memory_limit setting from /etc/php/8.4/{fpm,cli,apache2}/conf.d/*.ini snippets that are loaded after the main file. If your edit isn't taking effect, run php -i | grep memory_limit and check the "INI Files" line at the top to see the full include order.

Method 2: ini_set() in PHP code (per-script)

Add at the top of the script (before any allocations):

php
<?php
ini_set('memory_limit', '256M');

// ... rest of your script

This only affects the current script execution. Useful when one specific endpoint (image upload, CSV import, report generator) needs more memory than the global limit, without lifting the limit for every request.

It doesn't always work. If the host disabled ini_set for security reasons (some shared hosts do), or if memory_limit is in disable_functions / disable_classes exclusions, the call silently fails. Always check with phpinfo() or ini_get('memory_limit') after the call:

php
ini_set('memory_limit', '256M');
echo ini_get('memory_limit');  // should print "256M"

For long-running scripts, raise the limit BEFORE the memory-heavy work — ini_set doesn't help after PHP has already raised the fatal error.

Method 3: .htaccess (Apache, per-directory)

Only works on Apache + mod_php (not FPM). Add to .htaccess in your document root or a specific subdirectory:

apache
php_value memory_limit 256M

The setting cascades into subdirectories. If you need to combine with other PHP overrides:

apache
php_value memory_limit 256M
php_value upload_max_filesize 32M
php_value post_max_size 32M
php_value max_execution_time 300

This silently fails under PHP-FPM because FPM doesn't read .htaccess. If you're on Nginx + FPM, use Method 4 instead. If you're on Apache + FPM (the modern Apache default), also use Method 4.

Method 4: PHP-FPM pool config (Nginx)

The right way for modern Nginx + FPM stacks. Find the pool config — typically /etc/php/8.4/fpm/pool.d/www.conf. Add or modify:

ini
php_admin_value[memory_limit] = 256M

Use php_admin_value (admin) for values you don't want overridden by ini_set in user code. Use php_value if user code should be able to lower the limit but not raise it.

For per-application limits (multiple apps sharing one FPM master, each with its own pool):

ini
; /etc/php/8.4/fpm/pool.d/wordpress.conf
[wordpress]
user = www-data
group = www-data
listen = /run/php/php8.4-wordpress.sock
php_admin_value[memory_limit] = 512M

; /etc/php/8.4/fpm/pool.d/laravel.conf
[laravel]
user = www-data
group = www-data
listen = /run/php/php8.4-laravel.sock
php_admin_value[memory_limit] = 256M

Reload FPM:

bash
sudo systemctl reload php:php_version-fpm

Method 5: php -d flag (one-off CLI)

For Composer, PHPUnit, WP-CLI, artisan, drush — anything you run on the command line — pass -d memory_limit=... directly:

bash
# Composer install with extra memory
php -d memory_limit=-1 /usr/local/bin/composer install

# PHPUnit on a large suite
php -d memory_limit=:value vendor/bin/phpunit

# WP-CLI
php -d memory_limit=:value $(which wp) plugin update --all

# Laravel artisan
php -d memory_limit=:value artisan migrate --seed

# Or invoke your own script with the limit raised
php -d memory_limit=:value :php_file

-d overrides any setting in php.ini for that single invocation. No reload, no config edit, no side effects on other scripts. This is the right answer for "I need to run THIS command with more memory once".

For Composer specifically, the COMPOSER_MEMORY_LIMIT env var is an alias:

bash
COMPOSER_MEMORY_LIMIT=-1 composer install

Method 6: WordPress wp-config.php

WordPress has its own memory abstraction layered on top of PHP. Two constants in wp-config.php:

php
define('WP_MEMORY_LIMIT', '256M');      // for normal page requests
define('WP_MAX_MEMORY_LIMIT', '512M');  // for admin and cron tasks

WordPress applies these via ini_set on each request — they only work if ini_set is enabled and the underlying php.ini memory_limit isn't lower (PHP's limit is a ceiling; WordPress can only request UP TO that ceiling, not above it).

If you're hitting memory exhaustion specifically in admin (image-library imports, bulk-edit, plugin updates), raise WP_MAX_MEMORY_LIMIT. For front-end requests, WP_MEMORY_LIMIT is the one.

For specific WordPress memory pain points like bulk imports via wp_insert_post, see wp_insert_post Consuming Large Amounts of Memory — sometimes the right answer is fixing the script, not raising the limit.

Method 7: Docker / containers

For an official PHP container (or any image based on it), three ways depending on how the image is built:

(a) Mount a custom php.ini snippet:

yaml
# docker-compose.yml
services:
  app:
    image: php:8.4-fpm
    volumes:
      - ./docker/php-memory.ini:/usr/local/etc/php/conf.d/zz-memory.ini:ro
ini
; ./docker/php-memory.ini
memory_limit = 512M

The zz- prefix ensures the snippet loads last (and wins, since conf.d files load alphabetically).

(b) Use the PHP_INI_DIR and env-driven config in newer official images:

yaml
services:
  app:
    image: php:8.4-fpm
    environment:
      PHP_MEMORY_LIMIT: 512M

Requires the image to honor the env var — most do via a startup shim, but check the image's docs.

(c) Override in the FPM pool:

yaml
volumes:
  - ./docker/www.conf:/usr/local/etc/php-fpm.d/www.conf:ro

Same php_admin_value[memory_limit] = 512M line as Method 4.

For Docker Compose, the host's docker daemon also has a per-container memory cap — if you set PHP to 1G but the container is capped at 256M, the OOM-killer takes the whole container down. Pair PHP memory_limit with mem_limit in compose if you're being precise.

Common out-of-memory patterns

SymptomFix
composer install / composer update OOMCOMPOSER_MEMORY_LIMIT=-1 composer install (or php -d memory_limit=-1)
phpunit OOM on large suitesphp -d memory_limit=1G vendor/bin/phpunit, plus consider --testdox and process isolation
wp_insert_post OOM during bulk importBatch in chunks of 100-500, call wp_cache_flush() and gc_collect_cycles() between batches. See wp_insert_post memory deep-dive
artisan migrate --seed OOM with large seedphp -d memory_limit=512M artisan ..., or chunk seeders with Model::query()->chunkById(1000, ...)
Image processing (GD/Imagick) OOMCalculate (width × height × 4 × 2) bytes minimum; raise limit accordingly OR switch to streaming with imagick's readImageStream
Excel/CSV import OOMUse streaming readers (PhpSpreadsheet's ReadFilter, league/csv's iterator interface) instead of loading the whole file
WordPress admin "white screen of death"Set WP_MAX_MEMORY_LIMIT to 512M in wp-config.php — admin uses the max not the regular limit

The first instinct should be "what is the script doing that needs more memory" — sometimes the real fix is the script, not the limit. Raising memory_limit is a band-aid; fixing the algorithm is the cure.

Verify the change took effect

After any change, confirm the runtime sees it:

bash
# CLI — what php on the command line sees
php -i | grep memory_limit

# Output:
# memory_limit => :value => :value

The first value is the runtime current; the second is the "master" value (from php.ini). If they differ, something has called ini_set after startup.

For the web SAPI, drop a one-line script in your document root:

php
<?php echo ini_get('memory_limit');

Load it via the browser. The value you see is what the web SAPI is using for THIS request. Delete the file after — it's a small information leak.

Troubleshooting: why your change didn't apply

1. Edited the wrong php.ini. Run php --ini from CLI and phpinfo() from the web server. They typically use different files. Edit the right one.

2. PHP-FPM not reloaded. FPM caches the config at startup. After editing the pool file or php.ini, sudo systemctl reload php8.4-fpm. Apache mod_php similarly needs sudo systemctl reload apache2.

3. OPcache holding the old value. OPcache caches compiled PHP bytecode and the directive values are baked in. After a php.ini change, the OPcache might still serve old values for already-cached scripts until they're invalidated. Run opcache_reset() from a script you load, or restart the SAPI.

4. .htaccess silently ignored. PHP-FPM doesn't read .htaccess. If you're on Nginx, or on Apache with FPM proxying, the php_value line in .htaccess is a no-op. Switch to Method 4.

5. ini_set blocked by host. Some shared hosts add memory_limit to a "locked settings" list. ini_get('memory_limit') after ini_set will show the unchanged value. Contact support or switch hosts.

6. The script ran out of memory BEFORE ini_set. If you load a huge file with file_get_contents on line 1 and call ini_set on line 2, line 1 already triggered the fatal. Move the ini_set to the very top, before any allocation.

7. Container memory cap below PHP limit. Docker/Kubernetes will OOM-kill the container before PHP sees the limit. Match the container's mem_limit to PHP's memory_limit (slightly above).

8. php_admin_value in FPM overrides everything. ini_set in user code can't override php_admin_value — that's the point. If you need user code to be able to raise the limit, use php_value in the pool config instead.

What to do next

For specific PHP/WordPress memory situations:

For WordPress administration:

FAQ

See also

TagsPHPmemory_limitPHP-FPMComposerWordPressApacheNginxConfigurationTroubleshooting
Share
Ishan Karunaratne

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts

rsync files modified in the last N days by piping find -print0 into rsync --files-from=- --from0. The relative-path gotcha, the dry run, BSD vs GNU notes, and when rsync filters replace find.

How to rsync Only the Files find Selected

rsync has no native time filter, so the standard trick is to let find pick the files and feed the list to rsync. The one-liner is find ... -print0 | rsync --files-from=- --from0, and the failure mode is always the same: the paths in the list have to be relative to the rsync source argument. The breakdown, the dry run habit, and when rsync's own filters make find unnecessary.

Connect to a GCP Compute Engine VM with plain OpenSSH and no gcloud CLI. Add a public key via instance metadata, ssh to the external IP, configure ~/.ssh/config, plus OS Login and IAP.

How to SSH into a Google Cloud VM Without gcloud

Connect to a GCP VM using plain OpenSSH, no gcloud required. Add a public key to instance metadata, fetch the external IP, and ssh in like any normal Linux box. Plus OS Login, IAP, and a Windows PuTTY path.

Use find -size +100M to list files larger than 100 megabytes. Unit suffixes (c/k/M/G), +/- sign convention, combine with sort -rn to surface the biggest files on disk, and BSD vs GNU rendering differences.

How to Find Files Larger Than a Size with find -size

find . -size +100M lists every file larger than 100 megabytes. The unit suffixes (c, k, M, G), the +/- sign convention, how to combine with sort to find the biggest files on disk, the BSD vs GNU divergence for printing sizes, and the wc -c trick for byte-exact thresholds.