Contents
- 1 Php memory limit explained: why your code keeps running out of breath
- 2 What PHP memory_limit actually is (and what it isn’t)
- 3 The quiet traps that eat your memory
- 4 How to see what PHP actually thinks your limit is
- 5 How to change memory_limit (the grown-up version)
- 6 Memory, frameworks, and CMS: the hidden players
- 7 When increasing memory_limit is the right move
- 8 When increasing memory_limit is a bandage over a wound
- 9 Thinking in streams instead of piles
- 10 The hosting side: RAM, concurrency, and honest limits
- 11 Debugging a real memory_limit failure
- 12 What this all means for teams, hiring, and careers
- 13 Ending in the quiet part
Php memory limit explained: why your code keeps running out of breath
There’s a specific kind of silence in a server room at 2 a.m.
No Slack pings, no new tickets, just the faint hum of machines and that one tab in your browser that still says:
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes)
Some of you can feel that line in your body.
You’ve seen it on production. On Friday. Ten minutes after deploy.
Let’s talk about that thing: PHP memory_limit.
Not with copy-paste config snippets and “just set it to 512M, bro”, but like people who ship real things, with real constraints, and occasionally real panic.
Friends, fellow PHP developers, hiring managers, and anyone wondering whether 256M is “a lot” or “not nearly enough” — this is for you.
What PHP memory_limit actually is (and what it isn’t)
PHP memory_limit is deceptively simple:
It’s the maximum amount of RAM a single PHP script is allowed to allocate.
Per script. Per request. Per process.
Not “per server.” Not “per site.”
Think of it like a speed limit sign. It applies to each car, not to the entire highway.
So if you set:
memory_limit = 256M
that means:
- A single PHP request can use up to 256 MB of RAM
- If it goes over, PHP kills it with a fatal error
- Ten concurrent requests can each use up to 256 MB, and your server may simply… fall over if it doesn’t have enough physical memory
This is why “just increase memory_limit” can be both a fix and a loaded gun.
Where memory goes inside a request
Inside that 128M, 256M, or 1G, PHP has to fit:
- PHP variables (arrays, objects, strings, closures)
- Opcode structures and internals
- Framework overhead (Laravel, Symfony, Drupal, WordPress core)
- Composer autoloader structures
- Third‑party library data (ORM hydration, HTTP clients, etc.)
- Temporary buffers (string concatenation, JSON encode/decode, image manipulation)
And of course, your code.
Every $hugeArray, every json_decode of a 20 MB payload, every SELECT * on a 2M row table hydrated into objects — all of it lives under memory_limit for that one request.
The quiet traps that eat your memory
You almost never see memory_limit failures during a simple GET /healthcheck.
You see them where your code tries to do something “heavy” and “just once in a while”.
The classic landmines:
- Large CSV imports
- Image manipulation, PDFs, video processing
- Report generation (especially “export to Excel”)
- Massive joins or
SELECT *without limits - Caching the wrong thing in memory (e.g., an entire result set instead of IDs)
Let’s walk through a familiar scene.
Scene: the 3 a.m. CSV importer
You wrote a CSV importer. Something like:
$rows = array_map('str_getcsv', file($filePath));
foreach ($rows as $row) {
processRow($row);
}
Feels clean, right?
file()reads the entire file into an arrayarray_mapturns each line into its columns- You iterate, do your business logic, everyone’s happy
Except someone tries to import a 500 MB CSV.
At 3 a.m.
On the smallest production node.
file() cheerfully loads 500 MB into memory, plus PHP’s overhead of storing it in an array, plus the mapped arrays for each row, plus your additional processing. You hit memory_limit like a brick wall.
The fix isn’t “increase memory_limit to 2G” and hope for the best.
The fix is strategy.
A streaming approach:
if (($handle = fopen($filePath, 'r')) !== false) {
while (($row = fgetcsv($handle, 0, ',')) !== false) {
processRow($row);
}
fclose($handle);
}
You trade a bit of convenience for a dramatic drop in memory usage.
Suddenly, 128M is more than enough.
This is the pattern you’ll see again and again:
When you understand memory_limit, you don’t just raise it.
You change how you think about data.
How to see what PHP actually thinks your limit is
Before changing anything, check the current value the same way the server sees it.
Drop this somewhere safe (never leave it in production):
<?php
echo ini_get('memory_limit');
Or use phpinfo() and search for memory_limit.
One important detail: values can come from different layers:
php.ini(global configuration).user.ini(per-directory, especially on shared hosting).htaccess(php_value memory_limit 256Mon Apache)- Application code (e.g.,
ini_set('memory_limit', '512M');) - Framework/CMS overrides (WordPress
WP_MEMORY_LIMIT, Drupal settings, etc.)
Sometimes you “change the memory limit” and nothing happens. That’s usually because:
- You edited a different
php.inithan the one in use (CLI vs FPM vs Apache) - Your CMS/framework overrides the value at runtime
- You forgot to restart the web server or PHP-FPM
This is where experienced PHP developers quietly earn their money. They don’t just change config; they know which layer is actually in control.
How to change memory_limit (the grown-up version)
You’ve seen the snippets:
memory_limit = 256Minphp.iniphp_value memory_limit 256Min.htaccessini_set('memory_limit', '512M');somewhere in code
Let’s go beyond “copy this into a file” and talk about intent.
1. php.ini: the honest, system-level way
If you have real server access:
- On Debian/Ubuntu + Apache:
/etc/php/8.1/apache2/php.ini - On Debian/Ubuntu + PHP-FPM:
/etc/php/8.1/fpm/php.ini - On many panels: somewhere under
/etc/php/or/opt/…
Set:
memory_limit = 256M
Then:
- Restart Apache or PHP-FPM
- Confirm with
phpinfo()orini_get('memory_limit')
This is the cleanest way on a managed server or VPS.
One place of truth. Transparent. Predictable.
2. .user.ini / .htaccess: hosting gymnastics
On shared hosting you often live inside a cage:
- You cannot touch global
php.ini - You can only override via
.user.inior.htaccess
Common patterns:
.user.ini:
memory_limit = 256M
.htaccess:
php_value memory_limit 256M
You drop one of these in your document root, wait a bit (some hosts only re-read periodically), maybe kill PHP processes, and pray.
If you’re hiring PHP developers who will be working in shared hosting or constrained environments, this kind of knowledge isn’t glamorous, but it’s the difference between “our site randomly dies” and “our site has been boringly stable for years”.
3. In app code: scoped and intentional
This is the most powerful and most abusable approach.
ini_set('memory_limit', '512M');
You can use this for specific tasks that genuinely require more memory:
- A rare, heavy reporting endpoint
- A CLI command doing batch processing
- A migration script
Example:
function generateBigReport(): void
{
$oldLimit = ini_get('memory_limit');
ini_set('memory_limit', '512M');
try {
// heavy lifting here
} finally {
ini_set('memory_limit', $oldLimit);
}
}
This is you saying:
“I know this thing is heavy. I’ve thought about it. I’m watching it.”
Not:
“We got an error once, so we changed the global memory_limit from 128M to 2G in production at 11:58 on Friday.”
If you’re building with Laravel, Symfony, Drupal, Magento, WordPress, or any of the major PHP ecosystems, memory_limit is part of a bigger story.
And you’ve probably seen that story in logs.
WordPress
By default, WordPress plays with its own memory limits:
WP_MEMORY_LIMIT(front end / general)WP_MAX_MEMORY_LIMIT(admin / heavy operations)
In wp-config.php you might see:
define('WP_MEMORY_LIMIT', '128M');
define('WP_MAX_MEMORY_LIMIT', '256M');
Even if your PHP memory_limit is 256M, WordPress might still run with less, unless you align these. Some hosts override these defaults, some don’t.
This explains why developers sometimes change php.ini and see no effect — WordPress quietly reigns in memory for itself.
Drupal, Magento, and others
You see similar patterns:
- Drupal’s
settings.phpcan callini_set('memory_limit', '256M'); - Magento often assumes a fairly high limit because of its complexity
These systems are realistic about their footprint.
A “healthy standard” Magento install needing up to 768M is not rare. A WooCommerce-heavy WordPress store often lives comfortably around 256M.
That’s not “bad PHP.” That’s complex behavior, lots of abstraction, and a ton of features moving inside one request.
When increasing memory_limit is the right move
Despite all the warnings, sometimes the correct answer really is:
memory_limit = 512M
You’re not a “bad developer” for doing this. You’re a developer who understands context.
It makes sense to increase memory_limit when:
- You’ve profiled the code and know where the memory goes
- The operations are legitimately heavy (reporting, image processing, machine learning glue, large ETL jobs)
- The server has enough physical RAM, and you’ve thought about concurrency
- You’re hitting the limit for valid workloads, not because of a runaway query or a memory leak
It’s the difference between:
- “We hit memory_limit because we are processing 10,000 high-resolution images in a CLI command every night,” and
- “We hit memory_limit on GET /home because every request loads all users, all posts, all tags, and all categories into collections for no reason.”
When increasing memory_limit is a bandage over a wound
There’s a specific smell to a codebase that uses memory_limit = 1G for a plain brochure site.
You start seeing:
SELECT * FROM huge_tableeverywhere- In-memory joins done in PHP instead of the database
- Eager loading “all the relationships” where you actually need 10 records
- Caching giant arrays in RAM instead of small IDs or aggregates
- Recursion or generators misused, growing arrays indefinitely
Real story: a team I know had a “mystery memory issue.”
One endpoint, moderate traffic, but occasional fatal memory errors.
Turned out, they had code like this:
$allUsers = User::all();
foreach ($allUsers as $user) {
// send notification to a small subset
}
On a table with 3 million users.
The “quick fix” was raising memory_limit. The real fix was:
User::chunk(1000, function ($users) {
foreach ($users as $user) {
// process in smaller batches
}
});
Memory usage dropped. The crashes vanished.
The error disappeared not because they “added more memory”, but because they respected memory.
This is the kind of thinking that separates someone who can tick “PHP” on a resume from someone you trust with a production system.
And that, quietly, is what platforms like Find PHP help surface: who actually understands this stuff, beyond the snippets.
Thinking in streams instead of piles
Once you internalize how PHP memory_limit works, you start writing code differently.
You stop piling data into arrays “for convenience” and start thinking in streams, chunks, and boundaries.
Some practical patterns:
1. Streaming files
Instead of:
$lines = file($path);
foreach ($lines as $line) {
handle($line);
}
You do:
$handle = fopen($path, 'r');
while (($line = fgets($handle)) !== false) {
handle($line);
}
fclose($handle);
Or if you use higher-level abstractions (like Symfony’s StreamedResponse), you lean into them instead of buffering everything into memory before sending.
2. Chunked database processing
Instead of:
$orders = Order::where('status', 'pending')->get();
foreach ($orders as $order) {
processOrder($order);
}
You move towards:
Order::where('status', 'pending')
->chunk(500, function ($orders) {
foreach ($orders as $order) {
processOrder($order);
}
});
Same business logic.
Different memory profile.
One survives high data growth, one doesn’t.
3. Avoiding accidental duplication
Little things add up:
$data = getLargeArray();
$copy = $data; // may behave like copy-on-write, but one modification can double memory
$processed = array_map(fn($item) => transform($item), $data);
If transform() doesn’t need the entire dataset, consider a generator:
function transformStream(iterable $items): iterable
{
foreach ($items as $item) {
yield transform($item);
}
}
Generators, iterators, and lazy collections are your friends when memory_limit is breathing down your neck.
You don’t always need them. But it’s powerful to know they’re there.
4. Beware of “nice” debug tools in production
That beautiful Laravel telescope, or that symfony profiler, or that custom debug dump? It often:
- Captures full requests and responses
- Stores full query logs
- Holds big arrays of context in memory
If memory errors appear only in staging or production and “never locally,” check:
- Are debug or profiling tools disabled?
- Are you logging too much per request?
- Are there dump-and-die leftovers replaced with “temporary” in-memory logging?
A seasoned PHP developer has broken a staging cluster with a debugging tool at least once. The important part is that they remember it.
The hosting side: RAM, concurrency, and honest limits
Memory_limit doesn’t exist in a vacuum. It lives on a machine with real boundaries.
Imagine this:
- You have 4 GB RAM on your server
- You configure PHP-FPM with 20 workers
- You set
memory_limit = 512M
Theoretically, 20 workers * 512M = 10 GB of potential use.
You don’t have 10 GB.
In practice, not every worker will hit the ceiling at once. But under real load spikes, you might see:
- Aggressive swapping
- OOM killer taking out PHP or MySQL
- Random restarts that feel like “ghosts” until you look at logs
An “experienced” memory_limit setting isn’t just an arbitrary number. It’s a negotiation between:
- How heavy your requests are
- How many concurrent requests you expect
- How much RAM the server has
- How much the rest of your stack consumes (database, cache, queue workers, system)
This is why a PHP specialist who understands operations is so valuable.
They don’t just say “set it to 1G.” They ask, “how many workers do you have, and what does the rest of your stack look like?”
Debugging a real memory_limit failure
Let’s say you are staring at this:
PHP Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 20480 bytes)
Translated:
- 268,435,456 bytes = 256M
- Your script tried to allocate 20 KB more, and PHP said “nope”.
How do you debug this without pure guessing?
Step 1: get visibility
If you can, use tools or techniques like:
memory_get_usage(true)at various pointsmemory_get_peak_usage(true)at the end- Profilers like Xdebug, Tideways, Blackfire on non-production environments
Example:
logMemory('start');
// some heavy block
logMemory('after processing');
function logMemory(string $label): void
{
error_log(sprintf(
'[%s] Memory usage: %s MB (peak: %s MB)',
$label,
round(memory_get_usage(true) / 1048576, 2),
round(memory_get_peak_usage(true) / 1048576, 2)
));
}
It’s crude, but it tells you where memory explodes.
Step 2: look for “keep everything” patterns
Typical offenders:
- Long-lived arrays accumulating inside loops
- ORM collections that keep growing
- Recursive structures where you expected iterative behavior
- Caching large objects or entire entities instead of small, necessary fields
Ask questions like:
- Do I really need this whole dataset at once?
- Can I process this in chunks?
- Can I offload some of this to a queue or background job?
Step 3: decide: fix behavior vs raise limit
If the memory usage is the result of genuinely heavy work (like generating a pdf from a huge report once a day):
- Move that work to a CLI command or queue worker
- Give that worker a higher
memory_limit - Keep the global web memory_limit more conservative
If it’s the result of sloppy data usage:
- Refactor before touching config
- Treat raising memory_limit as a last step, not a first instinct
That small discipline builds trust — from users, from colleagues, from whoever will maintain your code when you’ve moved on.
What this all means for teams, hiring, and careers
On paper, “PHP memory_limit” is just a config key. Something you memorize for the next certification test or Stack Overflow answer.
In reality, how a developer talks about memory_limit tells you a lot:
- Do they know it’s per script, not per server?
- Do they reach for
2Gas a reflex or as a considered choice? - Do they understand streams, chunks, and lazy processing?
- Do they think about concurrency and physical RAM?
For teams hiring through platforms like Find PHP, these are not trick questions — they’re glimpses into how someone will treat your production systems at 3 a.m. when the log fills with “Allowed memory size exhausted.”
For developers looking for work, this is quiet leverage.
You don’t need to make a big deal of it. But when you say:
“We were hitting 128M on our export endpoint. Instead of just raising it, I changed the implementation to stream results in chunks and moved the heavy formatting to a queue worker. After that, we stayed under 64M per request, even under load.”
— that lands differently.
That’s not “I know PHP.” That’s “I understand what it means to run PHP in the real world.”
Ending in the quiet part
Somewhere, right now, a junior developer is staring at a fatal “Allowed memory size” error with the same mix of dread and curiosity you once had.
They’ll google. They’ll paste a line into php.ini. It might work. Or it might not. Maybe they’ll get stuck, maybe someone senior will drop into their PR with a better pattern, maybe they’ll learn chunking tonight and never forget it.
We’ve all been that person, watching the logs, waiting for the next fatal error, hoping we’ve actually fixed it this time.
Understanding PHP memory_limit is one of those small, unglamorous skills that quietly changes how you write code, design systems, and think about constraints.
And the next time that error appears on your screen, you’ll pause, maybe take a sip of something warm, and instead of rushing to double the limit, you’ll ask the deeper question:
What is this code really trying to hold on to — and what can it finally let go of?