Docker does not free memory after creating and deleting files with PHP
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
When a PHP process in Docker writes and deletes a lot of files, memory usage often appears to stay high. In most cases Docker is not leaking memory at all. The numbers are usually explained by Linux page cache, allocator reuse inside the PHP process, or files that were deleted while still open.
Deleting a File Does Not Mean Memory Drops Immediately
A deleted file and freed memory are not the same event. On Linux, file data may remain in the page cache after the directory entry is removed because the kernel keeps recently used pages around until something else needs that RAM.
Docker containers use the host kernel, so the same behavior shows up in container memory metrics. That means a workload can create files, delete them, and still appear to “hold” memory even though the memory is reclaimable.
A small PHP loop can trigger this easily:
That script allocates PHP memory and also drives file I/O that can populate the kernel page cache.
Distinguish PHP Heap from Kernel Cache
Two different memory stories can exist at the same time:
- PHP heap used for strings, arrays, buffers, and runtime allocations
- kernel-managed page cache caused by reading and writing files
memory_get_usage() only tells you about PHP allocations. docker stats shows container-level accounting, which may include cache. If those two views diverge, the difference is a clue rather than a contradiction.
A useful debugging sequence is:
The exact cgroup files differ between v1 and v2, but the goal is the same: separate process memory from cache-heavy container accounting.
Deleted Files Stay Alive If a Process Still Has Them Open
One case is not just cache: if PHP or another process unlinks a file but still keeps the file descriptor open, the storage and related kernel resources remain in use until the descriptor closes.
This happens with streaming code, temporary files, or libraries that keep handles open longer than expected.
Inside the container, check for deleted-but-open files:
If you see a deleted file still referenced by a process, memory and disk pressure may persist until that process closes the handle or exits.
Allocators Often Reuse Memory Instead of Returning It Immediately
Even if PHP no longer needs an allocation, the underlying memory allocator may keep arenas around for reuse instead of returning them to the operating system right away. That can make the resident set size look sticky under bursty workloads.
This is not unique to Docker and not unique to PHP. It is common behavior in long-running processes that repeatedly allocate and free large buffers.
The operational question is not “did the number instantly drop,” but “does memory continue to grow without bound under stable load.” If the number stabilizes and the kernel can reclaim memory under pressure, you are looking at reuse and caching, not a true leak.
Reduce File Churn If It Hurts Throughput or Headroom
If the workload is operationally expensive, change the pattern rather than fighting the kernel.
Practical fixes include:
- stream data instead of writing many temporary files
- reuse a smaller set of working files
- process data in chunks instead of building large intermediate blobs
- set realistic container memory limits and monitor reclaim activity
- profile libraries that may hold file handles longer than expected
For example, using a PHP stream can cut both heap pressure and temporary-file churn:
That will not fit every use case, but it illustrates the general direction: less disk churn usually means less confusing cache behavior.
Common Pitfalls
A common mistake is treating docker stats as proof of a memory leak without checking PHP's own heap usage. Container metrics alone do not tell you whether the “extra” memory is cache, open-file state, or allocator reuse.
Another mistake is assuming unlink() guarantees instant release of every related resource. It does not if the file is still open.
Teams also try to “fix” the symptom by manually dropping caches. That is occasionally useful in debugging, but it is not an application design strategy and it is usually the wrong production response.
Summary
- Docker memory that stays high after PHP deletes files is often page cache, not a leak.
- Compare PHP heap metrics with container-level memory metrics before drawing conclusions.
- Deleted files can still consume resources if a process keeps them open.
- Memory allocators may retain freed memory for reuse instead of returning it immediately.
- The clean fix is usually reducing file churn or changing buffering behavior, not forcing cache drops.

