Introduction
AWX is the open-source upstream project for Red Hat Ansible Automation Controller (formerly Ansible Tower). It provides a web UI, REST API, RBAC, job scheduling, and workflow orchestration for Ansible automation. Since AWX 18+, the only supported installation method is Kubernetes via the AWX Operator. This guide covers the complete installation on Kubernetes, Minikube, and K3s.
Prerequisites
| Requirement | Minimum |
|---|---|
| Kubernetes cluster | 1.24+ |
| kubectl | Matching cluster version |
| RAM | 4 GB available |
| CPU | 2 cores available |
| Storage | 20 GB persistent volume |
| DNS/Ingress | For external access |
Option 1: Install on Minikube (Development)
Start Minikube
# Start with enough resources
minikube start --cpus=4 --memory=6g --addons=ingress
# Verify
kubectl get nodes
Deploy AWX Operator
# Install the operator using kustomize
cat <<EOF > kustomization.yml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- github.com/ansible/awx-operator/config/default?ref=2.19.1
images:
- name: quay.io/ansible/awx-operator
newTag: 2.19.1
namespace: awx
EOF
# Create namespace and apply
kubectl create namespace awx
kubectl apply -k .
# Wait for operator to be ready
kubectl -n awx wait --for=condition=Available deployment/awx-operator-controller-manager --timeout=300s
Create AWX Instance
# awx.yml
apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
name: awx
namespace: awx
spec:
service_type: NodePort
ingress_type: none
postgres_storage_class: standard
projects_storage_class: standard
projects_storage_size: 8Gi
kubectl apply -f awx.yml -n awx
# Watch the deployment (takes 5-10 minutes)
kubectl -n awx get pods -w
Get Admin Password
kubectl -n awx get secret awx-admin-password -o jsonpath='{.data.password}' | base64 -d
echo
Access AWX
# Minikube
minikube service awx-service -n awx --url
# Opens browser to AWX login page
Option 2: Install on K3s (Production-Lite)
# Install K3s
curl -sfL https://get.k3s.io | sh -
# Copy kubeconfig
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config
# Deploy AWX Operator (same kustomization as above)
kubectl create namespace awx
kubectl apply -k .
# Wait for operator
kubectl -n awx wait --for=condition=Available deployment/awx-operator-controller-manager --timeout=300s
# Create AWX with Ingress
cat <<EOF | kubectl apply -f -
apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
name: awx
namespace: awx
spec:
service_type: ClusterIP
ingress_type: ingress
ingress_hosts:
- hostname: awx.example.com
ingress_tls_secret: awx-tls
postgres_storage_class: local-path
projects_storage_class: local-path
EOF
Option 3: Install on Production Kubernetes
With Helm (Alternative Method)
# Add the AWX Operator Helm chart
helm repo add awx-operator https://ansible.github.io/awx-operator/
helm repo update
# Install operator
helm install awx-operator awx-operator/awx-operator \
-n awx --create-namespace \
--set AWX.enabled=true \
--set AWX.name=awx \
--set AWX.spec.service_type=ClusterIP \
--set AWX.spec.ingress_type=ingress \
--set AWX.spec.ingress_hosts[0].hostname=awx.example.com
AWX Custom Resource (Full Configuration)
apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
name: awx
namespace: awx
spec:
# Replicas
replicas: 2
# Service
service_type: ClusterIP
# Ingress
ingress_type: ingress
ingress_hosts:
- hostname: awx.example.com
ingress_tls_secret: awx-tls
ingress_annotations: |
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
# PostgreSQL
postgres_storage_class: gp3
postgres_storage_size: 20Gi
postgres_resource_requirements:
requests:
cpu: 500m
memory: 2Gi
limits:
cpu: "2"
memory: 4Gi
# Web
web_resource_requirements:
requests:
cpu: 250m
memory: 1Gi
limits:
cpu: "1"
memory: 2Gi
# Task
task_resource_requirements:
requests:
cpu: 500m
memory: 2Gi
limits:
cpu: "2"
memory: 4Gi
# EE
ee_resource_requirements:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: "1"
memory: 2Gi
# Projects
projects_storage_class: gp3
projects_storage_size: 20Gi
projects_persistence: true
# Extra settings
extra_settings:
- setting: REMOTE_HOST_HEADERS
value: "['HTTP_X_FORWARDED_FOR']"
Post-Installation Setup
Create Organization
# Using awx CLI
pip install awxkit
awx login --conf.host https://awx.example.com --conf.username admin --conf.password $(kubectl -n awx get secret awx-admin-password -o jsonpath='{.data.password}' | base64 -d)
awx organizations create --name "My Org" --description "Main organization"
Add Credentials
# Machine credential (SSH)
awx credentials create \
--name "SSH Key" \
--credential_type "Machine" \
--organization "My Org" \
--inputs '{"ssh_key_data": "'"$(cat ~/.ssh/id_ed25519)"'"}'
# Source control credential
awx credentials create \
--name "GitHub" \
--credential_type "Source Control" \
--organization "My Org" \
--inputs '{"ssh_key_data": "'"$(cat ~/.ssh/github_deploy)"'"}'
Add Project
awx projects create \
--name "Infrastructure" \
--organization "My Org" \
--scm_type git \
--scm_url "git@github.com:myorg/ansible-playbooks.git" \
--scm_branch main \
--credential "GitHub"
Add Inventory and Job Template
# Inventory
awx inventory create --name "Production" --organization "My Org"
awx hosts create --name "web01.example.com" --inventory "Production"
# Job Template
awx job_templates create \
--name "Deploy Web App" \
--project "Infrastructure" \
--inventory "Production" \
--playbook "deploy.yml" \
--credential "SSH Key"
# Launch
awx job_templates launch "Deploy Web App"
Upgrade AWX
# Update the operator version in kustomization.yml
cat <<EOF > kustomization.yml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- github.com/ansible/awx-operator/config/default?ref=2.20.0
images:
- name: quay.io/ansible/awx-operator
newTag: 2.20.0
namespace: awx
EOF
# Apply — operator handles the rolling upgrade
kubectl apply -k .
Backup and Restore
# Backup
apiVersion: awx.ansible.com/v1beta1
kind: AWXBackup
metadata:
name: awx-backup-2026-04-25
namespace: awx
spec:
deployment_name: awx
backup_storage_class: gp3
# Restore
apiVersion: awx.ansible.com/v1beta1
kind: AWXRestore
metadata:
name: awx-restore
namespace: awx
spec:
deployment_name: awx
backup_name: awx-backup-2026-04-25
Troubleshooting
Pods Stuck in Pending
# Check events
kubectl -n awx describe pod <pod-name>
# Usually: insufficient resources or missing storage class
Database Migration Fails
# Check migration pod logs
kubectl -n awx logs -l app.kubernetes.io/component=migration
Reset Admin Password
kubectl -n awx exec -it deployment/awx-task -- awx-manage changepassword admin
Check Operator Logs
kubectl -n awx logs deployment/awx-operator-controller-manager -c manager -f
AWX vs Semaphore vs Automation Controller
| Feature | AWX | Semaphore | Automation Controller |
|---|---|---|---|
| Install complexity | Medium (K8s) | Low (Docker) | High (installer) |
| Resources | ~4 GB RAM | ~50 MB RAM | ~8 GB RAM |
| RBAC | Full | Basic | Full + orgs |
| Workflows | Visual builder | No | Visual builder |
| EE support | Yes | No | Yes |
| API | REST | REST | REST |
| Clustering | Yes (K8s) | No | Yes (Mesh) |
| Support | Community | Community | Red Hat |
Related Articles
- Ansible Automation Platform 2.6 Architecture
- Ansible Semaphore Guide
- Ansible Execution Environments
- Ansible Kubernetes Automation
Conclusion
AWX is the open-source Ansible automation platform, deployed on Kubernetes via the AWX Operator. Use Minikube for development, K3s for small production, and a full Kubernetes cluster for enterprise scale. The AWX Operator handles installation, upgrades, backups, and restores through Kubernetes custom resources. For teams that need a web UI, API, RBAC, and scheduling without a Red Hat subscription, AWX is the standard choice.
Related guide
Related reading: running Ansible against Kubernetes clusters covers this in real-world detail.