Ansible for Kubernetes: Automate Cluster Deployments Without the Pain
Ansible for Kubernetes: automate cluster provisioning, app deployments, and config management.
20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.
- ✓A Kubernetes cluster (local or cloud). kubectl configured and working. Ansible 2.10+ installed.
Use Ansible's k8s module to declaratively manage Kubernetes resources. Write playbooks that apply YAML manifests, handle idempotency, and integrate with dynamic inventories. Avoid shelling out to kubectl — use the native module for reliability.
Imagine you're a restaurant manager who needs to set up 50 tables exactly the same way every morning. Instead of walking to each table and placing napkins, silverware, and menus by hand, you write a checklist once and hand it to a team of assistants who each set up a table identically. Ansible is that checklist system — it ensures every Kubernetes cluster gets the same config, every time, without you touching each server.
| Chrome | Firefox | Safari | Edge |
|---|---|---|---|
| ✓ | ✓ | ✓ | ✓ |
You've been burned by shell scripts that work on Monday and fail on Tuesday because someone's kubectl version is different. Or you've watched a 200-line bash script silently skip a step when a pod name changed. That's where Ansible for Kubernetes comes in — it's not just automation, it's idempotent, declarative, and testable. This article gives you battle-tested patterns to provision clusters, deploy apps, and manage configs without the fragility of ad-hoc scripts. By the end, you'll write playbooks that survive a 3am pager and a junior engineer's first commit.
Why Ansible for Kubernetes Beats Shell Scripts
Shell scripts with kubectl are the duct tape of DevOps. They work until they don't — a typo in a grep, a missing pipe, a race condition when two scripts run simultaneously. Ansible gives you idempotency out of the box: run the same playbook ten times, get the same result. The k8s module communicates directly with the Kubernetes API, so you skip the kubectl binary dependency and get proper error handling. Plus, you can integrate with dynamic inventories from cloud providers, manage secrets via Ansible Vault, and reuse roles across environments. The problem it solves is simple: repeatable, auditable, and debuggable cluster management.
wait: yes and wait_timeout to block until the deployment rolls out. Without it, the task returns as soon as the API accepts the definition — not when pods are ready.Dynamic Inventories: Don't Hardcode Cluster Endpoints
Hardcoding cluster IPs in your inventory is a disaster waiting to happen — clusters get recycled, IPs change, and you end up deploying to the wrong environment. Use dynamic inventory scripts or plugins to source cluster info from your cloud provider or a CMDB. For Kubernetes, the simplest approach is to use the kubeconfig file on the Ansible controller, but that ties you to a single cluster. Instead, use the kubernetes.core.k8s_cluster_info module to discover clusters, or pass the kubeconfig path as a variable per environment. The why: your playbooks become environment-agnostic and safe to run in CI/CD.
Idempotency and State Management: The k8s Module Under the Hood
The k8s module is idempotent by design — it compares the desired state (your definition) with the current state on the cluster and applies only the diff. Under the hood, it uses the Kubernetes Python client to perform a strategic merge patch or a JSON patch, depending on the resource type. This means you can run the same playbook repeatedly without side effects. However, there's a gotcha: the module doesn't track fields it doesn't know about. If you omit a field that the cluster already has, the module won't remove it — it only adds or updates fields you specify. To enforce exact state, set state: patched with definition that includes all fields you want, or use state: present with apply: yes which does a full replace.
apply: yes.Managing Secrets: Ansible Vault vs. External Secrets Operators
Never put plaintext secrets in your playbooks or inventory. Ansible Vault encrypts variables and files at rest, but decryption happens on the controller — meaning the secret is in memory during execution. For Kubernetes, the better pattern is to use an external secrets operator (e.g., External Secrets Operator, Sealed Secrets) and let Ansible only manage the reference. This way, the actual secret never touches your playbook. If you must use Vault, encrypt the entire variable file and use include_vars with the vault password file. The gotcha: vault-encrypted files can't be diffed in code review — use a secrets manager instead.
k8s module with definition containing stringData with plaintext secrets. Anyone with access to the playbook can read them. Always use external secret stores.Rolling Updates and Canary Deployments with Ansible
Ansible can orchestrate complex rollout strategies by combining the k8s module with loops and conditionals. For a canary deployment, you create two deployments (stable and canary) and a service that routes a percentage of traffic to the canary. Use Ansible to gradually increase the canary replicas and monitor error rates before promoting. The key is to use wait with wait_condition to check pod readiness. Without Ansible, you'd manually run kubectl commands and watch — with Ansible, it's a single playbook run.
wait_condition to check for specific pod statuses. For canary, wait for condition=Ready, status=True before increasing traffic.Error Handling and Rollback Strategies
When a deployment fails, you need to roll back fast. Ansible's block and rescue let you catch failures and execute rollback tasks. For example, if the canary deployment doesn't become ready, rescue can delete the canary and restore the original service selector. The gotcha: the k8s module doesn't automatically roll back on failure — you must implement it yourself. Also, use ignore_errors: yes sparingly; it's better to use failed_when with specific conditions.
When Not to Use Ansible for Kubernetes
Ansible is overkill for simple, one-off deployments — use kubectl directly. It's also not ideal for real-time cluster autoscaling or event-driven operations; use a Kubernetes operator or a controller instead. If your team is already invested in Helm, Ansible can still manage Helm releases via the helm module, but writing raw YAML in Ansible may duplicate effort. Finally, avoid Ansible for day-2 operations like pod eviction or node draining — those are better handled by Kubernetes-native tools or scripts.
The 4GB Container That Kept Dying
k8s module with definition from a Jinja2 template. The template had a typo: resources.limits.memory was set to 512Mi but resources.requests.memory was missing, so Kubernetes set requests equal to limits. However, the Go app had a goroutine that spawned a subprocess reading large files — that subprocess wasn't accounted for in cgroup limits, so it consumed host memory until OOM.resources.requests.memory: 256Mi explicitly and set memory_swap: 0 in the container spec. Also added securityContext.runAsNonRoot: true to prevent subprocess privilege escalation.- Always set both requests and limits explicitly.
- Never assume a missing request defaults to zero — it defaults to the limit.
state: present instead of state: absent then present. 3. Ensure apiVersion and kind match exactly.kubectl logs <pod>. 2. Verify resource requests/limits. 3. Increase wait_timeout. 4. Check node capacity.definition as a dict, not a string. 4. Run kubectl apply --dry-run=client to test.pip show kubernetespip install kubernetespip install kubernetes| File | Command / Code | Purpose |
|---|---|---|
| deploy-webapp.yml | - name: Deploy web application to Kubernetes | Why Ansible for Kubernetes Beats Shell Scripts |
| dynamic-inventory.yml | def get_clusters(): | Dynamic Inventories |
| idempotent-deploy.yml | - name: Idempotent deployment with exact state | Idempotency and State Management |
| secrets-management.yml | - name: Deploy with external secrets | Managing Secrets |
| canary-deploy.yml | - name: Canary deployment with gradual traffic shift | Rolling Updates and Canary Deployments with Ansible |
| rollback.yml | - name: Deploy with automatic rollback | Error Handling and Rollback Strategies |
Key takeaways
pip install kubernetes.Common mistakes to avoid
3 patternsNot installing Python Kubernetes client
kubernetes.core collection requires pip install kubernetes. Without it, all k8s module calls fail with import errors.Using shell/command for kubectl calls
k8s module instead of kubectl via shell. The k8s module is idempotent and returns structured results.Storing Kubernetes secrets unencrypted in playbooks
Interview Questions on This Topic
How does Ansible's k8s module handle idempotency under the hood?
Frequently Asked Questions
20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.
That's Ansible. Mark it forged?
3 min read · try the examples if you haven't