Learn how to build a reliable CI/CD pipeline for Kubernetes Deployments with safe rollout checks, automated validation, and fast rollback. This guide covers prerequisites, step-by-step implementation, verification commands, failure modes, and an operations checklist, with practical examples you can adapt to your cluster.
Intro
Kubernetes Deployments manage the lifecycle of your application pods, providing declarative updates, scaling, and rollback. But manually applying YAML files and hoping for the best is risky. CI/CD automation brings repeatability, safety, and speed to your release process. This guide walks through building a practical CI/CD pipeline for Kubernetes Deployments, from environment setup to automated rollout checks and rollback procedures. You'll learn to verify your pipeline's behavior, diagnose failures, and establish an operational checklist that keeps your releases reliable.
By the end of this guide, you will have:
- A version-controlled, automated deployment workflow using GitHub Actions (or your preferred CI/CD tool)
- A Kubernetes Deployment configured with rolling updates, health probes, and resource limits
- Verification steps that act as quality gates before promoting to production
- A documented failure recovery and rollback plan
- An operations checklist to maintain consistency across releases
Version and Environment Inventory
Before automating, confirm your toolchain versions and cluster topology. Inconsistent versions are a common source of pipeline failures. This section lists the required components and a minimal cluster layout.
Prerequisites:
- Kubernetes cluster v1.19 or later (recommended for stable kubectl behavior and API compatibility)
- kubectl CLI v1.19+ installed and configured to reach your cluster
- Access to a container registry (for example, Docker Hub, Google Container Registry, Amazon ECR)
- A CI/CD platform (GitHub Actions, GitLab CI, Jenkins, or similar)
- Application source code with a Dockerfile
- A dedicated namespace for testing (we will create one in the next section)
Run this version check to confirm compatibility:
kubectl version --short
# Expected output:
# Client Version: v1.24.0
# Server Version: v1.24.0
If the client and server versions differ by more than one minor release, upgrade kubectl or the cluster to avoid deprecated API issues.
Cluster topology (hypothetical but illustrative):
- Namespace:
production - Deployment:
myapp - Service:
myapp-svc(NodePort or LoadBalancer) - Resource quotas per namespace: CPU 2 cores, Memory 4Gi
Keep this inventory in a shared document (for example, a README.md in your infrastructure repository) so every team member works with the same assumptions.
Safe Configuration Path
Start with a narrow, measurable pilot deployment. Avoid broad changes that affect critical services. Use a separate namespace for testing and gradually promote to production.
Step 1: Create a dedicated namespace
Create a namespace that mirrors production, including resource quotas and network policies if you use them.
kubectl create namespace myapp-test
# Expected: namespace/myapp-test created
Apply a resource quota to prevent runaway resource consumption:
# quota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: myapp-test-quota
namespace: myapp-test
spec:
hard:
requests.cpu: "2"
requests.memory: 4Gi
limits.cpu: "4"
limits.memory: 8Gi
kubectl apply -f quota.yaml -n myapp-test
# Expected: resourcequota/myapp-test-quota created
Step 2: Define a Deployment manifest with explicit rolling update strategy
A rolling update strategy ensures zero downtime by gradually replacing old pods with new ones. Configure maxUnavailable and maxSurge to control the pace. Include readiness and liveness probes so Kubernetes only routes traffic to healthy pods.
Example deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: myapp-test
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myregistry/myapp:1.0.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
Apply it:
kubectl apply -f deployment.yaml
# Expected: deployment.apps/myapp created
Check that pods come up:
kubectl get pods -n myapp-test
# Expected: 3 pods with Running status and 1/1 Ready
Step 3: Automate with a simple CI/CD pipeline
Below is a minimal GitHub Actions workflow that builds and deploys on every push to main. It uses kubectl with a service account token stored as a secret. Adjust the registry and image names for your environment.
First, create a Kubernetes service account with permissions limited to the myapp-test namespace:
# service-account.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: github-actions
namespace: myapp-test
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: github-actions-role
namespace: myapp-test
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "update", "patch"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: github-actions-binding
namespace: myapp-test
subjects:
- kind: ServiceAccount
name: github-actions
namespace: myapp-test
roleRef:
kind: Role
name: github-actions-role
apiGroup: rbac.authorization.k8s.io
Apply this and retrieve a token:
kubectl apply -f service-account.yaml -n myapp-test
# Expected: serviceaccount/github-actions created, role..., rolebinding...
# Get the token (assuming Kubernetes 1.24+ with TokenRequest API):
kubectl create token github-actions -n myapp-test --duration=24h
# Copy the token output.
Store the token and cluster server URL as secrets in your GitHub repository: KUBE_TOKEN and KUBE_SERVER.
Now create the workflow file .github/workflows/deploy.yml:
name: Deploy to Kubernetes
on:
push:
branches: [ main ]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build and push image
env:
REGISTRY: myregistry
IMAGE: myapp
TAG: ${{ github.sha }}
run: |
docker build -t $REGISTRY/$IMAGE:$TAG .
echo ${{ secrets.REGISTRY_PASSWORD }} | docker login $REGISTRY -u ${{ secrets.REGISTRY_USERNAME }} --password-stdin
docker push $REGISTRY/$IMAGE:$TAG
- name: Set up kubectl
uses: azure/setup-kubectl@v1
- name: Deploy to cluster
env:
KUBE_SERVER: ${{ secrets.KUBE_SERVER }}
KUBE_TOKEN: ${{ secrets.KUBE_TOKEN }}
run: |
kubectl config set-cluster mycluster --server=$KUBE_SERVER --insecure-skip-tls-verify=true
kubectl config set-credentials github-actions --token=$KUBE_TOKEN
kubectl config set-context mycontext --cluster=mycluster --user=github-actions --namespace=myapp-test
kubectl config use-context mycontext
kubectl set image deployment/myapp myapp=myregistry/myapp:${{ github.sha }} -n myapp-test
kubectl rollout status deployment/myapp -n myapp-test --timeout=120s
This pipeline builds the image, pushes it to the registry, updates the Deployment image, and waits for the rollout to complete. The rollout status command fails the pipeline if the deployment does not become healthy within 120 seconds.
Verification and Diagnostics
After applying changes, verify that the rollout succeeded and the application is serving traffic correctly. Integrate these checks into your pipeline as automated gates.
Basic rollout status
kubectl rollout status deployment/myapp -n myapp-test
# Expected: deployment "myapp" successfully rolled out
Inspect pod health
kubectl get pods -n myapp-test
# Expected: 3 pods with status Running and READY 1/1
View rollout history
kubectl rollout history deployment/myapp -n myapp-test
# Expected output:
# REVISION CHANGE-CAUSE
# 1 <none>
# 2 <none>
To record a change cause, add an annotation before applying:
kubectl annotate deployment/myapp kubernetes.io/change-cause="Deploy commit $(git rev-parse --short HEAD)" -n myapp-test --overwrite
Describe events for diagnostics
kubectl describe deployment myapp -n myapp-test
# Look for Events section: ScalingReplicaSet, SuccessfulCreate, etc.
Check logs if a pod fails
kubectl logs deployment/myapp -n myapp-test --tail=20
| Command | Expected Outcome |
|---|---|
kubectl rollout status deployment/myapp -n myapp-test | "successfully rolled out" |
kubectl get pods -n myapp-test | All pods Running and Ready |
kubectl get replicasets -n myapp-test | New ReplicaSet has desired replicas, old scaled down |
kubectl describe deployment myapp -n myapp-test | No error events |
Use these checks as gates in your pipeline before promoting to production. For example, after rollout status, add a step that runs a smoke test:
kubectl run smoke-test --image=curlimages/curl --rm -it --restart=Never -n myapp-test -- -s http://myapp-svc:8080/healthz
# Expected: HTTP 200 response
Failure Modes and Recovery
Even with automation, failures happen. Common failure modes include image pull errors, readiness probe failures, and insufficient resources. Knowing how to recover quickly is critical.
Failure 1: ImagePullBackOff
- Cause: Incorrect image tag or registry credentials.
- Detection:
kubectl get podsshowsImagePullBackOff. - Recovery: Fix the image tag or credentials, then apply a corrected manifest or update the Deployment.
Example diagnostic:
kubectl get pods -n myapp-test
# NAME READY STATUS RESTARTS AGE
# myapp-765d459796-8s9k7 0/1 ImagePullBackOff 0 2m
kubectl describe pod myapp-765d459796-8s9k7 -n myapp-test
# Look for Events: Failed to pull image ...
Failure 2: CrashLoopBackOff
- Cause: Application crashes on startup, often due to misconfiguration.
- Detection:
kubectl get podsshowsCrashLoopBackOff. - Recovery: Check logs with
kubectl logs, fix the code/config, and push a new image.
kubectl logs pod/myapp-765d459796-8s9k7 -n myapp-test --previous
# Examine stack trace or error message.
Failure 3: Rollout stuck due to readiness probe failure
- Cause: New pods never become ready, so the rollout stalls.
- Detection:
kubectl rollout statustimes out or shows progressing. - Recovery: Investigate readiness probe endpoint or application behavior. Rollback if needed.
Failure 4: Insufficient resources
- Cause: Resource quota exceeded or cluster capacity limit.
- Detection: New pods remain in
Pendingstate. - Recovery: Scale down other workloads or increase resource quotas.
Rollback procedure
Always have a rollback plan and test it in a non-production namespace before you need it in production.
# Rollback to previous revision
kubectl rollout undo deployment/myapp -n myapp-test
# Expected: deployment.apps/myapp rolled back
# Or rollback to a specific revision
kubectl rollout undo deployment/myapp --to-revision=2 -n myapp-test
# Expected: deployment.apps/myapp rolled back
After rollback, verify:
kubectl rollout status deployment/myapp -n myapp-test
# Expected: successfully rolled out
| Failure Mode | Symptom | Recovery Action |
|---|---|---|
| ImagePullBackOff | Pod status shows ImagePullBackOff | Fix image tag or registry secret, apply updated Deployment |
| CrashLoopBackOff | Pod restarts repeatedly | Check logs, fix application error, push new image |
| Readiness probe failure | Rollout stuck, new pods not Ready | Verify probe endpoint, adjust probe or fix app, rollback if needed |
| Insufficient resources | Pods pending | Scale down or increase resource quotas |
Operations Checklist
Use this checklist before and after each deployment to ensure consistency and safety. Keep it in your repository or wiki and update it as your process evolves.
Pre-deployment checklist
- [ ] Verify cluster and kubectl versions match expected (
kubectl version --short) - [ ] Review Deployment manifest changes in version control (diff against last release)
- [ ] Ensure image is built and pushed with a unique tag (e.g., git SHA, not
latest) - [ ] Confirm resource requests and limits are set for all containers
- [ ] Check that readiness and liveness probes are configured and point to correct endpoints
- [ ] Verify that the CI/CD pipeline has correct cluster credentials (service account token, server URL)
- [ ] Run a dry-run apply to validate YAML syntax:
kubectl apply -f deployment.yaml --dry-run=client -n myapp-test
Post-deployment checklist
- [ ] Run
kubectl rollout statusand confirm success - [ ] Check pod readiness and logs for errors (
kubectl get pods,kubectl logs) - [ ] Monitor application metrics (error rate, latency) for at least 5 minutes after rollout
- [ ] Record the deployment revision and any notable changes (update release notes or change log)
- [ ] If issues arise, execute rollback procedure and document root cause in an incident report
Periodic review
- [ ] Review pipeline security: rotate secrets, enforce least privilege on service accounts
- [ ] Update base images and dependencies to patch vulnerabilities
- [ ] Test rollback in a staging environment quarterly
- [ ] Evaluate pipeline speed and reliability; optimize build cache and parallelism where needed
Conclusion
Automating Kubernetes Deployment CI/CD improves reliability and reduces manual errors. By starting with a narrow pilot, implementing a simple pipeline, and verifying every rollout, you create a foundation for safer releases. When failures occur, having a clear rollback path minimizes downtime.
Adopt the operations checklist to keep your process consistent, and gradually expand automation to cover more services as confidence grows. Remember to iterate: start small, measure success, and refine your pipeline based on observed behavior. With these practices, you can ship faster with confidence, knowing that your Kubernetes deployments are safe, repeatable, and recoverable.
Next steps: implement these patterns in your own cluster, integrate notifications (e.g., Slack alerts) on pipeline failures, and explore progressive delivery tools like Argo Rollouts or Flagger for more advanced deployment strategies.