AWX is the open source upstream of Red Hat Ansible Automation Platform (formerly Ansible Tower). It gives Ansible a web interface, a REST API, role-based access control, stored credentials and a history of every job run. AWX is installed on Kubernetes through the AWX Operator. In this tutorial you will set up a single-node K3s cluster on Ubuntu 24.04, deploy AWX with the operator, and run your first playbook from the web interface.

Prerequisites

To follow this tutorial, you need:

  • A server running Ubuntu 24.04 LTS with at least 4 vCPUs, 8 GB of RAM and 40 GB of disk, for example a CubePath VPS. AWX runs several containers plus PostgreSQL, and smaller servers leave pods stuck in Pending.
  • A non-root user with sudo privileges.
  • Git installed (sudo apt install git), used by kubectl to fetch the operator manifests.
  • An SSH client on your workstation, used to reach the AWX interface through a tunnel.
  • Optionally, another server that AWX will manage, reachable over SSH with a key.

Step 1 - Installing K3s

K3s is a lightweight, certified Kubernetes distribution that installs as a single binary, which makes it a good fit for a one-server AWX. Its official installer is a script; download it and review it before running it:

curl -sfL https://get.k3s.io -o k3s-install.sh
less k3s-install.sh

Run the installer. It installs K3s as a systemd service and creates a kubectl command:

sudo sh k3s-install.sh

The cluster configuration is readable only by root. Copy it to your user so you can run kubectl without sudo:

mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown "$USER":"$USER" ~/.kube/config
chmod 600 ~/.kube/config
echo 'export KUBECONFIG=$HOME/.kube/config' >> ~/.bashrc
source ~/.bashrc

Check that the node is ready:

kubectl get nodes
NAME    STATUS   ROLES                  AGE   VERSION
awx01   Ready    control-plane,master   45s   v1.33.4+k3s1

K3s includes a local storage provisioner, so the PostgreSQL volume that AWX requests is created automatically on the server's disk.

Step 2 - Deploying the AWX Operator

The operator is deployed with Kustomize, which is built into kubectl. Look up the latest release on the AWX Operator releases page and use that tag in the two places below. This guide uses 2.19.1 as an example.

Create a working directory and the Kustomize file:

mkdir -p ~/awx
cd ~/awx
nano kustomization.yaml
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

Apply it. Kustomize downloads the manifests from GitHub and creates the awx namespace, the custom resource definitions and the operator deployment:

kubectl apply -k .

Wait until the operator pod is running:

kubectl get pods -n awx
NAME                                               READY   STATUS    RESTARTS   AGE
awx-operator-controller-manager-6c58d8b4f7-kq2xz   2/2     Running   0          60s

Step 3 - Creating the AWX instance

The operator watches for resources of kind AWX and builds everything each one needs: the web and task deployments, a PostgreSQL database and the services. Describe your instance in a file:

nano awx.yaml
apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
  name: awx
spec:
  service_type: ClusterIP

ClusterIP keeps the web interface reachable only from inside the server. You will open it through an SSH tunnel in the next step, so the admin login is never exposed to the Internet over plain HTTP.

Add the file to the resources in kustomization.yaml:

resources:
  - github.com/ansible/awx-operator/config/default?ref=2.19.1
  - awx.yaml

Apply the configuration again:

kubectl apply -k .

The deployment takes several minutes, because the operator pulls the images, starts PostgreSQL and runs the database migrations. Follow its progress in the operator log:

kubectl logs -f deployment/awx-operator-controller-manager -c awx-manager -n awx

When the log shows a play recap with failed=0, press Ctrl+C and list the pods:

kubectl get pods -n awx
NAME                                               READY   STATUS      RESTARTS   AGE
awx-migration-24.6.1-7mqzf                         0/1     Completed   0          4m
awx-operator-controller-manager-6c58d8b4f7-kq2xz   2/2     Running     0          9m
awx-postgres-15-0                                  1/1     Running     0          5m
awx-task-7b9d8f6c5d-l4xjp                          4/4     Running     0          4m
awx-web-5f8c9b7d6-9wz2m                            3/3     Running     0          4m

All pods must be Running, except the migration job, which ends as Completed.

Step 4 - Logging in to the web interface

The operator generates the password for the admin user and stores it in a Kubernetes secret named after your instance:

kubectl get secret awx-admin-password -n awx -o jsonpath='{.data.password}' | base64 --decode; echo

Copy the password. Then forward a local port on the server to the AWX web service:

kubectl port-forward service/awx-service -n awx 8080:80

Leave that command running. On your workstation, open an SSH tunnel to that port in another terminal:

ssh -L 8080:localhost:8080 your_user@your_server_ip

Browse to http://localhost:8080 on your workstation and log in as admin with the password from the secret. The AWX dashboard opens, showing no hosts or jobs yet.

Step 5 - Adding a machine credential

AWX stores secrets as credentials and injects them into jobs, so playbooks never contain passwords or keys. To manage a server over SSH, create a Machine credential:

  1. Go to Resources > Credentials and click Add.
  2. Name it Web servers SSH and choose the credential type Machine.
  3. Enter the remote Username (for example your_user) and paste the private key in SSH Private Key.
  4. If the user needs a password for sudo, set Privilege Escalation Method to sudo and fill in Privilege Escalation Password.
  5. Click Save.

The key is encrypted in the AWX database and is never shown again in the interface.

Step 6 - Creating an inventory

An inventory lists the hosts AWX can target:

  1. Go to Resources > Inventories, click Add and choose Add inventory.
  2. Name it Web servers, keep the Default organization and click Save.
  3. Open the Hosts tab, click Add, enter your managed server's IP address or hostname as the name, and save.

If the host's SSH port or address differs from its name, add it as a variable in the host's Variables field, for example ansible_host: your_server_ip.

Step 7 - Creating a project from Git

A project points AWX to a Git repository that contains playbooks. AWX clones it and updates it before each job if you choose so. For a first test, use the public sample repository published by the Ansible project:

  1. Go to Resources > Projects and click Add.
  2. Name it Samples, choose Source Control Type Git.
  3. Set Source Control URL to https://github.com/ansible/ansible-tower-samples.
  4. Enable Update Revision on Launch and click Save.

AWX syncs the project immediately. The status icon next to the project turns green when the clone succeeds. For a private repository, create a Source Control credential with a deploy key or token and select it in the project.

Step 8 - Running a job template

A job template combines a project, a playbook, an inventory and credentials into something anyone with permission can launch:

  1. Go to Resources > Templates, click Add and choose Add job template.
  2. Name it Hello world.
  3. Select the inventory Web servers, the project Samples and the playbook hello_world.yml.
  4. Under Credentials, select Web servers SSH.
  5. Click Save, then Launch.

AWX opens the job output, which streams the Ansible run live:

PLAY [Hello World Sample] ******************************************************

TASK [Gathering Facts] *********************************************************
ok: [203.0.113.20]

TASK [Hello Message] ***********************************************************
ok: [203.0.113.20] => {
    "msg": "Hello World!"
}

PLAY RECAP *********************************************************************
203.0.113.20               : ok=2    changed=0    unreachable=0    failed=0    skipped=0

A job status of Successful confirms that AWX reached the server with the stored credential. Every run stays in Views > Jobs with its full output, who launched it and when.

Step 9 - Giving access to other users

Avoid sharing the admin account. AWX permissions are granted on objects (templates, inventories, projects, credentials) to users or teams:

  1. Go to Access > Users and create a user for each person.
  2. Go to Access > Teams, create a team such as Operators, and add the users to it.
  3. Open the Hello world template, go to its Access tab (or User and Team Access in newer versions) and grant the team the Execute role.

Members of the team can now launch that template and read its output, but cannot see the SSH key or change the playbook, inventory or credentials.

Troubleshooting

  • Pods stay in Pending: the server does not have enough CPU or memory. Check the reason with kubectl describe pod <pod_name> -n awx and resize the server.
  • kubectl apply -k . fails to fetch the operator: Git is missing or the server cannot reach GitHub. Install Git and check outbound HTTPS.
  • The project sync fails: open the sync job under Views > Jobs to see the Git error; private repositories need a Source Control credential.
  • Jobs fail with UNREACHABLE: the managed server does not accept the key or blocks SSH from the AWX server. Test with ssh -i key your_user@host from the AWX server itself.
  • The operator log shows errors after an upgrade: operator releases must be applied in order. Read the release notes on GitHub before changing the tag in kustomization.yaml.

Conclusion

You installed K3s and the AWX Operator on Ubuntu 24.04, deployed an AWX instance, and ran a playbook from a Git project with a stored credential, inventory and job template. Next, point a project to your own playbook repository, schedule recurring jobs from the template's Schedules tab, and back up the instance regularly with the operator's AWXBackup resource before upgrades.