No SSH, No Pipeline: The Self-Deleting PHP Pattern That Keeps My 1,000-Page Site Alive

●1 ●1 ●6
calendar_today ago • schedule1 min read
— Originally published at bryanhamilton.info

Originally published on bryanhamilton.info - part of an engineering series from production.

I run The DJ Calendar - about 1,000 pages of PHP/MySQL on shared cPanel hosting. No shell. No SSH. No deploy pipeline. LiteSpeed caches HTML for 5 minutes, and opcache serves stale output for a minute after every write.

Pasting a code change into a production file through a browser file manager is how sites die. So over months of live deploys I built a worker pattern that makes that kind of deploy survivable - and I open-sourced it: guarded-deploy-workers.

What a "worker" actually is
A worker is a single PHP file I upload to the web root and hit once with a secret token. The skeleton is ~100 lines:

<?php
if (!isset($_GET['t']) || $_GET['t'] !== TOKEN) {
    http_response_code(404);
    include '404.php'; exit;          // real 404 - never leak the worker
}
$src = file_get_contents($file);
if (strpos($src, 'PATCH-MARKER') !== false) exit;   // idempotency
foreach ($needles as $n)                            // counted anchors
    if (substr_count($src, $n) !== 1) exit;
copy($file, $file . '.bak');
$src = str_replace($needles, $replacements, $src);
file_put_contents($file, $src);
exec('php -l ' . escapeshellarg($file), $o, $rc);
if ($rc !== 0) { copy($file . '.bak', $file); exit; } // auto-restore
unlink($file . '.bak');
unlink(__FILE__);                                   // self-delete

Every rule in that skeleton is there because a deploy taught me why.

The bugs that taught me the most
A verifier that checked for an unchanged file length when the patch legitimately removed 2 bytes. It restored a correct patch. My own safety check fired on a good deploy.

A needle string that appeared twice in the file. The patch landed in both places. Now every needle is counted, not assumed.

opcache serving the old file long after the disk was correct - the default explanation for "the patch did not work" on shared hosting, and the reason live verification always cache-busts.

Uppercase characters in a preg lowercase filter - the string shredder that ate a slug. Always lowercase before the character-class filter.

A sitemap whose disk bytes did not match the served bytes - byte-exact needles false-failed until the insert walked back to the nearest tag instead of trusting whitespace.

The full list is an 18-rule pre-flight checklist in the repo.

When NOT to use this
If you have a real deployment pipeline, use it. This pattern is for the world a lot of us actually live in: a cPanel box, FTP credentials, a production site that pays the bills, and no infrastructure team coming to save you.

Repo is MIT licensed: github.com/bryanhamiltondev/guarded-deploy-workers

Question for the comments: what's your survivability trick when you have zero shell access and a production fire? Genuinely collecting these for a follow-up piece.

🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

Sovereign Intelligence: The Complete 25,000 Word Blueprint (Download)

Pocket Portfolio - Apr 1

AI Email Classifier Cost per 1,000 Messages: Real Math

floxolab - Oct 8

How I Built a React Portfolio in 7 Days That Landed ₹1.2L in Freelance Work

Dharanidharan - Feb 9

TypeScript Complexity Has Finally Reached the Point of Total Absurdity

Karol Modelski - Apr 23

My 30-day log cleanup kept a file that was 30 and a half days old

BashSnippets - Oct 8
chevron_left
177 Points • 8 Badges
Rochelle Park, New Jersey, US • bryanhamilton.info
3Posts
1Comments
1Connections
Full-stack web engineer · PHP & vanilla JS · 25+ years · Founder of The DJ Calendar · Open to work

Related Jobs

View all jobs →

Commenters (This Week)

1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!