En bref : pour la mesure transitoire PHP2Shell memory_limit=-1, PHP-FPM a besoin d'une limite mémoire en dehors de PHP. Sous systemd, vous la définissez avec MemoryHigh= comme frein et MemoryMax= comme plafond strict dans un fichier complémentaire pour le service FPM. ulimit, LimitAS= ou LimitDATA= sont, selon php.net, inadaptés pour cela. Vérifiez en outre OOMPolicy=, afin qu'un seul worker ne termine pas tout le service.
Une recommandation qui surprend d'abord
Parmi les mesures transitoires que php.net a publiées le 21 octobre 2026 pour PHP2Shell, il en est une qui a fait hésiter de nombreux administrateurs : memory_limit=-1, donc aucune limite mémoire dans PHP lui-même. Pendant des années, le contraire passait pour une bonne pratique.
Un lien avec la description de la faille est évident. Avec CVE-2026-109618, un état de mémoire insuffisante peut interrompre PHP alors que les données de la requête ne sont qu'en partie initialisées. La mesure transitoire déplace la limite mémoire de PHP vers le système d'exploitation. php.net l'associe expressément à deux conditions : la valeur doit être verrouillée contre l'écrasement, et la limite doit venir du conteneur ou de cgroup, pas de RLIMIT_AS ni de RLIMIT_DATA.
Qui n'applique que la première moitié obtient un serveur sans aucune limite mémoire. Cet article présente la seconde moitié. Même combinée, la mesure reste transitoire : la faille n'est corrigée qu'avec PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 ou un backport confirmé.
Les cgroups en deux phrases
Les Control Groups, cgroups en abrégé, sont une fonction du noyau Linux qui regroupe des processus et limite leurs ressources. systemd crée automatiquement un cgroup propre pour chaque service, et tous les processus du service, pour le service FPM donc le maître et l'ensemble des workers, en font partie.
Les options de mémoire de systemd définissent des valeurs du contrôleur de mémoire cgroup v2. Une courte commande indique si votre système utilise cgroup v2 :
# "cgroup2fs" signifie : cgroup v2 est actif
stat -fc %T /sys/fs/cgroup/
Les versions récentes des distributions courantes utilisent cgroup v2 par défaut. Si la commande indique autre chose, clarifiez cela avant le changement, car la documentation de systemd décrit les anciennes options v1 comme obsolètes.
Définir systemd MemoryMax pour PHP-FPM
La limite va dans un fichier complémentaire, appelé drop-in. Ainsi, le fichier d'unité du paquet reste inchangé, et une mise à jour n'écrase pas votre paramètre. Les noms de service varient selon la distribution ; l'exemple utilise Debian et Ubuntu avec PHP 8.3.
# Ouvre un éditeur pour le drop-in : sudo systemctl edit php8.3-fpm
# Résultat : /etc/systemd/system/php8.3-fpm.service.d/override.conf
[Service]
MemoryAccounting=yes
MemoryHigh=1536M
MemoryMax=2G
MemorySwapMax=0
OOMPolicy=continue
Les options en un coup d'œil, d'après la documentation de systemd :
| Option | Effet | Remarque pour PHP-FPM |
|---|---|---|
MemoryHigh= |
seuil de ralentissement : les processus sont fortement freinés, de la mémoire est récupérée | la véritable grandeur de pilotage, un peu en dessous de MemoryMax |
MemoryMax= |
limite absolue : si elle ne peut être tenue, l'OOM killer devient actif dans le service | dernière ligne de défense |
MemorySwapMax= |
limite pour le swap | 0 empêche le service de se rabattre sur le swap |
OOMPolicy= |
comportement après un événement OOM : continue, stop ou kill |
continue laisse les autres workers continuer de tourner |
LimitAS=, LimitDATA= |
rlimits par processus | inadaptés pour cette mesure selon php.net |
OOMPolicy= mérite une attention particulière. Avec stop ou kill, systemd termine tout le service après un événement OOM, et tous les sites de ce service FPM tombent. La valeur qui s'applique sans indication explicite est fixée par le paramètre système par défaut DefaultOOMPolicy=. Vérifiez donc la valeur effective au lieu de la supposer.
Redémarrez ensuite le service et verrouillez memory_limit dans le pool :
sudo systemctl restart php8.3-fpm
systemctl show php8.3-fpm -p MemoryHigh -p MemoryMax -p MemorySwapMax -p OOMPolicy -p MemoryCurrent
; Fichier de pool, par ex. /etc/php/8.3/fpm/pool.d/seite1.conf
php_admin_value[memory_limit] = -1
Le fonctionnement du verrouillage est décrit dans php_admin_value : verrouiller les paramètres contre l'écrasement.
Trouver la bonne valeur
Une limite trop serrée provoque des interruptions en exploitation normale, une limite trop généreuse ne protège guère. Le point de départ est la consommation réelle.
- Observer pendant quelques jours l'utilisation mémoire actuelle de tous les processus FPM, y compris lors des pics de charge.
- Déterminer le besoin typique d'un worker et le multiplier par
pm.max_childrende tous les pools du service. - Prévoir une marge pour le processus maître, OPcache et les pics.
- Définir
MemoryHigh=juste au-dessus du pic mesuré,MemoryMax=un peu plus haut avec une certaine marge. - Réajuster les valeurs après une semaine à partir des mesures et des événements OOM.
# Mémoire de tous les processus PHP-FPM en Mo (Resident Set Size)
ps -C php-fpm8.3 -o rss= | awk '{s+=$1} END {printf "%.0f MB\n", s/1024}'
# Aperçu en continu de tous les cgroups triés par mémoire
systemd-cgtop -m
Notez la valeur de memory_limit avant le changement. Après la mise à jour, vous pouvez revenir à une limite fixe dans PHP ; la manière de la choisir judicieusement est décrite dans Bien régler memory_limit.
Surveiller : quand la limite s'applique
Une limite mémoire dont personne ne remarque le déclenchement est une source d'erreurs silencieuse. Le noyau compte les événements OOM par cgroup, et systemd ainsi que le journal du noyau les consignent.
# Compteur OOM du cgroup du service (chemin avec cgroup v2)
cat /sys/fs/cgroup/system.slice/php8.3-fpm.service/memory.events
# Messages de l'OOM killer dans le journal du noyau
journalctl -k --since "-1 day" | grep -i "out of memory"
Si le compteur oom_kill augmente, le noyau termine des workers. Cela peut indiquer une limite trop serrée, une application défectueuse ou des requêtes inhabituelles. Dans la situation actuelle, il vaut la peine de rapprocher de tels événements des journaux d'accès de la même période.
La limite mémoire est une mesure transitoire et ne remplace pas la mise à jour de PHP. Planifiez la mise à jour en parallèle et redémarrez ensuite tous les processus PHP. Toutes les mesures transitoires sont présentées dans Mesures immédiates si vous ne pouvez pas encore appliquer le correctif.
Ce qui se passe à l'intérieur de PHP en cas de manque de mémoire est expliqué dans memory_limit et Out-of-Memory. Avec Apache et mod_php, la même technique s'applique au service Apache, mais PHP partage alors la limite avec le serveur web lui-même.
Conclusion : ne pas oublier la seconde moitié
memory_limit=-1 n'est une mesure transitoire défendable qu'avec une limite externe. systemd rend cette limite possible en quelques lignes : MemoryHigh comme frein, MemoryMax comme plafond strict, MemorySwapMax contre le repli sur le swap et une OOMPolicy choisie en connaissance de cause. Mesurez avant, observez après, et gardez à l'esprit que tous les pools d'un service se partagent la limite.
Mais surtout : planifiez la fin de la mesure. Après la mise à jour vers une version corrigée de PHP, vous revenez à un memory_limit fixe. Vous pouvez conserver la limite cgroup comme protection supplémentaire contre les processus qui débordent.
Comment PHP SOS aide-t-il concrètement ?
- Mesurer : nous relevons les besoins en mémoire de vos services PHP et en déduisons des limites adaptées.
- Mettre en œuvre : nous définissons des drop-ins avec
MemoryHigh,MemoryMaxet uneOOMPolicyadaptée, verrouillonsmemory_limitdans le pool et redémarrons les services de façon contrôlée. - Observer : nous intégrons les événements OOM à la supervision et ajustons les valeurs.
- Conduire au correctif : avec le service de correctifs PHP, nous appliquons la mise à jour puis retirons les valeurs transitoires de façon ordonnée.
Questions fréquentes
Pourquoi memory_limit devrait-il valoir -1 ? N'est-ce pas dangereux ?
Seul, ce le serait. php.net recommande memory_limit=-1 comme mesure transitoire expressément en association avec une limite mémoire au niveau du conteneur ou de cgroup. La limitation passe ainsi de PHP au système d'exploitation. Sans cette limite externe, vous ne devriez pas définir la valeur.
Puis-je utiliser ulimit ou LimitAS= à la place ?
Non. php.net déconseille expressément RLIMIT_AS et RLIMIT_DATA, et ce sont précisément ces limites que fixent ulimit -v, LimitAS= et LimitDATA=. La documentation de systemd souligne en outre que LimitAS= limite l'espace d'adressage, pas la consommation de mémoire.
Que se passe-t-il lorsque PHP-FPM atteint la limite ?
Avec MemoryHigh, le noyau freine les processus et récupère de la mémoire. Avec MemoryMax, l'OOM killer devient actif au sein du service et termine un processus, le plus souvent un gros worker. Savoir si tout le service s'arrête ensuite est réglé par OOMPolicy=.
La limite s'applique-t-elle à chaque pool individuellement ?
Non. Tous les pools d'un service FPM partagent un cgroup et donc une limite commune. Des limites séparées par site nécessitent des services FPM ou des conteneurs séparés.
Comment définir la limite dans Docker ou Kubernetes ?
Via la limitation de mémoire du conteneur, par exemple l'option --memory avec docker run ou resources.limits.memory dans Kubernetes. Les deux utilisent également des cgroups. Le principe reste le même : limite en dehors de PHP, memory_limit verrouillé dans PHP.