Zero-Downtime Deployments Without Kubernetes: Proven Approaches
For years Kubernetes has been the industry's favorite hammer, and every software deployment problem has looked like a nail. One reason folks reach for it is simple: it makes zero-downtime deployments feel as easy as flipping a toggle. Roll out a new version, keep the old one alive until the new one's steady, let the load balancer shuffle traffic around, and call it a day.
People were shipping code without downtime long before Kubernetes strutted onto the scene, though, and they're still doing it today, often with far less complexity, fewer moving parts, and a much lighter cognitive tax. Hang out with engineers who've been in the trenches for a while and you'll hear the same theme again and again: Kubernetes is convenient, but it's one way among several, and not always the best one.
In a world where "just use Kubernetes" has become the default refrain, the so-called "ancient ways" of deployment are making a comeback because they still work, shockingly well.
The load balancer, the original MVP
The most common answer from seasoned engineers comes with a shrug, as if they were answering a question about gravity: run two servers and let the load balancer handle it.
This approach is older than some of today's cloud engineers. You spin up two instances (or twenty), point a load balancer at them, and update one instance at a time. As long as your app knows how to politely bow out (more on that later), you can roll out new code without anyone noticing.
It isn't flashy, but it's bulletproof, a blunt instrument that has been trusted for decades. "We've been doing this for 30+ years," one engineer said, and the crowd basically nodded in unison.
If you're using NGINX or HAProxy, the core features are already baked in: health checks, connection draining, and graceful cutover. One commenter even reminded people that NGINX can swap upstreams without dropping packets, so there are no tears and no drama.
Blue-green deployments, the classic pattern
If Kubernetes deploys are the modern Web framework of uptime, blue-green deployments are the HTML 1.0 of the technique: simple, direct, and still perfectly effective.
Here's the playbook:
- You deploy the new version into the green environment.
- You keep the blue environment running the stable version.
- When green passes all your checks, you switch traffic over.
- If everything goes sideways, you flip traffic back to blue.
It's the deployment equivalent of switching HDMI ports on your TV, with low effort, high reliability, and zero downtime unless you count the milliseconds routing needs to catch up.
Engineers in the thread swear by it, and some even brag about how fast they can do it. One person said they used to switch entire fleets of over a thousand servers in under a minute, just by swapping symlinks. Speaking of which…
Symlinks, the UNIX trick that refuses to die
If there were a Hall of Fame for simple deployment hacks, this would be first-ballot material.
Before containers, before orchestration platforms, and before modern config management, people deployed apps by repointing a symlink called current from the old release directory to the new one.
The flow goes like this:
- Upload new code to
/var/www/releases/version-329320 - Update the
currentsymlink to point at it - Restart (or gracefully reload) the web server if needed
- Celebrate with something caffeinated
Because updating a symlink is an atomic operation, the switch is literally instant. Want to roll back? Point current back at the old directory. There's nothing to rebuild, nothing to redeploy to a container platform, and no need to close your eyes and pray that nothing breaks.
People who use this pattern today often pair it with tools like Ansible's deploy_helper, but you don't need special tooling. A Bash script works just as well, as long as you clean up old releases once in a while.
DNS, technically feasible and practically chaotic
A few engineers admitted they've tried using DNS to pull off zero-downtime deploys. The idea is simple: set a low TTL, flip your A record to the new server, and let propagation do the rest.
In practice it sort of works, right up until it doesn't.
Caching behavior is unpredictable across ISPs, corporate networks, browsers, and devices. One engineer said you can do it "if complexity is a concern," but others quickly chimed in with horror stories about DNS changes taking minutes, hours, or (yes) longer.
It's fine at small scale or for something informal. Using DNS as a load balancer, though, is like roasting a turkey in a microwave: technically possible and shockingly inconsistent.
Session draining, the secret sauce of graceful shutdowns
Whether you're using Kubernetes, a homegrown script, or a pile of rusty servers in a broom closet, the golden rule of zero downtime is the same. Your app has to be able to shut down without slamming the door on users.
The trick is listening for SIGTERM, the signal that tells your process to start winding down instead of dying instantly. Engineers in the thread were practically shouting from the rooftops that this is what makes deployments smooth.
At a high level, it works like this:
- App receives SIGTERM
- It stops accepting new requests
- It lets active requests finish
- After a grace period, the system kills it if it's still hanging around
Every web framework worth using supports this one way or another. One engineer said in Go it's "a 2 liner," Spring Boot has it built in, and even Python frameworks support it. If your app handles SIGTERM gracefully, you can roll out updates on almost any infrastructure.
Cloud platforms, zero downtime as a service
If you prefer managed services, the good news is that pretty much every cloud has worked out zero-downtime deploys by now, and none of them require you to understand the finer points of pod eviction.
Google Cloud Run spins up new revisions and routes traffic over gradually. AWS ECS and Fargate have been doing rolling updates with load balancers for years. Azure App Service lets you deploy into staging slots and swap them like a magician switching cups.
All of these are built on the same old ideas people have used for decades: multiple instances, load balancing, and health checks.
Docker without Kubernetes is still totally possible
Plenty of folks in the thread run everything in Docker and don't bother with Kubernetes. They're using:
- Docker Compose with manual scaling
- Traefik as a reverse proxy
- docker-swarm for lightweight orchestration
- Simple rolling updates one container at a time
It's the same idea as running multiple servers, except each "server" is a container. Traefik in particular makes life easy by routing traffic only to containers that report themselves as healthy.
One engineer described a workflow where CI/CD boots a second copy of the app image, waits for it to be healthy, routes traffic to it, updates the rest of the service, and then shuts down the staging copy. It sounds fancy, but the bones are very traditional.
The trickiest part is database migrations
If a boogeyman haunts zero-downtime deployments, it's database changes. New code expects new columns or tables while old code is still running, and you can't break either one.
The answer from the thread is the same advice you hear from experienced teams everywhere: write migrations that don't break old code. That usually means:
- Add new columns instead of renaming
- Stop writing to old columns before dropping them
- Avoid destructive changes in the same deploy
- Clean up schema later, after all old app versions are gone
Get good at this and half your deployment anxiety disappears.
So do you actually need Kubernetes?
If you're running at global scale with services talking to other services about more services, sure, Kubernetes helps. If you're wrangling a large team that needs consistent tooling and guardrails, it absolutely earns its keep.
But if you're running a few web apps, your fleet is measured in servers rather than continents, and you want something that works without dedicating part of your life to YAML, the old ways still slap. Load balancers, two servers, symlinks, reverse proxies, SIGTERM, blue-green, and health checks are simple, predictable, and rock-solid.
Zero-downtime deployments without Kubernetes are standard practice for tons of teams, the kind of thing engineers have been running in production for decades without making a fuss, and they're not going anywhere.