Zero-Downtime Deploys Without a Dedicated Load Balancer
Learn how to achieve seamless application updates using Nginx and atomic symbolic link switching, eliminating the need for expensive external load balancers.
Disclosure: this article contains an affiliate link. DotExe may earn a commission if you purchase through it, at no extra cost to you. Any discount is subject to Hostinger's current offers (20% OFF) and eligibility terms.
Achieving zero-downtime deployments is often perceived as a challenge requiring complex infrastructure like cloud-native load balancers or Kubernetes ingress controllers. However, for many small-to-medium applications, you can achieve the same result using a simple Nginx reverse proxy and atomic file system operations. If you are just starting out, you might consider Hostinger Web Hosting for a reliable foundation to host your application deployments.
The Strategy: Atomic Symlink Switching
The core concept relies on maintaining two distinct directories for your application code: current and next. By using a symbolic link to point Nginx to the active directory, you can swap versions instantly without restarting the web server.
- Deploy the new code to a timestamped folder (e.g.,
/var/www/app-20231027). - Run your build scripts and migrations.
- Update the symbolic link to point to the new folder.
- Reload Nginx to pick up the changes.
Implementation Steps
First, ensure your Nginx configuration points to a symbolic link, not a hard path:
server {
listen 80;
server_name example.com;
root /var/www/app/current/public;
location / {
proxy_pass http://localhost:3000;
}
}
When deploying, you perform the swap atomically using the ln command:
# Deploy new version
mkdir /var/www/app/releases/v2
cp -r . /var/www/app/releases/v2
# Atomic switch
ln -sfn /var/www/app/releases/v2 /var/www/app/current
# Reload Nginx without dropping connections
sudo nginx -s reload
Handling Process Management
Since you are not using a load balancer to drain connections, you must ensure your application handles signals gracefully. If you are running a Node.js application, use pm2 or a similar process manager to perform a "graceful reload." This allows the old process to finish serving existing requests while the new process starts up.
// In your Node.js app, handle SIGTERM to close database connections
process.on("SIGTERM", () => {
server.close(() => {
console.log("Process terminated");
process.exit(0);
});
});
Why This Works
Nginx's reload command performs a graceful shutdown of worker processes. It stops accepting new connections on the old workers but allows them to finish their current tasks. Simultaneously, it spawns new worker processes that read the updated symbolic link. Because the file system operation ln -sfn is atomic, there is no window where the directory path does not exist.
Conclusion
Zero-downtime deployments do not always require a complex stack. By leveraging atomic symlink swaps and Nginx's graceful reload capabilities, you can maintain high availability for your users while keeping your infrastructure lean and manageable. Always ensure your database migrations are backward compatible to prevent errors during the transition period.