Deploying AWX on kubernetes
After running Ansible playbooks directly from my workstation for a while, I decided it was time to get a proper AWX instance running in my homelab k3s cluster. This post covers the full journey — from Flux manifests to a custom Execution Environment with Cisco collections — including every gotcha I hit along the way.
The Stack
- k3s multi-node cluster (aarch64 NUC nodes)
- Flux v2.8.1 for GitOps
- Cilium CNI with Gateway API
- cert-manager for TLS
- Forgejo self-hosted git and container registry
- AWX 24.6.1 via the awx-operator Helm chart
Repository Structure
I use a base/overlay pattern in my GitOps repo. AWX lives under apps/:
Base Manifests
Namespace
HelmRepository
Gotcha #1: The correct Helm repository URL is
https://ansible-community.github.io/awx-operator-helm(theansible-communityGitHub org), nothttps://ansible.github.io/awx-operator/. The old URL no longer works.
HelmRelease
Gotcha #2: The chart version (
3.2.1) is not the same as the AWX operator version (2.19.1). The Helm chart and the operator have separate versioning. Always check the chart releases for the correct chart version.
Kustomization
Note the AWX CR (awx.yaml) is not in the base — it lives in the cluster overlay. This is intentional to avoid a race condition where Helm tries to create the AWX custom resource before the CRD is fully registered on first deploy.
Cluster Overlay (athena)
AWX Instance
TLS Certificate
Gateway and HTTPRoute
Since my cluster uses Cilium with the Gateway API instead of a traditional ingress controller, I expose AWX with a Gateway and HTTPRoute instead of an Ingress resource:
Overlay Kustomization
Deploying and Watching the Rollout
Commit everything, push, and watch Flux do its thing:
The startup sequence is:
awx-postgres-15-0starts firstawx-migration-*job runs DB migrationsawx-webcomes upawx-taskwaits inInit:0/3until migrations finish — this is normal, don’t panic
Once everything is Running, grab the admin password:
Custom Execution Environment with Cisco Collections
The default awx-ee image doesn’t include Cisco collections or ansible-pylibssh. Running a Cisco playbook out of the box gives you:
The fix is to build a custom Execution Environment.
The Build Files
execution-environment.yml — this is the key file, and it took several attempts to get right:
Gotcha #3: Use
quay.io/ansible/creator-eeas the base, notquay.io/ansible/awx-ee. Theawx-eebase triggers acheck_ansiblefailure in EE version 3 builds.creator-eeis the correct base for building custom EEs.
Gotcha #4:
creator-eeusesmicrodnf, notdnf. Withoutpackage_manager_path: /usr/bin/microdnf, system dependencies silently fail to install.
requirements.yml:
requirements.txt:
bindep.txt:
Building the Image
Since I’m on Apple Silicon (arm64) but my k3s nodes are amd64, I can’t use ansible-builder build directly — it doesn’t support --platform. The trick is to use ansible-builder create to generate the context, then hand off to docker buildx:
Gotcha #5:
ansible-buildergenerates aContainerfile, not aDockerfile. Pass-f context/Containerfileexplicitly todocker buildx.
Gotcha #6: If you just
docker pushafteransible-builder build, you’ll hitexec format errorin AWX because the image is arm64 but the nodes are amd64. Always build with--platform linux/amd64for amd64 clusters.
Registering the EE in AWX
-
Add a Container Registry credential (
Credentials → Add → Container Registry) pointing toforgejo.uclab.devwith your Forgejo username and password/token. -
Add the Execution Environment (
Administration → Execution Environments → Add):
| Field | Value |
|---|---|
| Name | awx-ee-cisco |
| Image | forgejo.uclab.dev/affragak/awx-ee-cisco:latest |
| Pull | Always |
| Registry Credential | your Forgejo credential |
- Assign to your Job Template (
Templates → Edit → Execution Environment → awx-ee-cisco).
Source Control with Forgejo SSH
For AWX to clone your playbook repos from Forgejo, create a Source Control credential with SSH:
In Forgejo, add awx_forgejo.pub as a Deploy Key on the repo (Repo → Settings → Deploy Keys).
In AWX (Credentials → Add → Source Control):
- SCM Private Key: contents of
~/.ssh/awx_forgejo - SCM Host Key: output from
ssh-keyscan
Use the SSH URL format in your AWX Project: [email protected]:youruser/yourrepo.git
Gotcha Summary
| # | Problem | Fix |
|---|---|---|
| 1 | Wrong Helm repo URL | Use ansible-community.github.io/awx-operator-helm |
| 2 | Chart version ≠ operator version | Check chart releases separately |
| 3 | check_ansible failure in EE build |
Use creator-ee as base, not awx-ee |
| 4 | System deps fail silently | Add package_manager_path: /usr/bin/microdnf |
| 5 | docker buildx can’t find Dockerfile |
Use -f context/Containerfile |
| 6 | exec format error in AWX |
Build with --platform linux/amd64 on Apple Silicon |
| 7 | awx-task stuck in Init:0/3 |
Normal — it waits for DB migrations to finish |
| 8 | AWX CR not created on first deploy | Keep AWX CR in overlay, separate from HelmRelease |
AWX is now fully running at https://awx.uclab.dev, managing Cisco IOS, NX-OS and IOS-XR playbooks across my lab network. The custom EE approach is the right long-term solution — collections and dependencies are baked in, no re-downloading on every job run.
