[{"data":1,"prerenderedAt":602},["ShallowReactive",2],{"navigation":3,"/blog/patching-php2shell-docker-playbook":154,"surround-patching-php2shell-docker-playbook":591},[4],{"title":5,"path":6,"stem":7,"children":8,"page":153},"Blog","/blog","blog",[9,13,17,21,25,29,33,37,41,45,49,53,57,61,65,69,73,77,81,85,89,93,97,101,105,109,113,117,121,125,129,133,137,141,145,149],{"title":10,"path":11,"stem":12},"How to Increase Free Tier Memory on AWS EC2","/blog/aws-ec2-ram","blog/aws-ec2-ram",{"title":14,"path":15,"stem":16},"Blooper: The Disappearing Cloudflare Variables","/blog/blooper-cloudflare-env-vars","blog/blooper-cloudflare-env-vars",{"title":18,"path":19,"stem":20},"Blooper: Hardcoded Content Links cause 404s","/blog/blooper-hardcoded-nuxt-content-links","blog/blooper-hardcoded-nuxt-content-links",{"title":22,"path":23,"stem":24},"Blooper: The Invisible SEO","/blog/blooper-nuxt-seo-env","blog/blooper-nuxt-seo-env",{"title":26,"path":27,"stem":28},"Blooper: The SSR Crash of Doom","/blog/blooper-ssr-feedback-form","blog/blooper-ssr-feedback-form",{"title":30,"path":31,"stem":32},"Blooper: The YouTube Crawl Budget Killer","/blog/blooper-video-crawl-budget","blog/blooper-video-crawl-budget",{"title":34,"path":35,"stem":36},"Blooper: PWA Redirecting SPA Routes","/blog/blooper-vite-pwa-ssr-redirects","blog/blooper-vite-pwa-ssr-redirects",{"title":38,"path":39,"stem":40},"Git Branching in 2026: Code State, Environment Isolation, and Modern CD","/blog/branch-theater-staging-branches","blog/branch-theater-staging-branches",{"title":42,"path":43,"stem":44},"The Developer's Guide to Avoiding Cloud Billing Traps in 2026","/blog/cloud-billing-traps-2026","blog/cloud-billing-traps-2026",{"title":46,"path":47,"stem":48},"Disk Management in Linux","/blog/disk-management-in-linux","blog/disk-management-in-linux",{"title":50,"path":51,"stem":52},"Docker Devtools Stage | Powerlevel, OhMyZSH, tmux, fuzzy finder, autocomplete etc","/blog/docker-devtools","blog/docker-devtools",{"title":54,"path":55,"stem":56},"Docker container | Resolving Permission Denied Woes: A Decade-Long Struggle Unraveled","/blog/docker-non-root-user-perms","blog/docker-non-root-user-perms",{"title":58,"path":59,"stem":60},"The Ultimate Guide to Free Tier Hosting Platforms (Serverless & Containers)","/blog/free-hosting-platforms-guide","blog/free-hosting-platforms-guide",{"title":62,"path":63,"stem":64},"Git commands | underrated","/blog/git-commands-underrated","blog/git-commands-underrated",{"title":66,"path":67,"stem":68},"Journey: Taming Edge Caching and KV Storage","/blog/journey-github-streak-caching","blog/journey-github-streak-caching",{"title":70,"path":71,"stem":72},"Kubernetes Cluster Setup Guide","/blog/k8s-setup","blog/k8s-setup",{"title":74,"path":75,"stem":76},"Linux WiFi Drivers","/blog/linux-wifi-drivers","blog/linux-wifi-drivers",{"title":78,"path":79,"stem":80},"4 Common Routing Traps When Migrating to Nuxt i18n","/blog/nuxt-i18n-routing","blog/nuxt-i18n-routing",{"title":82,"path":83,"stem":84},"Patch the CVE, Don't Trigger an Accidental Refactor: Surviving PHP2Shell on Docker","/blog/patching-php2shell-docker-playbook","blog/patching-php2shell-docker-playbook",{"title":86,"path":87,"stem":88},"pfSense basic firewall setup","/blog/pfsense-basic-firewall-setup","blog/pfsense-basic-firewall-setup",{"title":90,"path":91,"stem":92},"pfSense Quickstart","/blog/pfsense-quickstart","blog/pfsense-quickstart",{"title":94,"path":95,"stem":96},"Postmortem: Cloudflare Worker Missing Dependency on Deploy","/blog/postmortem-cloudflare-worker-dependency-deploy","blog/postmortem-cloudflare-worker-dependency-deploy",{"title":98,"path":99,"stem":100},"Postmortem: Serverless HTTP Driver Transaction Failures","/blog/postmortem-neon-http-transactions","blog/postmortem-neon-http-transactions",{"title":102,"path":103,"stem":104},"Postmortem: The Ghost in the Chrome Cache (Nuxt & Cloudflare Pages)","/blog/postmortem-nuxt-cloudflare-chunk-error","blog/postmortem-nuxt-cloudflare-chunk-error",{"title":106,"path":107,"stem":108},"Postmortem: Nuxt Content 404s on Staging Deployments","/blog/postmortem-nuxt-content-404-staging","blog/postmortem-nuxt-content-404-staging",{"title":110,"path":111,"stem":112},"Postmortem: 500 Internal Server Error on ISR Routes","/blog/postmortem-nuxt-content-payload-extraction","blog/postmortem-nuxt-content-payload-extraction",{"title":114,"path":115,"stem":116},"Post-Mortem: Intrusive Database Driver Refactoring","/blog/postmortem-proxy-re-architecting","blog/postmortem-proxy-re-architecting",{"title":118,"path":119,"stem":120},"Post-Mortem: Cloudflare Worker Connection Hang & Resilient DB Fallbacks","/blog/postmortem-worker-db-connection-hang","blog/postmortem-worker-db-connection-hang",{"title":122,"path":123,"stem":124},"Pro Tip: Sandboxing Cloudflare Workers for AI Agents","/blog/pro-tip-wrangler-local-dev-trick","blog/pro-tip-wrangler-local-dev-trick",{"title":126,"path":127,"stem":128},"Proxmox DNS Management","/blog/proxmox-dns","blog/proxmox-dns",{"title":130,"path":131,"stem":132},"Proxmox Recovery","/blog/proxmox-recovery","blog/proxmox-recovery",{"title":134,"path":135,"stem":136},"Proxmox WiFi Connection Story","/blog/proxmox-wifi","blog/proxmox-wifi",{"title":138,"path":139,"stem":140},"roadauth-rails api jwt cors","/blog/rodauth-jwt","blog/rodauth-jwt",{"title":142,"path":143,"stem":144},"SOCKS5 Proxy Quickstart","/blog/sock5-proxy","blog/sock5-proxy",{"title":146,"path":147,"stem":148},"tmux status bar toggle","/blog/tmux-status","blog/tmux-status",{"title":150,"path":151,"stem":152},"UFW - Cheatsheet","/blog/ufw-cheatsheet","blog/ufw-cheatsheet",false,{"id":155,"title":82,"authors":156,"badge":162,"body":164,"date":580,"description":581,"extension":582,"image":583,"imageComponent":584,"meta":585,"navigation":586,"path":83,"pinned":153,"seo":587,"seoImage":588,"stem":84,"__hash__":590},"posts/blog/patching-php2shell-docker-playbook.md",[157],{"name":158,"to":159,"avatar":160},"Rahul Dhole","/",{"src":161},"/profile.png",{"label":163},"Security",{"type":165,"value":166,"toc":567},"minimark",[167,171,174,183,186,189,192,195,200,203,242,247,254,256,260,263,279,282,315,321,324,340,343,345,349,356,359,374,377,379,383,386,391,402,425,429,432,453,457,460,482,485,505,507,511,514,546,548,552,555,558,563],[168,169,170],"p",{},"When national cyber agencies like the UK NCSC issue emergency threat alerts for a critical CVSS 9.2 unauthenticated Remote Code Execution in PHP core (PHP2Shell / CVE-2026-109618 & CVE-2026-109619), production Slack channels lose their minds.",[168,172,173],{},"The immediate knee-jerk reaction from most developers and ops teams is always identical:",[175,176,177],"blockquote",{},[168,178,179],{},[180,181,182],"em",{},"\"Drop everything. Upgrade all containers to PHP 8.5 right now.\"",[168,184,185],{},"Don't do that.",[168,187,188],{},"Rushing a major runtime upgrade during an active vulnerability drill is how you cause the exact production downtime the attacker was hoping to achieve. You take down custom WordPress themes, break WooCommerce extensions, trigger fatal deprecation errors across payment gateways, and suddenly spend your night debugging runtime incompatibilities instead of patching a security hole.",[168,190,191],{},"Here is the SRE incident playbook on how point patches work, why your Docker layer caching is probably lying to you, and the deterministic way to patch containerized PHP stacks without breaking production.",[193,194],"hr",{},[196,197,199],"h2",{"id":198},"_1-upstream-did-the-heavy-lifting-for-you","1. Upstream Did the Heavy Lifting for You",[168,201,202],{},"Security releases do not force you across major version boundaries. The PHP maintainers backported fixes for both the out-of-memory memory corruption and the worker cross-request use-after-free bugs directly into active branches:",[204,205,206,218,226,234],"ul",{},[207,208,209,213,214],"li",{},[210,211,212],"strong",{},"PHP 8.2"," → ",[215,216,217],"code",{},"8.2.35",[207,219,220,213,223],{},[210,221,222],{},"PHP 8.3",[215,224,225],{},"8.3.36",[207,227,228,213,231],{},[210,229,230],{},"PHP 8.4",[215,232,233],{},"8.4.27",[207,235,236,213,239],{},[210,237,238],{},"PHP 8.5",[215,240,241],{},"8.5.12",[168,243,244],{},[180,245,246],{},"(Note: If you are running PHP 8.1 or older, you are already past End-of-Life and on your own. You have no upstream patches and need extended vendor security repos or a scheduled upgrade window.)",[168,248,249,250,253],{},"If your app is running on PHP 8.2 or 8.3, you ",[210,251,252],{},"do not"," need to move to 8.4 or 8.5 today. You only need to step to the patched point release on your existing branch. Zero syntax breaking changes, zero extension incompatibilities.",[193,255],{},[196,257,259],{"id":258},"_2-the-docker-tag-trap-why-rebuilding-didnt-fix-it","2. The Docker Tag Trap: Why \"Rebuilding\" Didn't Fix It",[168,261,262],{},"Most engineers maintain Dockerfiles that look something like this:",[264,265,270],"pre",{"className":266,"code":267,"language":268,"meta":269,"style":269},"language-dockerfile shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","FROM php:8.3-fpm-alpine\n","dockerfile","",[215,271,272],{"__ignoreMap":269},[273,274,277],"span",{"class":275,"line":276},"line",1,[273,278,267],{},[168,280,281],{},"When news of the patch breaks, they run:",[264,283,287],{"className":284,"code":285,"language":286,"meta":269,"style":269},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","docker compose build\ndocker compose up -d\n","bash",[215,288,289,302],{"__ignoreMap":269},[273,290,291,295,299],{"class":275,"line":276},[273,292,294],{"class":293},"sBMFI","docker",[273,296,298],{"class":297},"sfazB"," compose",[273,300,301],{"class":297}," build\n",[273,303,305,307,309,312],{"class":275,"line":304},2,[273,306,294],{"class":293},[273,308,298],{"class":297},[273,310,311],{"class":297}," up",[273,313,314],{"class":297}," -d\n",[168,316,317,318],{},"They think they are safe. ",[210,319,320],{},"They are not.",[168,322,323],{},"Docker tags are mutable aliases, but the Docker daemon’s caching engine is aggressively lazy:",[325,326,327,330,337],"ol",{},[207,328,329],{},"Docker checks your local image cache.",[207,331,332,333,336],{},"It sees an image already tagged ",[215,334,335],{},"php:8.3-fpm-alpine"," from three weeks ago.",[207,338,339],{},"It assumes nothing has changed and builds your application container using the old, vulnerable base layer.",[168,341,342],{},"Unless you explicitly tell Docker to check the registry for a new manifest digest, it will happily serve cached, vulnerable binaries forever.",[193,344],{},[196,346,348],{"id":347},"_3-the-immutable-alternative-pin-exact-subversions","3. The Immutable Alternative: Pin Exact Subversions",[168,350,351,352,355],{},"Floating tags (",[215,353,354],{},"php:8.3-fpm",") are convenient for local sandbox playgrounds, but they hide state in production CI/CD pipelines.",[168,357,358],{},"If you pin your Dockerfile to the exact patched point release, you introduce deterministic traceability into Git version control:",[264,360,362],{"className":266,"code":361,"language":268,"meta":269,"style":269},"# Lock to the patched upstream release\nFROM php:8.3.36-fpm-alpine\n",[215,363,364,369],{"__ignoreMap":269},[273,365,366],{"class":275,"line":276},[273,367,368],{},"# Lock to the patched upstream release\n",[273,370,371],{"class":275,"line":304},[273,372,373],{},"FROM php:8.3.36-fpm-alpine\n",[168,375,376],{},"Now the Git diff explicitly documents that the security vulnerability was evaluated, addressed, and tracked.",[193,378],{},[196,380,382],{"id":381},"_4-the-3-step-clean-rebuild-playbook","4. The 3-Step Clean Rebuild Playbook",[168,384,385],{},"Whether you pin exact subversions or use floating branch tags, you must force Docker to pull upstream manifests and ignore local cached base layers.",[387,388,390],"h3",{"id":389},"step-1-force-upstream-pull-and-bypass-cache","Step 1: Force Upstream Pull and Bypass Cache",[168,392,393,394,397,398,401],{},"Run your build with ",[215,395,396],{},"--pull"," (forces registry check for new tag digests) and ",[215,399,400],{},"--no-cache",":",[264,403,405],{"className":284,"code":404,"language":286,"meta":269,"style":269},"docker compose build --pull --no-cache php\n",[215,406,407],{"__ignoreMap":269},[273,408,409,411,413,416,419,422],{"class":275,"line":276},[273,410,294],{"class":293},[273,412,298],{"class":297},[273,414,415],{"class":297}," build",[273,417,418],{"class":297}," --pull",[273,420,421],{"class":297}," --no-cache",[273,423,424],{"class":297}," php\n",[387,426,428],{"id":427},"step-2-force-recreate-the-service-container","Step 2: Force Recreate the Service Container",[168,430,431],{},"Prevent Docker from reusing an existing container instance:",[264,433,435],{"className":284,"code":434,"language":286,"meta":269,"style":269},"docker compose up -d --force-recreate php\n",[215,436,437],{"__ignoreMap":269},[273,438,439,441,443,445,448,451],{"class":275,"line":276},[273,440,294],{"class":293},[273,442,298],{"class":297},[273,444,311],{"class":297},[273,446,447],{"class":297}," -d",[273,449,450],{"class":297}," --force-recreate",[273,452,424],{"class":297},[387,454,456],{"id":455},"step-3-verify-the-running-runtime","Step 3: Verify the Running Runtime",[168,458,459],{},"Never trust command output—trust the running process memory. Execute directly inside the active container:",[264,461,463],{"className":284,"code":462,"language":286,"meta":269,"style":269},"docker compose exec php php -v\n",[215,464,465],{"__ignoreMap":269},[273,466,467,469,471,474,477,479],{"class":275,"line":276},[273,468,294],{"class":293},[273,470,298],{"class":297},[273,472,473],{"class":297}," exec",[273,475,476],{"class":297}," php",[273,478,476],{"class":297},[273,480,481],{"class":297}," -v\n",[168,483,484],{},"Look at the output:",[204,486,487,495],{},[207,488,489,213,492],{},[215,490,491],{},"PHP 8.3.36 (cli) ...",[210,493,494],{},"Patched.",[207,496,497,500,501,504],{},[215,498,499],{},"PHP 8.3.35"," or lower → ",[210,502,503],{},"Still vulnerable."," Investigate your registry pulls immediately.",[193,506],{},[196,508,510],{"id":509},"_5-stopgap-protections-while-you-roll-out","5. Stopgap Protections While You Roll Out",[168,512,513],{},"If you operate multiple environments or orchestrate fleets that take time to redeploy, lock down the vector at your ingress layer:",[325,515,516,522,536],{},[207,517,518,521],{},[210,519,520],{},"WAF Body Inspection:"," Ensure your edge proxy (Cloudflare, AWS WAF) is active. Cloudflare has rolled out managed rule mitigations across all tiers targeting multipart/OOM fuzzing payloads.",[207,523,524,527,528,531,532,535],{},[210,525,526],{},"Cycle PHP-FPM Workers Aggressively:"," In your pool configuration (",[215,529,530],{},"www.conf","), clamp ",[215,533,534],{},"pm.max_requests = 100",". This destroys and respawns workers frequently, sharply limiting stale memory persistence across requests.",[207,537,538,541,542,545],{},[210,539,540],{},"Limit POST Body Sizes at the Reverse Proxy:"," Clamp ",[215,543,544],{},"client_max_body_size"," in Nginx/Traefik down to what your app actually requires so large payload floods never reach PHP workers in the first place.",[193,547],{},[196,549,551],{"id":550},"key-takeaway","Key Takeaway",[168,553,554],{},"A zero-day or critical CVE drill is not the time to modernize your runtime architecture.",[168,556,557],{},"Stay on your active minor branch, pull the exact upstream point release, force Docker to bust its layer cache, and verify the running process.",[168,559,560],{},[210,561,562],{},"Patch the vulnerability. Don't trigger an accidental refactor.",[564,565,566],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}",{"title":269,"searchDepth":304,"depth":304,"links":568},[569,570,571,572,578,579],{"id":198,"depth":304,"text":199},{"id":258,"depth":304,"text":259},{"id":347,"depth":304,"text":348},{"id":381,"depth":304,"text":382,"children":573},[574,576,577],{"id":389,"depth":575,"text":390},3,{"id":427,"depth":575,"text":428},{"id":455,"depth":575,"text":456},{"id":509,"depth":304,"text":510},{"id":550,"depth":304,"text":551},"2026-10-23","An SRE incident playbook for PHP2Shell (CVE-2026-109618 & CVE-2026-109619) following national cyber alerts. How to patch containerized PHP stacks without breaking production.","md",null,{"type":163},{},true,{"title":82,"description":581},{"src":589},"https://placehold.co/800x400/0f172a/3b82f6?text=Patching+PHP2Shell+on+Docker","A7ubmrJGaTaZ38EAFikLix8abBY79uGxBOngkFONxfY",[592,596],{"title":78,"path":79,"stem":80,"description":593,"imageComponent":583,"seoImage":594,"children":-1},"How to fix broken navigation, missing widgets, and active links when migrating a NuxtJS app to @nuxtjs/i18n.",{"src":595},"https://placehold.co/800x400/0f172a/3b82f6?text=Nuxt+i18n+Routing",{"title":86,"path":87,"stem":88,"description":597,"imageComponent":598,"seoImage":600,"children":-1},"pfSense basic firewall setup - Dive into the details of pfsense basic firewall setup with this quick guide.",{"type":599},"Networking",{"src":601},"https://placehold.co/800x400/0f172a/3b82f6?text=pfSense+basic+firewall+setup",1792752997593]