Rolling Deployments for Python Apps

Learn to update Python apps with rolling deployments in Kubernetes. Step-by-step guide with hands-on exercises, troubleshooting, and next steps.

Focus: update python apps with rolling deployments

Sponsored

Your Python API is serving thousands of requests when your team asks you to roll out a critical bug fix. You push the new image, run kubectl set image, and watch in horror as 30% of users start seeing 500 errors. What went wrong? You updated all replicas at once, creating a brief but painful service outage. Rolling deployments exist to solve this exact problem — they update your Python apps incrementally, keeping every pod available throughout the release. In this lesson, you'll master rolling deployments in Kubernetes, from the mental model to hands-on kubectl commands, so your next update goes unnoticed by your users.

The problem this lesson solves

Imagine you run a Flask or FastAPI service with 10 replicas behind a Kubernetes Service. When you release a new version, the naive approach — deleting all pods and starting new ones — drops your service to zero availability for several seconds or minutes. During that window, every request fails. Even a short outage can cost you money, reputation, and user trust.

The need to update Python apps with rolling deployments arises because modern software changes constantly. Bug fixes, dependency upgrades, config changes, and feature releases happen weekly or daily. You need a release mechanism that:

  • Keeps your service available during the update
  • Allows you to catch problems early
  • Lets you roll back quickly if something goes wrong
  • Minimizes risk without requiring complex orchestration

Kubernetes answers this with rolling updates — a strategy where new pods are created gradually while old pods are terminated one by one. By the time the update is complete, every pod runs the new version, and no traffic is lost because at least one old pod is always serving until a new pod is ready. This is the default update strategy for Deployment objects, and every Python developer deploying to Kubernetes should understand it.

You've likely already used kubectl apply to create deployments. Without a solid grasp of rolling updates, you risk either full downtime or a release that looks successful but actually deployed a broken version to all users. This lesson eliminates that risk.

Core concept / mental model

What is a rolling deployment?

A rolling deployment (or rolling update) is a strategy for updating a Deployment's pods incrementally. It ensures that the number of available pods never drops below a threshold you define, and the number of pods above the desired count never exceeds a limit. New pods are created before old ones are terminated, so the service stays available.

Think of it like a conveyor belt

Picture a bakery line with 10 workers (pods) baking cookies (serving requests). You need to replace one worker with a new version. A rolling deployment doesn't fire everyone at once. It hires one new worker, waits until they're fully trained and producing cookies at the same rate (ready), then lets one old worker go. It repeats this until all 10 are the new version. Traffic never stops because there's always someone taking orders.

Key terminology

  • ReplicaSet: The group of pods running a specific version. A deployment manages one or more ReplicaSets.
  • MaxUnavailable: The maximum number of pods that can be unavailable during the update. Default is 25%.
  • MaxSurge: The maximum number of pods above the desired count during the update. Default is 25%.
  • Readiness probe: A check that tells Kubernetes when a pod is ready to receive traffic. Rolling updates rely on this to avoid sending requests to a broken new pod.

How it works step by step

Step 1 — Trigger an update

You trigger a rolling update by changing the deployment's pod template — typically the container image tag or a config value. This can be done with:

  • kubectl set image deployment/my-app my-app=myregistry/my-python-app:v2
  • kubectl edit deployment/my-app
  • kubectl apply -f deployment.yaml with a modified manifest

Step 2 — Kubernetes creates a new ReplicaSet

The Deployment controller sees the spec change and creates a new ReplicaSet with the desired number of replicas (e.g., 3). It doesn't touch the old ReplicaSet yet.

Step 3 — Scale up the new, scale down the old

The controller alternates between scaling up the new ReplicaSet and scaling down the old one, respecting maxSurge and maxUnavailable. For example, with maxSurge: 25% and maxUnavailable: 25%, on a 4-replica deployment, it might add one new pod (surge), then remove one old pod (keeping 3 available), and repeat.

Step 4 — Wait for readiness

Before Kubernetes terminates an old pod, it ensures the new pod is ready (passing its readiness probe). If the new pod never becomes ready, the update pauses, and the old pods remain running.

Step 5 — Completion and rollback

When all new pods are ready and old pods are scaled to zero, the update is complete. The old ReplicaSet remains (scaled to zero) for potential rollback, which you can trigger with kubectl rollout undo.

Hands-on walkthrough

Prerequisite setup

Make sure you have a running cluster (e.g., minikube or kind) and kubectl configured. Clone a simple Python app or use the following example.

Create a simple Python deployment

Create a file app.py with a minimal Flask-like server (or just use a placeholder). Here's a complete example using a small Python HTTP server:

# app.py
from http.server import HTTPServer, BaseHTTPRequestHandler
import os

VERSION = os.getenv("APP_VERSION", "v1")

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.end_headers()
        self.wfile.write(f"Hello from {VERSION}!\n".encode())

if __name__ == "__main__":
    server = HTTPServer(("0.0.0.0", 8000), Handler)
    print(f"Starting {VERSION} on port 8000")
    server.serve_forever()

Build two images: my-python-app:v1 and v2, where v2 sets the environment variable to v2. Push them to a registry or load them into your cluster (e.g., with minikube image load).

Create a deployment manifest deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-python-app
spec:
  replicas: 4
  selector:
    matchLabels:
      app: my-python-app
  template:
    metadata:
      labels:
        app: my-python-app
    spec:
      containers:
      - name: app
        image: myregistry/my-python-app:v1
        ports:
        - containerPort: 8000
        # Readiness probe so Kubernetes knows when the pod is ready
        readinessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 2
          periodSeconds: 3

Apply it:

kubectl apply -f deployment.yaml
kubectl get pods -l app=my-python-app

Expected output: four pods running, all with Running status.

Trigger a rolling update

Update the image to v2:

kubectl set image deployment/my-python-app app=myregistry/my-python-app:v2
kubectl rollout status deployment/my-python-app

Watch the pods change:

kubectl get pods -l app=my-python-app -w

You'll see new pods being created (with names ending in a different ReplicaSet hash) and old ones terminating. The rollout completes when the status shows deployment "my-python-app" successfully rolled out.

Verify the update

kubectl get rs -l app=my-python-app
kubectl get pods -o custom-columns=NAME:.metadata.name,IMAGE:.spec.containers[0].image
kubectl rollout history deployment/my-python-app

Expected: two ReplicaSets (old scaled to 0, new at 4), all pods using v2, and history shows revision 1 and 2.

Compare options / when to choose what

Kubernetes offers several update strategies. Here's how they compare:

Strategy When to use Pros Cons
Recreate When the app cannot run multiple versions simultaneously (e.g., uses exclusive locks) Simplest, no surge Full downtime during update
RollingUpdate (default) Most Python web services Zero downtime, gradual rollout, automatic rollback Slightly more complex, uses extra resources for surge
Blue/Green (via manual) When you need instant switch and full version separation Zero downtime, instant rollback Requires extra infrastructure (2x resources)
Canary (manual) When you want to test with a small percentage of traffic Controlled exposure, metrics-based Requires additional tooling (e.g., Flagger)

For typical Python microservices, RollingUpdate is the default and best choice. It's built into Deployment and requires zero extra configuration. If your app uses a database with incompatible schema changes, you might need blue/green or canary to avoid breaking old pods. But if you can keep backward compatibility, rolling is safer and simpler.

Troubleshooting & edge cases

Issue — New pods never become ready

Your readiness probe might be failing. Check with kubectl get pods and inspect logs:

kubectl logs <new-pod-name>
kubectl describe pod <new-pod-name>

Common causes: wrong health check path, image crash on startup, or missing environment variables. Fix the issue and the rollout will resume automatically (or you can restart the deployment).

Issue — Rollout stuck

Run kubectl rollout status to see progress. If it hangs, inspect with kubectl describe deployment. Look for events like FailedCreate or Unhealthy. Often it's a resource constraint (insufficient CPU/memory) or a misconfigured probe.

Issue — Rollback fails

If you need to undo a bad rollout:

kubectl rollout undo deployment/my-python-app

This reverts to the previous revision. If that revision also fails, you can specify a revision number:

kubectl rollout undo deployment/my-python-app --to-revision=1

Edge case — Multiple containers changing at once

If your pod has multiple containers, the deployment updates them simultaneously within each pod. Make sure all containers are compatible; otherwise, the readiness probe might fail for the whole pod.

What you learned & what's next

You now understand the core problem of updating Python apps, the mental model of rolling deployments, and the step-by-step mechanism. You also completed a hands-on exercise where you built a minimal Python app, deployed it, updated it with kubectl set image, and verified the rollout. You learned about strategies like Recreate and Blue/Green and when to choose them, and you know how to troubleshoot common issues.

Specifically, you can now:

  • Explain why rolling deployments are essential for zero-downtime updates
  • Manage maxUnavailable and maxSurge settings to control the rollout pace
  • Use kubectl rollout commands to monitor, pause, resume, and undo updates
  • Diagnose and fix readiness probe failures

You've mastered the default Deployment update path. But what about strategies that give you even more control, like relying on the next lesson in this track — canary deployments — where you can send a small percentage of traffic to the new version and observe metrics before fully rolling out? That's exactly what you'll tackle next. Practice the rolling update with your own Python service until the process feels automatic.

Practice recap

Try updating your own Python deployment with a readiness probe that intentionally fails (e.g., a wrong path) and watch the rollout hang. Then fix the probe and resume the update with kubectl rollout resume. This hands-on exercise will cement your understanding of readiness checks and rollout control.

Common mistakes

  • Forgetting to define a readiness probe — without it, Kubernetes sends traffic to new pods before they're ready, causing intermittent 500s.
  • Using maxUnavailable: 0 without maxSurge: 1 — the rollout can't proceed because no old pod can be taken down and no new pod can be created.
  • Updating the image tag but forgetting to push the new image to the registry the cluster can access — pods will pull the image and fail with ImagePullBackOff.
  • Assuming the rollout is complete just because kubectl get pods shows all pods running — always run kubectl rollout status to confirm.
  • Scaling up replicas during a rolling update manually — this can conflict with maxSurge and cause the rollout to behave unexpectedly.

Variations

  1. Use kubectl rollout pause to pause the rollout after a few pods are replaced, inspect logs, then resume to continue.
  2. Manually implement a canary deployment by creating a second deployment with a small replica count and adjusting a Service selector — useful when you need finer control.
  3. Use a GitOps approach with Argo Rollouts or Flux to automate rolling updates and automate rollbacks based on metrics.

Real-world use cases

  • Deploy a bug fix to a Flask API serving production traffic without dropping a single request, ensuring users stay connected.
  • Update a Celery worker deployment to a new code version while jobs are still processing — rolling updates keep the queue draining continuously.
  • Upgrade a Django app's dependencies or image tag across a large replica set in a multi-zone cluster, minimizing resource use and downtime.

Key takeaways

  • Rolling deployments update pods incrementally, keeping your Python service available throughout the release.
  • The deployment controller creates a new ReplicaSet and scales it up while scaling down the old one, respecting maxUnavailable and maxSurge.
  • Readiness probes are critical — without them, Kubernetes may send traffic to pods that aren't actually ready.
  • You can monitor, pause, resume, and rollback using kubectl rollout commands.
  • Rolling is the default strategy and usually the best choice for Python web services; use Recreate for apps that can't run multiple versions, and consider Canary/Blue-Green for extra control.

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.