← Blog · ENGINEERING

Why we version SFTP uploads like Git commits

Half our customers deploy over SFTP. Treating those uploads as second-class was never an option — so we built one versioning pipeline for every way a file can change.

EO
Elena Okafor
Principal Engineer · August 4, 2026 · 7 min read

When we started Mirahost, we assumed everyone would eventually move to Git. We were wrong — and the data was unambiguous. Six months in, 52% of production changes arrived over SFTP: designers pushing theme tweaks, freelancers patching a client's plugin, agencies syncing a build folder from a CI job that predates half the tools on Hacker News.

The industry's answer to this has mostly been shame. Platforms either refuse SFTP outright or bolt it on as an untracked side door — the one place where changes happen without history, without attribution, and without a way back. That side door is where 3am incidents come from.

Every change is a commit, whether you typed git or not

Our fix was architectural: the filesystem a SFTP session writes to isn't the live site. It's a copy-on-write overlay. When the session closes — or after 90 seconds of inactivity — we diff the overlay against the current deploy, snapshot it, and build a new immutable image from the result. The upload becomes a deploy object, identical in shape to one produced by git push.

deploy history — acme-studio.com
deploy_d02f git push · main@8c1e2 · dev@kessler.co
deploy_c81a sftp · 14 files in /templates · maya@studio.co
deploy_b774 panel · php 8.3 → 8.4 · ops@kessler.co
every entry: diffable · attributable · restorable

Three doors, one history. A rollback doesn't care which door a bad change came through — it restores the whole image, files and configuration together, in about four seconds.

The hard part: sessions aren't transactions

Git gives you an atomic unit for free — the push. SFTP gives you a stream of writes with no beginning and no end. Cut a snapshot too early and you capture a half-uploaded plugin; too late and you merge two people's unrelated work into one deploy.

We settled on three boundary signals, in priority order: an explicit session close, a 90-second write gap, and a structural heuristic — when a write lands in a directory that a previous write already "completed" (think wp-content/plugins/foo/ receiving its last checksum-verified file), we treat the tree as settled. In production this cuts deploys at the right boundary 99.4% of the time; the rest are still safe, just split into two deploys instead of one.

The measure of infrastructure isn't how it treats your best-practice users. It's whether your messiest workflow still gets a way back.

What it changed for customers

The numbers after a year: support tickets containing the phrase "can you restore" dropped 71%, because customers restore themselves from the deploy list. Agencies report onboarding non-Git contractors without a workflow document. And the scariest category of incident — "someone changed something and nobody knows what" — stopped existing, because nothing can change without leaving a deploy behind.

If you're building a platform that bridges old and new workflows, our advice is simple: don't build a bridge with a toll booth on one side. Make the old path emit the same artifacts as the new one, and both kinds of users get the safety net.

Try the pipeline yourself
Every plan versions SFTP, Git, and panel changes identically.
Start free trial

More from the blog

MIGRATION
Moving 62 sites off cPanel in two weeks: a case study with Kessler Digital
July 22, 2026 · 9 min
PLATFORM
Inside the 10-second failover: how the grid reschedules a dying container
July 8, 2026 · 6 min
AGENCIES
Pricing white-label hosting: what 200 agencies told us about margins
June 30, 2026 · 8 min