Published 28 October 2026 · 5 min read
Git deploys into release folders: how automatic rollback works
This is the first part of a series on the features of Indepanel, the hosting panel we built for the group's own sites. We start with the one it was built for: deploys that cannot break a running site.
- In short
- A new version is fetched and built into its own folder beside the running site. Switching over is a symlink swap, a fraction of a second, not a gradual file copy.
- A health check has to call an endpoint that really touches the database and the configuration. A check that returns 200 from cache checks nothing.
- One-click rollback is not rollback. At the moment you need it, you are usually not at your computer.
What breaks when you copy over a running site
A classic FTP or rsync deploy uploads files one by one into the folder the site is currently served from. For several seconds or minutes the site therefore has some files new and some old. Anyone opening a page during that window gets a combination that was never tested.
On a static site this shows up as broken styling. On an application with templates, dependencies and database migrations it shows up as a 500, or as something worse: the page renders but writes data in the old shape.
And crucially: by the time you find out the new version is broken, the old one is no longer on disk. Going back means uploading the previous state again, the same slow operation under more pressure.
A folder per version
The principle, used by everything from Capistrano to modern platforms, is simple. Each deploy gets its own folder named after the time or the commit. The code is cloned into it, dependencies are installed and the application is built. The running site knows nothing about any of it.
Next to the release folders sit two things that do not belong to a version:
- Shared storage for everything created by running the site: uploads, generated images, caches. It is linked into the release folder.
- Per-site environment variables: database credentials, API keys, settings. They do not belong in the repository and are only read at deploy time.
Only once the new folder is complete and has passed its check does the symlink the web server uses as its document root point at it. That is the only operation a visitor could notice, and it takes a fraction of a second.
You can tell a good deploy because nobody notices it. Including you.
A health check that checks something
This is where the most common mistake happens, and we made it too. Our post-deploy check originally called the home page and waited for a 200. The trouble is that a home page is often served from cache and answers even when the application underneath is broken.
A useful check has to touch what the deploy could have broken. Ours calls an endpoint that:
- opens a database connection and runs one real query,
- reads at least one environment variable the application genuinely needs,
- returns the version, meaning the hash of the commit just deployed.
That last point matters more than it looks. Without it you cannot tell the difference between "the new version answers" and "the old version still answers, because the switch never happened".
Why rollback has to be automatic
The argument against automation sounds reasonable: I do not want a system changing what runs on the site by itself. In practice it is the other way round. You need a rollback precisely when you are not there, and the longer it takes, the more people have seen a broken page in the meantime.
Our order is: deploy, check, and if the check fails, do not switch. The running site stays on the previous version and the new folder is marked as failed. If a problem shows up after the switch, the panel can point the link back at the previous version, which is still on disk and does not need rebuilding.
The human part comes afterwards: somebody looks calmly at the failed deploy log and works out what happened. The difference is that they are looking at a working site.
What to get right even without your own panel
Most teams do not need their own hosting panel, but these are worth setting up with off-the-shelf tools too:
- How many old versions you keep. Three to five is usually enough. Zero means rollback does not exist.
- What the health check actually tests. If it answers with the database switched off, it tests nothing.
- Where user data ends up. If it lives in the release folder, every deploy gets a say over it.
- How you find out what is running. A commit hash visible from the application saves a lot of later guessing.
- What happens to database migrations on rollback. Automation does not solve this, and it is the most common hole in the whole scheme.
That last point deserves saying out loud: rolling back code does not roll back the database schema. If a migration dropped a column, the previous version of the code will not find it after a rollback. The fix is not technical but in the design: write migrations so the previous version of the code survives them, meaning add first, stop using second, and drop only a deploy later.
Where this is not enough
For completeness: this scheme does not cover deploying to several servers at once, it shifts no traffic gradually, and it cannot undo anything that left the server, such as sent e-mails or writes into third-party systems. At larger scale those are handled differently and with more expensive tools. For running dozens of sites on one server, though, it is the difference between deploying on a Friday and deploying on a Monday morning.
The next part looks at running four PHP versions on one server and knowing, per site, whether known vulnerabilities exist for the one it uses. How we build that kind of operation is described under hosting and operations.
Contact
Have a project? Write to us.
A few sentences about what you are dealing with is enough. We reply within one working day with questions and a proposed approach.