Introduction
Ansible and Kubernetes are complementary: Kubernetes orchestrates containers at runtime, while Ansible automates the infrastructure around it — cluster provisioning, application deployment, config management, and Day 2 operations. The kubernetes.core collection provides modules to manage any Kubernetes resource directly from Ansible playbooks.
Prerequisites
Install the kubernetes.core collection and Python dependencies:
# Install the collection
ansible-galaxy collection install kubernetes.core
# Install Python dependencies
pip install kubernetes PyYAML jsonpatch
Verify:
ansible-galaxy collection list | grep kubernetes
# kubernetes.core 3.2.0
Authentication
Ansible connects to your cluster using the same kubeconfig as kubectl:
---
- name: K8s tasks using default kubeconfig
hosts: localhost
connection: local
tasks:
- name: Get all pods (uses ~/.kube/config)
kubernetes.core.k8s_info:
kind: Pod
namespace: default
register: pods
Explicit kubeconfig
- name: Use specific kubeconfig
kubernetes.core.k8s:
kubeconfig: /path/to/kubeconfig
state: present
definition: "{{ lookup('file', 'deployment.yml') }}"
# Or via environment variable
- name: Set kubeconfig via env
hosts: localhost
environment:
K8SCONFIG: /path/to/kubeconfig
tasks:
- kubernetes.core.k8s:
state: present
src: deployment.yml
In-cluster Authentication (Running Inside K8s)
- name: Use service account token (in-pod)
kubernetes.core.k8s:
host: "https://kubernetes.default.svc"
api_key: "{{ lookup('file', '/var/run/secrets/kubernetes.io/serviceaccount/token') }}"
validate_certs: false
state: present
definition: "{{ deployment }}"
kubernetes.core.k8s Module
The main module — create, update, and delete any Kubernetes resource:
Create a Namespace
- name: Create namespace
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Namespace
metadata:
name: myapp
labels:
team: backend
env: production
Deploy an Application
---
- name: Deploy web application to Kubernetes
hosts: localhost
connection: local
vars:
app_name: mywebapp
app_namespace: production
app_image: nginx:1.25
app_replicas: 3
tasks:
- name: Create namespace
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Namespace
metadata:
name: "{{ app_namespace }}"
- name: Create ConfigMap
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: ConfigMap
metadata:
name: "{{ app_name }}-config"
namespace: "{{ app_namespace }}"
data:
APP_ENV: production
LOG_LEVEL: info
- name: Deploy application
kubernetes.core.k8s:
state: present
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: "{{ app_name }}"
namespace: "{{ app_namespace }}"
labels:
app: "{{ app_name }}"
spec:
replicas: "{{ app_replicas }}"
selector:
matchLabels:
app: "{{ app_name }}"
template:
metadata:
labels:
app: "{{ app_name }}"
spec:
containers:
- name: "{{ app_name }}"
image: "{{ app_image }}"
ports:
- containerPort: 80
envFrom:
- configMapRef:
name: "{{ app_name }}-config"
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
- name: Create Service
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Service
metadata:
name: "{{ app_name }}"
namespace: "{{ app_namespace }}"
spec:
selector:
app: "{{ app_name }}"
ports:
- port: 80
targetPort: 80
type: ClusterIP
- name: Create Ingress
kubernetes.core.k8s:
state: present
definition:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: "{{ app_name }}"
namespace: "{{ app_namespace }}"
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: "{{ app_name }}.example.com"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: "{{ app_name }}"
port:
number: 80
Load from YAML Files
# From a single file
- name: Apply manifest file
kubernetes.core.k8s:
state: present
src: manifests/deployment.yml
# From a directory (all YAML files)
- name: Apply all manifests
kubernetes.core.k8s:
state: present
src: "{{ item }}"
loop: "{{ lookup('fileglob', 'manifests/*.yml', wantlist=True) }}"
Query Resources with k8s_info
---
- name: Gather Kubernetes information
hosts: localhost
connection: local
tasks:
- name: Get all pods in namespace
kubernetes.core.k8s_info:
kind: Pod
namespace: production
register: pods
- name: Show running pods
ansible.builtin.debug:
msg: "{{ item.metadata.name }} — {{ item.status.phase }}"
loop: "{{ pods.resources }}"
when: item.status.phase == 'Running'
- name: Get specific deployment
kubernetes.core.k8s_info:
api_version: apps/v1
kind: Deployment
name: mywebapp
namespace: production
register: deployment
- name: Show replica count
ansible.builtin.debug:
msg: "Replicas: {{ deployment.resources[0].status.readyReplicas | default(0) }}/{{ deployment.resources[0].spec.replicas }}"
- name: Get nodes
kubernetes.core.k8s_info:
kind: Node
register: nodes
- name: Show node names and versions
ansible.builtin.debug:
msg: "{{ item.metadata.name }} — {{ item.status.nodeInfo.kubeletVersion }}"
loop: "{{ nodes.resources }}"
- name: Find pods by label
kubernetes.core.k8s_info:
kind: Pod
namespace: production
label_selectors:
- app=mywebapp
- tier=frontend
register: labeled_pods
Manage Secrets
- name: Create secret from variables
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: production
type: Opaque
stringData:
DB_HOST: "{{ vault_db_host }}"
DB_USER: "{{ vault_db_user }}"
DB_PASS: "{{ vault_db_pass }}"
- name: Create TLS secret
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Secret
metadata:
name: tls-cert
namespace: production
type: kubernetes.io/tls
data:
tls.crt: "{{ lookup('file', 'certs/tls.crt') | b64encode }}"
tls.key: "{{ lookup('file', 'certs/tls.key') | b64encode }}"
Helm Charts with Ansible
---
- name: Deploy with Helm
hosts: localhost
connection: local
tasks:
- name: Add Helm repository
kubernetes.core.helm_repository:
name: bitnami
repo_url: https://charts.bitnami.com/bitnami
- name: Deploy PostgreSQL via Helm
kubernetes.core.helm:
name: my-postgres
namespace: databases
create_namespace: true
chart_ref: bitnami/postgresql
chart_version: "15.5.0"
values:
auth:
postgresPassword: "{{ vault_pg_password }}"
database: myapp
primary:
persistence:
size: 20Gi
metrics:
enabled: true
- name: Upgrade with new values
kubernetes.core.helm:
name: my-postgres
namespace: databases
chart_ref: bitnami/postgresql
values:
primary:
resources:
requests:
cpu: "500m"
memory: "512Mi"
- name: Uninstall chart
kubernetes.core.helm:
name: old-release
namespace: default
state: absent
Wait for Resources
- name: Deploy and wait for readiness
kubernetes.core.k8s:
state: present
definition: "{{ lookup('file', 'deployment.yml') }}"
wait: true
wait_timeout: 300
wait_condition:
type: Available
status: "True"
- name: Wait for pod to be running
kubernetes.core.k8s_info:
kind: Pod
namespace: production
label_selectors:
- app=mywebapp
wait: true
wait_timeout: 120
wait_condition:
type: Ready
status: "True"
Rolling Updates
---
- name: Rolling update deployment
hosts: localhost
connection: local
vars:
new_image: mywebapp:v2.1.0
tasks:
- name: Update deployment image
kubernetes.core.k8s:
state: present
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mywebapp
namespace: production
spec:
template:
spec:
containers:
- name: mywebapp
image: "{{ new_image }}"
wait: true
wait_timeout: 300
- name: Verify rollout
kubernetes.core.k8s_info:
api_version: apps/v1
kind: Deployment
name: mywebapp
namespace: production
register: deploy
- name: Check all replicas ready
ansible.builtin.assert:
that:
- deploy.resources[0].status.readyReplicas == deploy.resources[0].spec.replicas
fail_msg: "Rolling update incomplete — {{ deploy.resources[0].status.readyReplicas | default(0) }}/{{ deploy.resources[0].spec.replicas }} ready"
Delete Resources
- name: Delete deployment
kubernetes.core.k8s:
state: absent
api_version: apps/v1
kind: Deployment
name: mywebapp
namespace: production
- name: Delete namespace (and everything in it)
kubernetes.core.k8s:
state: absent
definition:
apiVersion: v1
kind: Namespace
metadata:
name: staging
Ansible vs kubectl vs Helm
| Feature | Ansible + k8s | kubectl | Helm |
|---|---|---|---|
| Idempotent | ✅ Built-in | ❌ apply only | ✅ |
| Templating | ✅ Jinja2 + vars | ❌ Basic | ✅ Go templates |
| Multi-cluster | ✅ Easy with vars | Manual context switch | Manual |
| Non-K8s resources | ✅ Full Ansible | ❌ K8s only | ❌ K8s only |
| Secrets management | ✅ Ansible Vault | ❌ Plain YAML | ❌ Values files |
| Rollback | Manual | ✅ rollout undo | ✅ helm rollback |
When to use Ansible for K8s:
- You already manage infra with Ansible
- You need to combine K8s + non-K8s tasks (DNS, LB, DB)
- You want Ansible Vault for secrets
- Multi-cluster deployments with environment-specific vars
Troubleshooting
"Failed to import kubernetes Python library"
pip install kubernetes PyYAML jsonpatch
# Or for a specific Python
pip3 install kubernetes
"Connection refused" or "Unable to connect to server"
Your kubeconfig isn't set or the cluster is unreachable:
# Verify kubectl works first
kubectl cluster-info
# Check kubeconfig location
echo $KUBECONFIG
ls ~/.kube/config
"Forbidden: User cannot create resource"
RBAC permissions issue. Check your service account or user has the right ClusterRole:
# Create a ClusterRoleBinding for your service account
- kubernetes.core.k8s:
state: present
definition:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: ansible-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: ansible
namespace: default
Related Articles
- Ansible Kubernetes Ops — Enterprise Guide
- Ansible on AWS
- Ansible VMware vSphere
- Ansible Terraform Integration
- Ansible Best Practices
Conclusion
The kubernetes.core collection makes Ansible a powerful K8s management tool. Use k8s for creating/updating any resource, k8s_info for querying, and helm for chart-based deployments. The biggest advantage over raw kubectl: you can combine K8s operations with non-K8s tasks (DNS, load balancers, monitoring) in a single playbook, use Ansible Vault for secrets, and manage multiple clusters with environment-specific variables.
Further reading
To go deeper, Ansible for Kubernetes by Example expands on these patterns in production.