Rolling Updates for Python Apps

Implement rolling updates for Python apps in Kubernetes, with hands-on steps, troubleshooting, and next steps.

Focus: implement rolling updates for python apps

Sponsored

You’ve just pushed a new version of your Python API — maybe you’ve added a /health endpoint, fixed a sneaky bug, or upgraded your dependencies. In development, you restart your local server and you’re done. But in production, a naive approach — deleting all your old pods and creating new ones — will drop every in-flight request and leave your users staring at a 502 error. This is exactly the pain that rolling updates solve: a safe, incremental deployment strategy that keeps your service available throughout the release. In this lesson, you’ll learn how to implement rolling updates for Python apps in Kubernetes, from the underlying mechanics to hands-on commands you can run today.

The problem this lesson solves

When you update a running application, the moment your old pods are terminated, they stop accepting new connections and close any in-flight requests. If you replace all pods at once — or worse, delete the Deployment and recreate it — your service experiences a hard cutover with a burst of connection failures, timeouts, and user-visible errors.

Rolling updates fix this by replacing pods gradually. Instead of tearing down the entire fleet, you create new pods while old ones are still serving. Kubernetes watches health checks (readiness probes) to decide when it’s safe to move on. The result: zero downtime and a stable, continuously available service — even during buggy releases, because you can pause or roll back at any moment.

For Python developers, rolling updates are especially important because Python services often carry state in memory (like a FastAPI dependency cache) or rely on long-lived connections (e.g., WebSockets). A blunt restart might break those connections. Rolling updates give you a controlled transition that respects your application’s lifecycle.

Core concept / mental model

Think of a Deployment as a staging manager for your pods. It doesn’t just create and destroy; it orchestrates the changeover. You define a desired state (how many replicas you want, which image version, what ports, and what probes). The Deployment controller ensures that the cluster always moves toward that state — but it does so in a special way when you change the image.

Imagine you run a restaurant with 10 servers (pods). You can’t suddenly replace every server at the same time because you’d have no one serving tables. Instead, you bring in a few new servers, let them learn the menu (wait for readiness), and only then let the old servers go home. That’s a rolling update.

Key terms you’ll hear repeatedly:

  • ReplicaSet — the controller that maintains a desired number of identical pod replicas. A Deployment manages one or more ReplicaSets.
  • MaxSurge — how many extra pods you can create above the desired count during an update (as a number or percentage).
  • MaxUnavailable — how many pods may be unavailable during the update (number or percentage).
  • Readiness probe — a check Kubernetes uses to determine if a pod is ready to serve traffic. If it fails, the pod is removed from the Service’s endpoints.
  • Rollback — reverting to a previous Revision of the Deployment, often with a single command.

During a rolling update, Kubernetes creates a new ReplicaSet with the updated pod template, then scales it up while scaling down the old ReplicaSet — step by step. The precise behavior is governed by strategy.rollingUpdate.maxSurge and maxUnavailable.

How it works step by step

When you run kubectl set image deployment/myapp myapp=myapp:v2 (or apply a manifest with a new image), the Deployment controller does the following:

  1. Create a new ReplicaSet with the updated pod template (the new image, labels, etc.).
  2. Scale up the new ReplicaSet — adding new pods up to maxSurge (e.g., 25%). These new pods start alongside the old ones.
  3. Wait for the new pods to become Ready — the readiness probe must pass; otherwise the update pauses.
  4. Scale down the old ReplicaSet — terminating old pods down to maxUnavailable (e.g., 25%), so the total available pods never drop below the desired count.
  5. Repeat steps 2–4 until the new ReplicaSet has 100% of replicas and the old one is scaled to zero.
  6. Record a new Revision in the Deployment’s rollout history — this is what allows you to roll back later.

The exact pacing depends on your maxSurge and maxUnavailable settings. By default, both are 25% (of the total replicas). For a 4-replica deployment, that means at most 1 new pod above the target and 1 pod unavailable at a time — a nice balance between speed and safety.

Why readiness probes matter — without them, Kubernetes treats a pod as “Ready” as soon as it’s running. If your Python app takes 10 seconds to load models or connect to a database, those pods will be marked Ready too early, and traffic will be routed to an app that isn’t actually serving. Always define a readinessProbe that hits an endpoint like /health or /ready.

Hands-on walkthrough

Let’s implement rolling updates for a simple Python FastAPI app. You’ll create a Deployment, update it, and observe the rollout — all with kubectl.

1. Start with a minimal Deployment

Create a file called deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 4
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: app
        image: myregistry/myapp:v1
        ports:
        - containerPort: 8000
        readinessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 5
          periodSeconds: 5
        livenessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 10
          periodSeconds: 10

Apply it:

kubectl apply -f deployment.yaml

2. Update the image to v2

Now bump the image version — this is the classic way to trigger a rollout:

kubectl set image deployment/myapp app=myregistry/myapp:v2

Watch the rollout in real time:

kubectl rollout status deployment/myapp

Expected output (abbreviated):

Waiting for deployment "myapp" rollout to finish: 1 out of 4 new replicas have been updated...
...
Waiting for deployment "myapp" rollout to finish: 4 out of 4 new replicas have been updated...
deployment "myapp" successfully rolled out

While the rollout runs, inspect what’s happening:

kubectl get pods -l app=myapp

You’ll see pods with different REVISION suffixes or different image versions side by side — that’s the rolling update in action.

3. Verify the rollout history

kubectl rollout history deployment/myapp

Output:

deployments "myapp"
REVISION  CHANGE-CAUSE
1         <none>
2         <none>

To record the reason for the change, use the --record flag (deprecated in newer versions, but still useful in older ones) or set the kubernetes.io/change-cause annotation in your manifest.

4. Simulate a failed rollout and roll back

Suppose v3 is buggy — its /health endpoint always returns 503. Deploy it and watch the rollout pause:

kubectl set image deployment/myapp app=myregistry/myapp:v3
kubectl rollout status deployment/myapp

The command will hang because the new pods fail the readiness probe. After a timeout, you’ll see something like:

Waiting for deployment "myapp" rollout to finish: 1 old replicas are pending termination...

Stop waiting and roll back to the previous revision:

kubectl rollout undo deployment/myapp

Or roll back to a specific revision:

kubectl rollout undo deployment/myapp --to-revision=2

5. Apply a new Deployment with custom rolling update strategy

For more control, define the strategy in the manifest:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 6
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 2
      maxUnavailable: 1
  ...

This allows 2 extra pods above the desired count and at most 1 pod unavailable — good if you want to keep a tight minimum availability but don’t mind a slight pop in resource usage.

Compare options / when to choose what

Rolling updates are the default for Deployments, but they’re not the only strategy. Here’s how they compare to alternatives.

Strategy How it works Zero downtime? When to choose
RollingUpdate Incrementally swaps pods, respecting probes Yes, if probes are correct Default choice for most Python web services; best for steady traffic and no external state changes
Recreate Deletes all old pods, then creates new ones No — brief downtime When your app doesn’t support multiple versions running at once (e.g., strong schema incompatibility)
Blue/Green Deploy new version alongside old, then switch Service selector Yes (switch is atomic) When you need instant rollback and can double resource usage
Canary Send a small % of traffic to new version, gradually increase Yes When you want to validate new version with real traffic before full rollout

For Python apps, rolling updates are usually the simplest and most cost-effective. They don’t require extra infrastructure or traffic-splitting logic, and Kubernetes’ built-in reconciliation handles the details. If you’re running a public API with moderate traffic, stick with rolling updates. If you need canary ability, consider a service mesh like Istio or a progressive delivery tool like Argo Rollouts.

Pro tip: If your Python app migrates a database schema, a rolling update may create issues where old pods write to a new schema or vice versa. In that case, run migrations as a separate pre-deployment Job, or use the Recreate strategy for that specific release.

Troubleshooting & edge cases

Rollout hangs at “Waiting for deployment ... 0 out of N new replicas have been updated”

  • New pods keep crashing. Check kubectl logs and kubectl describe pod on the new pods. Common causes: image pull errors, missing environment variables, or your app exits early.

Rollout proceeds even though new pods are failing readiness

  • You may not have a readiness probe, or the probe path is wrong. Kubernetes only waits for the pod to be ready — if your probe is too lenient (e.g., always returns 200), the update will continue even if the app is broken. Test the probe manually with curl inside the pod.

Old pods are not being terminated

  • This happens if maxUnavailable is 0 and your new pods aren’t becoming Ready. Check readiness probe logs and ensure the new image actually works. If the new pods are stuck, the rollout intentionally pauses to protect availability.

You see “error: deployment already exists”

  • You’re using kubectl create instead of kubectl apply. Use kubectl apply -f deployment.yaml to update the existing Deployment.

Rollback doesn’t restore the old version

  • Make sure you have a previous Revision in the history. If you edited the Deployment in place without changing its template, Kubernetes may not create a new Revision. To force a new revision, change the image field explicitly.

Connection resets during rollout

  • This usually happens because you’re terminating pods too aggressively. Lower maxUnavailable (e.g., 0) and increase maxSurge (e.g., 1 or a percentage). Also configure a terminationGracePeriodSeconds so in-flight requests can finish before SIGTERM.

What you learned & what's next

You now know how to implement rolling updates for Python apps in Kubernetes. You understand the mental model of ReplicaSets, the step-by-step update process, how to trigger a rollout with kubectl set image, how to monitor it with kubectl rollout status, and how to roll back when things go wrong. You also saw how to tune the strategy with maxSurge and maxUnavailable, and how to debug common issues like stuck rollouts or probe failures.

In the next lesson, you’ll take rolling updates further by automating them — learn how to use ConfigMaps and Secrets to change environment configuration without rebuilding your image, and how to trigger rollouts on config changes as part of a CI/CD pipeline. You’ll also explore advanced rollout patterns like canary deployments and progressive delivery with Argo Rollouts.

Practice recap: Create a Deployment with 3 replicas of a FastAPI app. Add a readiness probe on /health. Update the image to a new tag, watch the rollout, and then trigger a rollback to the previous version. Verify that zero traffic is lost by running a load test in parallel.

Practice recap

Create a Deployment with 3 replicas of a simple FastAPI app that has a readiness probe on /health. Update the image to a new tag and watch the rollout with kubectl rollout status. Then trigger a rollback and verify that traffic is never interrupted by running a load test in parallel. This hands-on exercise will cement the workflow—and prepare you for the next lesson on ConfigMaps and Secrets.

Common mistakes

  • Forgetting to define a readiness probe — Kubernetes treats the pod as ready as soon as it's running, so broken apps get traffic and the rollout looks successful.
  • Setting maxUnavailable to 0 without allowing enough maxSurge, causing the rollout to hang forever if new pods never become ready.
  • Manually deleting or recreating pods during an update, which can interfere with the ReplicaSet controller and lead to unpredictable states.
  • Ignoring the rollout history — if you don't annotate changes with a change-cause, you won't know what version you're rolling back to.

Variations

  1. Use kubectl rollout restart deployment/myapp to force a rolling update without changing the image — useful for picking up a new Secret or ConfigMap.
  2. Adopt the Recreate strategy for apps that can’t run two versions at the same time (e.g., due to schema migrations).
  3. Use a tool like Argo Rollouts to implement canary or blue-green deployments with automated traffic analysis.

Real-world use cases

  • Deploying a new version of a FastAPI backend to production with zero downtime, so users never see a 502 error.
  • Updating a Celery worker deployment to a new dependency version without killing in-flight tasks.
  • Rolling back a buggy release of a Django app in seconds using kubectl rollout undo.

Key takeaways

  • Rolling updates replace pods incrementally, using readiness probes to ensure new pods are healthy before old ones are terminated.
  • MaxSurge and MaxUnavailable control the update speed and availability guarantee.
  • Always define a readiness probe for Python web apps so Kubernetes routes traffic only to truly prepared pods.
  • Use kubectl rollout status and kubectl rollout history to track and audit your deployments.
  • Rolling updates are the default, but you can choose Recreate, Blue-Green, or Canary depending on your app’s needs.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.