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.
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.
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.