On this page
A rollback is more than “revert Git”
Time matters during a production incident. If rollback still requires finding a commit, reinstalling dependencies, and guessing whether the database is compatible, it is not a workable plan. Before release, keep the previous code or image, configuration, data protection point, and traffic-switching procedure ready.
One server cannot eliminate all downtime, but it can avoid unnecessary interruption. Start a candidate version on another local port, verify its health and essential routes, and only then point Caddy at it.
Define success and stop conditions before release
Write down the change, affected routes, database changes, expected window, and owner. Success is not an active service status; it is working health, homepage, essential API, and login or form behavior. Set concrete stop conditions such as repeated health failures, a sustained rise in 5xx responses, or a migration exceeding its planned window.
Freeze unrelated changes during release. Record the current version, configuration hash, and database backup. Do not combine an operating-system upgrade, database major upgrade, and application dependency update in one deployment; if it fails, you will not know which part caused it.
Start the candidate on a different port
The current version might listen on 127.0.0.1:3000 while the candidate uses 127.0.0.1:3002. Keep their release directories separate and read-only. Before both touch shared data, confirm that the old and new versions are compatible with the same database structure.
curl -fsS http://127.0.0.1:3002/health
curl -fsS -H 'Host: www.example.com' http://127.0.0.1:3002/The health endpoint should show that the process is ready for traffic; an essential-route check tests the page and its dependencies. A health endpoint that always returns 200 regardless of state is not useful. Conversely, making it depend on expensive outside calls can turn a brief wobble into a false failure.
Validate the proxy change and observe immediately afterward
Change the Caddy upstream from 3000 to 3002, run caddy validate, and reload only if validation passes. Preserve the previous configuration. After switching, inspect external HTTPS, Caddy logs, new-version logs, and remaining connections to the old process. Keep the old version available during a short observation window instead of deleting it immediately.
If the app supports multiple upstreams, you may briefly include a candidate and manage traffic, but both versions must be compatible with the shared database. Single-server blue-green deployment is a fast switching method, not independent infrastructure.
Database migrations set the limits of rollback
Dropping a column, renaming a field, or performing an irreversible transformation can prevent the previous version from running. A safer pattern is expand, migrate, contract: add backward-compatible fields, deploy code that tolerates both structures, move data and observe, then remove old fields in a later release.
Rehearse every data-writing migration on a recent backup copy and record its runtime and locking effects. Do not hide a long migration in the app's start command; a systemd restart or container replacement might run it again.
Follow the written rollback sequence
When a stop condition is met, switch Caddy back to the old upstream and reload. Confirm user traffic has recovered before stopping the candidate. If the database change was backward-compatible, do not restore the whole database automatically. If incorrect writes occurred, use a targeted recovery plan; overwriting production to repair one table may discard valid new data.
Keep candidate logs and the incident timeline. Restore service first, then investigate the cause—but do not erase the evidence of the failure.
Keep a short deployment record
Record the version, operator, backup location, start time, switch time, health results, problems, rollback decision, and finish time. After several releases, these records will show the real time and riskiest steps, which you can then automate.
You are ready when a candidate can be tested independently, switching does not restart the whole proxy, the old version can return within minutes, and the migration compatibility plan is clear. Next, set up monitoring and alerts to notice regressions promptly.
Sources
- Dockerfile HEALTHCHECKDocker Docs
- Caddy reverse_proxy directiveCaddy Documentation
Next steps
Continue with the next useful task; you do not need to read everything at once.