Deploying without downtime
Most outages are deployments. The good news is that this is the failure you have the most control over.
Atomic releases
Never edit files in place in a live directory. Build the new version beside the old one and switch with a symlink:
/srv/app/releases/2026-10-14-1530/
/srv/app/current -> releases/2026-10-14-1530
ln -sfn onto a temporary name and mv -T it into place, so
the switch is a single atomic operation. No request ever sees a half-copied
tree.
Keep the last five releases. Rollback then becomes moving the symlink back and reloading — seconds, not a rebuild.
Reload, do not restart
gunicorn reloads on HUP, PHP-FPM on USR2, nginx on reload. Old workers finish what
they are doing and new ones pick up the new code. Your own application should handle
SIGTERM the same way — see
running your application as a service.
If you set opcache.validate_timestamps=0 as
PHP-FPM tuning recommends, the reload is not optional: your
new code is not used until it happens.
Migrations are the dangerous part
A rollback cannot undo a dropped column. Make migrations additive and split them across deploys:
- Deploy 1 — add the new column, write to both, read from the old
- Deploy 2 — read from the new
- Deploy 3, days later — stop writing the old, then drop it
Tedious, and it means any single deploy is reversible. Watch for migrations that lock a large table: adding an index on a busy table can stall writes for minutes. Use the concurrent or online variants, and rehearse against a copy of production data.
Run migrations as their own step
Not at application start-up, or every worker races through them at once. One command, before the symlink switch, with its own exit code that aborts the deploy if it fails.
A pipeline worth having
- Tests run on every push
- The artefact is built once and the same one goes to staging and production
- Deploy to staging automatically
- Production needs a human to approve
- Health check afterwards, automatically, with an automatic rollback if it fails
The deploy user on the server gets exactly the sudo rights it needs and no more — one line in sudoers for the reload, as users and sudo describes.
Two rules
Do not deploy on Friday afternoon. Not superstition: if something surfaces two hours later, you want people awake and available.
Practise the rollback. A rollback that has never been executed is a theory, in exactly the way an untested restore is. Do it once on purpose, on staging, and time it.
Still stuck? Mail support@novogara.com — an engineer answers, at any hour. Back to the knowledge base