Zero-downtime deployment of Java services: how not to crash production

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Zero-downtime deployment of Java services: how not to crash production



Imagine: Friday, 5:45 PM, you're deploying a new version of the service, go get coffee, come back — and the site is down. A 500 error, clients are nervous, the team lead is writing in Slack "What happened?". Sound familiar? If you work with Java and microservices, you know: stopping a service for deployment is not just a technical hiccup, it's a loss of money and reputation.



The good news: zero-downtime deployment is not magic, but a set of proven practices. In this article, we'll break down how to organize seamless deployment of Java applications: from concepts to real code on Spring Boot. You'll learn how to use rolling update, blue-green, and canary deployment so your users don't even notice updates.



Why do Java services crash during deployment?



The classic problem: the application starts, but the old process is still handling requests. You kill the old process — and new requests fall into a void. Or vice versa: the new instance hasn't warmed up yet, but traffic is already pouring in. In Java, this is exacerbated by the slow startup of the JVM, Hibernate initialization, and cache loading.



Main causes of downtime:

  • Abrupt termination of the old process — requests are cut off mid-way.
  • Unwarmed new instance — the first request is slow, the cache is empty.
  • Session breakage — the user loses cart data or authentication.
  • Database incompatibility — migrations break the old version.


Zero-downtime deployment of Java solves all these problems if approached systematically. Let's look at three main patterns.



Pattern 1: Rolling Update



This is the most popular approach in the world of Kubernetes and cloud platforms. The idea is simple: you have several instances of the service (replicas). You update them one by one: the old instance is stopped, a new one is started — and so on, until all replicas are new.



How it looks in practice with Spring Boot and Kubernetes



Let's say you have a microservice on Spring Boot deployed in Kubernetes. For a rolling update, all you need is the correct Deployment configuration and setting up graceful shutdown.



Step 1: Configure Spring Boot for graceful shutdown:



// application.ymlserver:  shutdown: graceful

spring: lifecycle: timeout-per-shutdown-phase: 30s


What does this do? When Kubernetes sends a SIGTERM signal, Spring Boot stops accepting new requests but finishes processing existing ones within 30 seconds. Without this setting, the JVM would simply kill the process, cutting off requests.



Step 2: Configure readiness probe and liveness probe:



@RestControllerpublic class HealthController {

@GetMapping("/actuator/health/readiness") public ResponseEntity<String> readiness() { // Check that the service is ready to accept traffic // For example, the database is connected, the cache is warmed return ResponseEntity.ok("OK"); }

@GetMapping("/actuator/health/liveness") public ResponseEntity<String> liveness() { // Check that the JVM is alive return ResponseEntity.ok("OK"); }}


Step 3: Kubernetes Deployment configuration:



apiVersion: apps/v1kind: Deploymentmetadata:  name: my-java-servicespec:  replicas: 3  strategy:    type: RollingUpdate    rollingUpdate:      maxSurge: 1        # how many extra pods can be created above the norm      maxUnavailable: 0  # no pod should be unavailable  template:    spec:      containers:      - name: app        image: myregistry/my-java-service:2.0        readinessProbe:                

Blogs

Book Recommendations