It is 4:47 PM on a Friday. You merged a pull request, the CI pipeline passed, and the deployment to production completed without errors. But then the Slack messages start: "Is the app down?" "I'm getting a 500 error." "The checkout page is broken."
Your heart sinks. Something slipped through testing. Maybe a missing environment variable, a database query that works differently at scale, or a third-party API that changed its response format. The cause does not matter right now. What matters is getting your application back to a working state as fast as humanly possible.
With Deploynix, that takes about 30 seconds.
How Deploynix Manages Releases
To understand why rollback is so fast, you need to understand how Deploynix structures deployments on your server.
Deploynix does not deploy by overwriting files in place. Instead, it uses a release-based deployment strategy. Each deployment creates a new, self-contained release directory. The directory structure on your server looks something like this:
/home/deploynix/your-site/
releases/
20260318_143200/ # ← Release from March 18, 2:32 PM
20260318_160500/ # ← Release from March 18, 4:05 PM
20260318_164700/ # ← Release from March 18, 4:47 PM (current/broken)
current -> releases/20260318_164700/ # ← Symlink to active release
shared/
.env # ← Shared across all releases
storage/ # ← Shared across all releases
The current symlink is the critical piece. Nginx is configured to serve your application from the current directory. When a deployment completes, Deploynix atomically swaps this symlink from the old release to the new one. This is what makes deployments zero-downtime — the swap is instantaneous, and Nginx seamlessly starts serving the new code.
The shared directory contains your .env file and storage directory, which are symlinked into each release. Uploaded files, logs, cached data, and environment variables persist regardless of which release is active.
Previous releases are kept on the server (Deploynix retains a configurable number of recent releases). This means the code from your last working deployment is still sitting on disk, ready to be activated at any moment.
What Happens When a Deployment Fails
Deploynix monitors each step of the deployment pipeline. If any step fails — Composer install encounters an error, an npm build breaks, a migration fails — the deployment is marked as failed and the symlink is not updated. Your previous working release continues serving traffic uninterrupted.
This is an important safety net. A failed deployment never affects your live application. If composer install fails because of a dependency conflict, or if npm run build crashes due to a syntax error, your users never see it. The old code keeps running.
But what about the scenario we described at the beginning? The deployment completed successfully — all steps passed — but the application is broken in production due to an issue that was not caught in testing. This is where manual rollback comes in.
One-Click Rollback
In your Deploynix dashboard, navigate to the affected site and open the Releases section. You will see a list of recent releases with their timestamps, commit references, and status indicators.
Find the last known working deployment. Click the "Rollback" button next to it.
That is it. Deploynix swaps the current symlink to point to the selected release directory. Nginx starts serving the old code immediately. PHP-FPM workers are gracefully reloaded to pick up the change. The entire process takes seconds.
Your application is back to its previous working state. The broken deployment still exists on disk (you might want to investigate it later), but it is no longer active.
What Rollback Does and Does Not Revert
Understanding the scope of a rollback is critical for making good decisions under pressure.
What Rollback Reverts
- Application code. The PHP files, Blade templates, compiled JavaScript and CSS, and all other files that are part of your repository are reverted to the selected release.
- Vendor dependencies. The
vendordirectory in each release is self-contained. Rolling back restores the exact Composer dependencies that were installed during that deployment. - Built assets. The
public/builddirectory (or wherever Vite/Mix outputs assets) is part of the release, so rolling back restores the previous version of your CSS and JavaScript.
What Rollback Does NOT Revert
- Database changes. If the broken deployment ran migrations that altered your database schema or data, rolling back the code does not undo those migrations. This is the most important thing to understand about rollback.
- Environment variables. The
.envfile is shared across releases. If you changed an environment variable as part of the broken deployment, that change persists after rollback. - Uploaded files. The
storagedirectory is shared. User uploads and other stored files are not affected by rollback. - Cache. Cached configuration, routes, and views may contain data from the broken deployment. Consider clearing caches after rollback.
- Queue jobs. Jobs that were dispatched by the broken deployment are still in the queue. Workers running the rolled-back code may or may not be able to process them, depending on what changed.
Handling Database Migrations After Rollback
Database migrations are the trickiest part of rollback because they cannot be automatically undone. Here are strategies for dealing with this.
Strategy 1: Forward-Compatible Migrations
The safest approach is to write migrations that are forward-compatible. This means the old code can work with the new database schema, and the new code can work with both the old and new schemas.
For example, instead of renaming a column:
// DANGEROUS — breaks old code
$table->renameColumn('name', 'full_name');
Add the new column and deprecate the old one:
// SAFE — old code still works
$table->string('full_name')->nullable()->after('name');
Then in a subsequent deployment, migrate data from name to full_name, and in a third deployment, remove the old column. This multi-step approach means any individual deployment can be rolled back without breaking the database.
Strategy 2: Manual Migration Rollback
If the migration needs to be undone, you can SSH into your server (or use Deploynix's web terminal) and run:
php artisan migrate:rollback --step=1
This reverses the most recent migration batch. Be careful with this approach in production, especially if the migration modified existing data. The down() method of your migration must correctly reverse the changes.
Strategy 3: Database Restore
For catastrophic situations where migrations have corrupted data, restore from a database backup. If you have Deploynix's automated backups configured, you can restore to a point before the broken deployment.
This is the nuclear option and causes data loss for any writes that occurred between the backup and the restore. Use it only when the migration did something truly destructive.
Clearing Caches After Rollback
After rolling back, clear your application caches to ensure the old code does not encounter cached data from the broken deployment:
php artisan config:clear
php artisan route:clear
php artisan view:clear
php artisan cache:clear
You can run these commands through Deploynix's web terminal or SSH. Then re-cache for optimal performance:
php artisan config:cache
php artisan route:cache
php artisan view:cache
Dealing with Queue Workers
If you have queue workers running, restart them after rollback to ensure they are executing the rolled-back code:
Deploynix makes this easy — navigate to your site's Queue Workers section and restart the queue worker processes. Alternatively, you can run:
php artisan queue:restart
This sends a signal to all queue workers to finish their current job and then restart, picking up the rolled-back code.
Be aware that jobs dispatched by the broken deployment might still be in the queue. If these jobs reference classes, methods, or data structures that do not exist in the rolled-back code, they will fail. Monitor your failed jobs table after rollback and decide how to handle them.
Building a Rollback Checklist
When something goes wrong in production, clear thinking is hard. Having a checklist prepared in advance helps you act quickly and methodically.
- Confirm the issue. Verify that the problem is caused by the recent deployment, not an external service outage or unrelated infrastructure issue.
- Communicate. Let your team know you are rolling back. A quick Slack message prevents confusion and duplicate efforts.
- Roll back the code. Use Deploynix's one-click rollback to activate the previous working release.
- Clear caches. Run the cache clear commands to ensure clean state.
- Restart queue workers. Ensure workers are running the rolled-back code.
- Verify the fix. Browse your application and confirm the issue is resolved.
- Handle the database. If migrations ran, assess whether they need to be reverted or if they are forward-compatible.
- Monitor. Watch error logs and monitoring dashboards for the next 30–60 minutes.
- Communicate resolution. Let your team and affected users know the issue is resolved.
- Post-mortem. Once the dust settles, investigate why the issue was not caught in testing and what changes would prevent it in the future.
Preventing the Need for Rollback
While fast rollback is a critical safety net, the best rollback is one you never need. Here are practices that reduce the likelihood of broken deployments:
Write comprehensive tests. Feature tests that exercise your critical paths catch most issues before they reach production.
Use staging environments. Deploy to a staging server that mirrors production before deploying to production. Deploynix makes it easy to maintain multiple environments.
Use deployment hooks. Run smoke tests as a post-deployment hook. If the smoke test fails, you can configure automatic rollback.
Deploy frequently in small batches. Small deployments are easier to debug and roll back than large ones that change hundreds of files.
Review database migrations carefully. Migrations are the riskiest part of any deployment. Have them reviewed by a second pair of eyes.
Use feature flags. Deploy new code behind feature flags that you can toggle without a deployment. If a feature causes issues, disable the flag instead of rolling back.
Scheduled Deployments as a Safety Measure
Deploynix's scheduled deployment feature can also help reduce risk. Instead of deploying immediately when a PR is merged, schedule the deployment for a time when your team is available to monitor it. If something goes wrong, someone is already at their desk ready to roll back.
You can even schedule deployments during low-traffic periods to minimize the impact of any issues.
Conclusion
A failed deployment does not have to be a crisis. With Deploynix's release-based deployment strategy and one-click rollback, you can restore your application to a working state in 30 seconds. The key is understanding what rollback does and does not cover — especially regarding database migrations — and having a plan for each scenario.
Build the habit of writing forward-compatible migrations, maintaining comprehensive tests, and using staging environments. When something does slip through, roll back confidently, investigate calmly, and ship the fix when it is ready.
Your production deployments should be boring. Deploynix makes sure they are, and when they are not, it makes recovery fast. Sign up at deploynix.io and deploy with confidence.
Top comments (0)