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

RequirementMinimum
Kubernetes cluster1.24+
kubectlMatching cluster version
RAM4 GB available
CPU2 cores available
Storage20 GB persistent volume
DNS/IngressFor 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

FeatureAWXSemaphoreAutomation Controller
Install complexityMedium (K8s)Low (Docker)High (installer)
Resources~4 GB RAM~50 MB RAM~8 GB RAM
RBACFullBasicFull + orgs
WorkflowsVisual builderNoVisual builder
EE supportYesNoYes
APIRESTRESTREST
ClusteringYes (K8s)NoYes (Mesh)
SupportCommunityCommunityRed Hat

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 reading: running Ansible against Kubernetes clusters covers this in real-world detail.