AWX (Ansible Tower)

DevOps & Automation rootless 4 containers
awx-web quay.io/ansible/awx:latest · 8052:8052
awx-task quay.io/ansible/awx:latest
postgres docker.io/postgres:15
redis docker.io/redis:7-alpine

Deployment rootless

1. Prepare the host

Operating System:
!

Prerequisites once as root

Install Podman (needs podman 4.3 or newer; Ubuntu 22.04 ships 3.4 and is too old, use 24.04)

apt update && apt install -y podman

Create user (if not existing)

useradd -m -s /bin/bash awx
passwd awx

Enable linger (services keep running after logout and start after reboot)

loginctl enable-linger awx
↪

Switch to deployment user

su - awx

Use su - with the dash — this creates a proper login shell as awx. Without the dash, root environment variables carry over and rootless Podman will not work correctly.

!

Fix environment for rootless systemd (new users only — skip if already set)

Required once so systemctl --user works in non-login sessions.

cat << 'EOF' >> ~/.bashrc

# Fix for systemd user services (rootless Podman/systemctl)
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export DBUS_SESSION_BUS_ADDRESS=unix:path=$XDG_RUNTIME_DIR/bus
EOF
source ~/.bashrc

2. Choose a format and deploy

The same pod in four forms. Pick one and follow only its steps.

Each tab says which podman it needs. Check yours with podman --version: Debian 12 ships 4.3, Ubuntu 24.04 4.9, Debian 13 5.4, Fedora 5.8, Arch 6.x.

Kubernetes YAML for podman kube play, started by systemd through a Quadlet .kube unit (podman 4.4+; older podman like Debian 12 uses podman-kube@, see the version check below).

Kubernetes YAML

apiVersion: v1
kind: Pod
metadata:
  name: awx
  labels:
    app: awx
spec:
  restartPolicy: Always
  containers:
  - name: awx-web
    image: quay.io/ansible/awx:latest
    imagePullPolicy: Always
    ports:
    - containerPort: 8052
      hostPort: 8052
    env:
    - name: DATABASE_USER
      value: awx
    - name: DATABASE_PASSWORD
      value: Bb3GfGNhP_tp2j7MeCUDUw
    - name: DATABASE_NAME
      value: awx
    - name: DATABASE_HOST
      value: 127.0.0.1
    - name: DATABASE_PORT
      value: '5432'
    - name: REDIS_HOST
      value: 127.0.0.1
    - name: REDIS_PORT
      value: '6379'
    - name: SECRET_KEY
      value: sH-5EVaeQo7PL4TV7xoudA
    - name: AWX_ADMIN_USER
      value: admin
    - name: AWX_ADMIN_PASSWORD
      value: Bb3GfGNhP_tp2j7MeCUDUw
    volumeMounts:
    - name: vol-0
      mountPath: /etc/tower
    - name: vol-1
      mountPath: /etc/nginx
    - name: vol-2
      mountPath: /var/lib/awx/projects
    command:
    - /bin/sh
    args:
    - -c
    - until python3 -c "import socket;s=socket.create_connection(('${DATABASE_HOST:-127.0.0.1}',${DATABASE_PORT:-5432}),1);s.close()"
      2>/dev/null; do echo 'Waiting for postgres...'; sleep 3; done && awx-manage
      migrate && DJANGO_SUPERUSER_PASSWORD=${AWX_ADMIN_PASSWORD:-Bb3GfGNhP_tp2j7MeCUDUw}
      awx-manage createsuperuser --noinput --username ${AWX_ADMIN_USER:-admin} --email
      admin@localhost 2>/dev/null || true && exec /usr/bin/launch_awx_web.sh
  - name: awx-task
    image: quay.io/ansible/awx:latest
    imagePullPolicy: Always
    env:
    - name: DATABASE_USER
      value: awx
    - name: DATABASE_PASSWORD
      value: Bb3GfGNhP_tp2j7MeCUDUw
    - name: DATABASE_NAME
      value: awx
    - name: DATABASE_HOST
      value: 127.0.0.1
    - name: DATABASE_PORT
      value: '5432'
    - name: REDIS_HOST
      value: 127.0.0.1
    - name: REDIS_PORT
      value: '6379'
    - name: SECRET_KEY
      value: sH-5EVaeQo7PL4TV7xoudA
    - name: AWX_ADMIN_USER
      value: admin
    - name: AWX_ADMIN_PASSWORD
      value: Bb3GfGNhP_tp2j7MeCUDUw
    volumeMounts:
    - name: vol-0
      mountPath: /etc/tower
    - name: vol-2
      mountPath: /var/lib/awx/projects
    command:
    - /usr/bin/launch_awx_task.sh
  - name: postgres
    image: docker.io/postgres:15
    env:
    - name: POSTGRES_USER
      value: awx
    - name: POSTGRES_PASSWORD
      value: Bb3GfGNhP_tp2j7MeCUDUw
    - name: POSTGRES_DB
      value: awx
    volumeMounts:
    - name: vol-3
      mountPath: /var/lib/postgresql/data
    securityContext:
      runAsUser: 999
      runAsGroup: 999
  - name: redis
    image: docker.io/redis:7-alpine
    volumeMounts:
    - name: vol-4
      mountPath: /data
    args:
    - redis-server
    - --appendonly
    - 'yes'
  initContainers:
  # Auto-generated init container — do not edit manually.
  # settings.py: required by AWX to read DB/Redis credentials from environment variables above.
  # nginx.conf: reverse proxy config for the AWX web UI (port 8052 -> uWSGI on 8050).
  # uwsgi.ini: copied directly from the AWX image — updates automatically with new image versions.
  - name: awx-init
    image: quay.io/ansible/awx:latest
    imagePullPolicy: Always
    volumeMounts:
    - name: vol-0
      mountPath: /init-tower
    - name: vol-1
      mountPath: /init-nginx
    command:
    - sh
    args:
    - -c
    - cp -r /etc/nginx/. /init-nginx/ && echo aW1wb3J0IG9zCgpEQVRBQkFTRVMgPSB7CiAgICAnZGVmYXVsdCc6IHsKICAgICAgICAnQVRPTUlDX1JFUVVFU1RTJzogVHJ1ZSwKICAgICAgICAnRU5HSU5FJzogJ2F3eC5tYWluLmRiLnByb2ZpbGVkX3BnJywKICAgICAgICAnTkFNRSc6IG9zLmVudmlyb24uZ2V0KCdEQVRBQkFTRV9OQU1FJywgJ2F3eCcpLAogICAgICAgICdVU0VSJzogb3MuZW52aXJvbi5nZXQoJ0RBVEFCQVNFX1VTRVInLCAnYXd4JyksCiAgICAgICAgJ1BBU1NXT1JEJzogb3MuZW52aXJvbi5nZXQoJ0RBVEFCQVNFX1BBU1NXT1JEJywgJ2NoYW5nZW1lJyksCiAgICAgICAgJ0hPU1QnOiBvcy5lbnZpcm9uLmdldCgnREFUQUJBU0VfSE9TVCcsICcxMjcuMC4wLjEnKSwKICAgICAgICAnUE9SVCc6IG9zLmVudmlyb24uZ2V0KCdEQVRBQkFTRV9QT1JUJywgJzU0MzInKSwKICAgIH0KfQpBTExPV0VEX0hPU1RTID0gWycqJ10KU0VDUkVUX0tFWSA9IG9zLmVudmlyb24uZ2V0KCdTRUNSRVRfS0VZJywgJ2NoYW5nZW1lJykKREpBTkdPX1JFRElTX0lHTk9SRV9FWENFUFRJT05TID0gVHJ1ZQoKX3JlZGlzX2hvc3QgPSBvcy5lbnZpcm9uLmdldCgnUkVESVNfSE9TVCcsICcxMjcuMC4wLjEnKQpfcmVkaXNfcG9ydCA9IG9zLmVudmlyb24uZ2V0KCdSRURJU19QT1JUJywgJzYzNzknKQpfcmVkaXNfdXJsID0gJ3JlZGlzOi8ve306e30nLmZvcm1hdChfcmVkaXNfaG9zdCwgX3JlZGlzX3BvcnQpCgpDQUNIRVMgPSB7CiAgICAnZGVmYXVsdCc6IHsKICAgICAgICAnQkFDS0VORCc6ICdhd3gubWFpbi5jYWNoZS5BV1hSZWRpc0NhY2hlJywKICAgICAgICAnTE9DQVRJT04nOiAne30vMScuZm9ybWF0KF9yZWRpc191cmwpLAogICAgfQp9CkNIQU5ORUxfTEFZRVJTID0gewogICAgJ2RlZmF1bHQnOiB7CiAgICAgICAgJ0JBQ0tFTkQnOiAnY2hhbm5lbHNfcmVkaXMuY29yZS5SZWRpc0NoYW5uZWxMYXllcicsCiAgICAgICAgJ0NPTkZJRyc6IHsKICAgICAgICAgICAgJ2hvc3RzJzogWyhfcmVkaXNfaG9zdCwgaW50KF9yZWRpc19wb3J0KSldLAogICAgICAgICAgICAnY2FwYWNpdHknOiAxMDAwMCwKICAgICAgICAgICAgJ2dyb3VwX2V4cGlyeSc6IDE1Nzc4NDc2MCwKICAgICAgICB9CiAgICB9Cn0KQlJPS0VSX1VSTCA9IF9yZWRpc191cmwK
      | base64 -d > /init-tower/settings.py && cp /etc/tower/uwsgi.ini /init-tower/uwsgi.ini
      && echo d29ya2VyX3Byb2Nlc3NlcyBhdXRvOwplcnJvcl9sb2cgL2Rldi9zdGRlcnIgd2FybjsKcGlkIC90bXAvbmdpbngucGlkOwoKaW5jbHVkZSAvdXNyL3NoYXJlL25naW54L21vZHVsZXMvKi5jb25mOwoKZXZlbnRzIHsKICAgIHdvcmtlcl9jb25uZWN0aW9ucyAxMDI0Owp9CgpodHRwIHsKICAgIGNsaWVudF9ib2R5X3RlbXBfcGF0aCAvdG1wL25naW54LWNsaWVudC1ib2R5OwogICAgcHJveHlfdGVtcF9wYXRoIC90bXAvbmdpbngtcHJveHk7CiAgICBmYXN0Y2dpX3RlbXBfcGF0aCAvdG1wL25naW54LWZhc3RjZ2k7CiAgICB1d3NnaV90ZW1wX3BhdGggL3RtcC9uZ2lueC11d3NnaTsKICAgIHNjZ2lfdGVtcF9wYXRoIC90bXAvbmdpbngtc2NnaTsKICAgIGluY2x1ZGUgL2V0Yy9uZ2lueC9taW1lLnR5cGVzOwogICAgZGVmYXVsdF90eXBlIGFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbTsKICAgIGFjY2Vzc19sb2cgL2Rldi9zdGRvdXQ7CiAgICBzZW5kZmlsZSBvbjsKICAgIGtlZXBhbGl2ZV90aW1lb3V0IDY1OwoKICAgIHNlcnZlciB7CiAgICAgICAgbGlzdGVuIDgwNTIgZGVmYXVsdF9zZXJ2ZXI7CiAgICAgICAgc2VydmVyX25hbWUgXzsKICAgICAgICBjbGllbnRfbWF4X2JvZHlfc2l6ZSAwOwoKICAgICAgICBsb2NhdGlvbiB+IF4vd2Vic29ja2V0IHsKICAgICAgICAgICAgcHJveHlfcGFzcyBodHRwOi8vMTI3LjAuMC4xOjgwNTE7CiAgICAgICAgICAgIHByb3h5X2h0dHBfdmVyc2lvbiAxLjE7CiAgICAgICAgICAgIHByb3h5X3NldF9oZWFkZXIgVXBncmFkZSAkaHR0cF91cGdyYWRlOwogICAgICAgICAgICBwcm94eV9zZXRfaGVhZGVyIENvbm5lY3Rpb24gIlVwZ3JhZGUiOwogICAgICAgICAgICBwcm94eV9jb25uZWN0X3RpbWVvdXQgNzVzOwogICAgICAgICAgICBwcm94eV9yZWFkX3RpbWVvdXQgMTIwczsKICAgICAgICB9CgogICAgICAgIGxvY2F0aW9uIC9zdGF0aWMvIHsKICAgICAgICAgICAgYWxpYXMgL3Zhci9saWIvYXd4L3B1YmxpYy9zdGF0aWMvOwogICAgICAgIH0KCiAgICAgICAgbG9jYXRpb24gLyB7CiAgICAgICAgICAgIHV3c2dpX3JlYWRfdGltZW91dCAxMjBzOwogICAgICAgICAgICB1d3NnaV9wYXNzIDEyNy4wLjAuMTo4MDUwOwogICAgICAgICAgICBpbmNsdWRlIC9ldGMvbmdpbngvdXdzZ2lfcGFyYW1zOwogICAgICAgICAgICBwcm94eV9zZXRfaGVhZGVyIFgtRm9yd2FyZGVkLUZvciAkcHJveHlfYWRkX3hfZm9yd2FyZGVkX2ZvcjsKICAgICAgICAgICAgdXdzZ2lfcGFyYW0gSFRUUF9YX0ZPUldBUkRFRF9GT1IgJHByb3h5X2FkZF94X2ZvcndhcmRlZF9mb3I7CiAgICAgICAgfQogICAgfQp9Cg==
      | base64 -d > /init-nginx/nginx.conf
  volumes:
  - name: vol-0
    persistentVolumeClaim:
      claimName: awx-tower
  - name: vol-1
    persistentVolumeClaim:
      claimName: awx-nginx
  - name: vol-2
    persistentVolumeClaim:
      claimName: awx-projects
  - name: vol-3
    persistentVolumeClaim:
      claimName: awx-db
  - name: vol-4
    persistentVolumeClaim:
      claimName: awx-redis
1

Save the YAML file

mkdir -p ~/.config/containers/
nano ~/.config/containers/awx.yaml
2

Test the pod (without autostart)

podman kube play ~/.config/containers/awx.yaml

# Check status:
podman pod ps && podman ps

# Stop:
podman kube down ~/.config/containers/awx.yaml

Check your podman version

podman --version

4.4 or newer: continue with step 3 below.

Older, e.g. 4.3 on Debian 12? There is no Quadlet yet: use podman's own podman-kube@ service instead of steps 3–5
systemctl --user enable --now podman-kube@$(systemd-escape ~/.config/containers/awx.yaml).service

# Status & logs:
systemctl --user status podman-kube@$(systemd-escape ~/.config/containers/awx.yaml).service
journalctl --user -u podman-kube@$(systemd-escape ~/.config/containers/awx.yaml).service -f

The day-to-day commands below have a block for this service.

3

Create Quadlet .kube file

awx.kube
[Unit]
Description=awx pod
After=network-online.target
Wants=network-online.target

[Kube]
Yaml=%h/.config/containers/awx.yaml

[Service]
Restart=always

[Install]
WantedBy=default.target

%h is a systemd home-directory specifier — do not replace it with ~/.

Save to ~/.config/containers/systemd/:

mkdir -p ~/.config/containers/systemd/
nano ~/.config/containers/systemd/awx.kube
4

Start systemd service

systemctl --user daemon-reload
systemctl --user start awx.service
5

Status & Logs

systemctl --user status awx.service
journalctl --user -u awx.service -f
podman pod ps
podman ps
6

Enable automatic image updates optional

Requires AutoUpdate=registry in the .kube file. Podman then checks for new images and restarts the pod automatically:

systemctl --user enable --now podman.socket
systemctl --user daemon-reload
systemctl --user enable --now podman-auto-update.timer

# Check status:
systemctl --user status podman-auto-update.timer

# Trigger manually:
podman auto-update
Day-to-day logs, restart, update, remove (click to open)

Logs & shell into a container

podman logs -f awx-awx-web
podman logs -f awx-awx-task
podman logs -f awx-postgres
podman logs -f awx-redis
podman exec -it awx-awx-web /bin/sh

Restart

systemctl --user restart awx.service

Update an image by hand

Pull the new version, the restart recreates the containers from it:

podman pull <image>:<tag>
systemctl --user restart awx.service

Stop & remove

Stop through systemd, otherwise Restart=always brings the pod back. To remove it for good, delete the unit files and reload:

systemctl --user stop awx.service
rm ~/.config/containers/systemd/awx.kube
systemctl --user daemon-reload

podman older than 4.4 (podman-kube@ from step 3)

systemctl --user restart podman-kube@$(systemd-escape ~/.config/containers/awx.yaml).service

# Update an image: pull, then restart as above
podman pull <image>:<tag>

# Stop and remove from autostart:
systemctl --user disable --now podman-kube@$(systemd-escape ~/.config/containers/awx.yaml).service

Delete the data — permanent, only after stop & remove

podman volume rm awx-tower
podman volume rm awx-nginx
podman volume rm awx-projects
podman volume rm awx-db
podman volume rm awx-redis

Native systemd units without the Kubernetes YAML: one .pod plus one .container per container. Each container becomes its own systemd service (restart, logs and ordering per container). Needs podman 5.0 or newer — Debian 12 ships 4.3, use the Kube YAML tab there.

1

Save the unit files

awx.pod
[Unit]
Description=awx pod

[Pod]
PodName=awx
PodmanArgs=--exit-policy=continue
PublishPort=8052:8052
awx-awx-init.container
[Unit]
Description=awx-awx-init container

[Container]
Image=quay.io/ansible/awx:latest
Pod=awx.pod
ContainerName=awx-awx-init
Pull=always
Volume=awx-tower:/init-tower:Z
Volume=awx-nginx:/init-nginx:Z
Entrypoint=sh
Exec=-c "cp -r /etc/nginx/. /init-nginx/ && echo aW1wb3J0IG9zCgpEQVRBQkFTRVMgPSB7CiAgICAnZGVmYXVsdCc6IHsKICAgICAgICAnQVRPTUlDX1JFUVVFU1RTJzogVHJ1ZSwKICAgICAgICAnRU5HSU5FJzogJ2F3eC5tYWluLmRiLnByb2ZpbGVkX3BnJywKICAgICAgICAnTkFNRSc6IG9zLmVudmlyb24uZ2V0KCdEQVRBQkFTRV9OQU1FJywgJ2F3eCcpLAogICAgICAgICdVU0VSJzogb3MuZW52aXJvbi5nZXQoJ0RBVEFCQVNFX1VTRVInLCAnYXd4JyksCiAgICAgICAgJ1BBU1NXT1JEJzogb3MuZW52aXJvbi5nZXQoJ0RBVEFCQVNFX1BBU1NXT1JEJywgJ2NoYW5nZW1lJyksCiAgICAgICAgJ0hPU1QnOiBvcy5lbnZpcm9uLmdldCgnREFUQUJBU0VfSE9TVCcsICcxMjcuMC4wLjEnKSwKICAgICAgICAnUE9SVCc6IG9zLmVudmlyb24uZ2V0KCdEQVRBQkFTRV9QT1JUJywgJzU0MzInKSwKICAgIH0KfQpBTExPV0VEX0hPU1RTID0gWycqJ10KU0VDUkVUX0tFWSA9IG9zLmVudmlyb24uZ2V0KCdTRUNSRVRfS0VZJywgJ2NoYW5nZW1lJykKREpBTkdPX1JFRElTX0lHTk9SRV9FWENFUFRJT05TID0gVHJ1ZQoKX3JlZGlzX2hvc3QgPSBvcy5lbnZpcm9uLmdldCgnUkVESVNfSE9TVCcsICcxMjcuMC4wLjEnKQpfcmVkaXNfcG9ydCA9IG9zLmVudmlyb24uZ2V0KCdSRURJU19QT1JUJywgJzYzNzknKQpfcmVkaXNfdXJsID0gJ3JlZGlzOi8ve306e30nLmZvcm1hdChfcmVkaXNfaG9zdCwgX3JlZGlzX3BvcnQpCgpDQUNIRVMgPSB7CiAgICAnZGVmYXVsdCc6IHsKICAgICAgICAnQkFDS0VORCc6ICdhd3gubWFpbi5jYWNoZS5BV1hSZWRpc0NhY2hlJywKICAgICAgICAnTE9DQVRJT04nOiAne30vMScuZm9ybWF0KF9yZWRpc191cmwpLAogICAgfQp9CkNIQU5ORUxfTEFZRVJTID0gewogICAgJ2RlZmF1bHQnOiB7CiAgICAgICAgJ0JBQ0tFTkQnOiAnY2hhbm5lbHNfcmVkaXMuY29yZS5SZWRpc0NoYW5uZWxMYXllcicsCiAgICAgICAgJ0NPTkZJRyc6IHsKICAgICAgICAgICAgJ2hvc3RzJzogWyhfcmVkaXNfaG9zdCwgaW50KF9yZWRpc19wb3J0KSldLAogICAgICAgICAgICAnY2FwYWNpdHknOiAxMDAwMCwKICAgICAgICAgICAgJ2dyb3VwX2V4cGlyeSc6IDE1Nzc4NDc2MCwKICAgICAgICB9CiAgICB9Cn0KQlJPS0VSX1VSTCA9IF9yZWRpc191cmwK | base64 -d > /init-tower/settings.py && cp /etc/tower/uwsgi.ini /init-tower/uwsgi.ini && echo d29ya2VyX3Byb2Nlc3NlcyBhdXRvOwplcnJvcl9sb2cgL2Rldi9zdGRlcnIgd2FybjsKcGlkIC90bXAvbmdpbngucGlkOwoKaW5jbHVkZSAvdXNyL3NoYXJlL25naW54L21vZHVsZXMvKi5jb25mOwoKZXZlbnRzIHsKICAgIHdvcmtlcl9jb25uZWN0aW9ucyAxMDI0Owp9CgpodHRwIHsKICAgIGNsaWVudF9ib2R5X3RlbXBfcGF0aCAvdG1wL25naW54LWNsaWVudC1ib2R5OwogICAgcHJveHlfdGVtcF9wYXRoIC90bXAvbmdpbngtcHJveHk7CiAgICBmYXN0Y2dpX3RlbXBfcGF0aCAvdG1wL25naW54LWZhc3RjZ2k7CiAgICB1d3NnaV90ZW1wX3BhdGggL3RtcC9uZ2lueC11d3NnaTsKICAgIHNjZ2lfdGVtcF9wYXRoIC90bXAvbmdpbngtc2NnaTsKICAgIGluY2x1ZGUgL2V0Yy9uZ2lueC9taW1lLnR5cGVzOwogICAgZGVmYXVsdF90eXBlIGFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbTsKICAgIGFjY2Vzc19sb2cgL2Rldi9zdGRvdXQ7CiAgICBzZW5kZmlsZSBvbjsKICAgIGtlZXBhbGl2ZV90aW1lb3V0IDY1OwoKICAgIHNlcnZlciB7CiAgICAgICAgbGlzdGVuIDgwNTIgZGVmYXVsdF9zZXJ2ZXI7CiAgICAgICAgc2VydmVyX25hbWUgXzsKICAgICAgICBjbGllbnRfbWF4X2JvZHlfc2l6ZSAwOwoKICAgICAgICBsb2NhdGlvbiB+IF4vd2Vic29ja2V0IHsKICAgICAgICAgICAgcHJveHlfcGFzcyBodHRwOi8vMTI3LjAuMC4xOjgwNTE7CiAgICAgICAgICAgIHByb3h5X2h0dHBfdmVyc2lvbiAxLjE7CiAgICAgICAgICAgIHByb3h5X3NldF9oZWFkZXIgVXBncmFkZSAkaHR0cF91cGdyYWRlOwogICAgICAgICAgICBwcm94eV9zZXRfaGVhZGVyIENvbm5lY3Rpb24gIlVwZ3JhZGUiOwogICAgICAgICAgICBwcm94eV9jb25uZWN0X3RpbWVvdXQgNzVzOwogICAgICAgICAgICBwcm94eV9yZWFkX3RpbWVvdXQgMTIwczsKICAgICAgICB9CgogICAgICAgIGxvY2F0aW9uIC9zdGF0aWMvIHsKICAgICAgICAgICAgYWxpYXMgL3Zhci9saWIvYXd4L3B1YmxpYy9zdGF0aWMvOwogICAgICAgIH0KCiAgICAgICAgbG9jYXRpb24gLyB7CiAgICAgICAgICAgIHV3c2dpX3JlYWRfdGltZW91dCAxMjBzOwogICAgICAgICAgICB1d3NnaV9wYXNzIDEyNy4wLjAuMTo4MDUwOwogICAgICAgICAgICBpbmNsdWRlIC9ldGMvbmdpbngvdXdzZ2lfcGFyYW1zOwogICAgICAgICAgICBwcm94eV9zZXRfaGVhZGVyIFgtRm9yd2FyZGVkLUZvciAkcHJveHlfYWRkX3hfZm9yd2FyZGVkX2ZvcjsKICAgICAgICAgICAgdXdzZ2lfcGFyYW0gSFRUUF9YX0ZPUldBUkRFRF9GT1IgJHByb3h5X2FkZF94X2ZvcndhcmRlZF9mb3I7CiAgICAgICAgfQogICAgfQp9Cg== | base64 -d > /init-nginx/nginx.conf"

[Service]
Type=oneshot
RemainAfterExit=yes
Restart=no

[Install]
WantedBy=default.target
awx-awx-web.container
[Unit]
Description=awx-awx-web container
Requires=awx-awx-init.service
After=awx-awx-init.service

[Container]
Image=quay.io/ansible/awx:latest
Pod=awx.pod
ContainerName=awx-awx-web
Pull=always
Environment=DATABASE_USER=awx
Environment=DATABASE_PASSWORD=Bb3GfGNhP_tp2j7MeCUDUw
Environment=DATABASE_NAME=awx
Environment=DATABASE_HOST=127.0.0.1
Environment=DATABASE_PORT=5432
Environment=REDIS_HOST=127.0.0.1
Environment=REDIS_PORT=6379
Environment=SECRET_KEY=sH-5EVaeQo7PL4TV7xoudA
Environment=AWX_ADMIN_USER=admin
Environment=AWX_ADMIN_PASSWORD=Bb3GfGNhP_tp2j7MeCUDUw
Volume=awx-tower:/etc/tower:Z
Volume=awx-nginx:/etc/nginx:Z
Volume=awx-projects:/var/lib/awx/projects:Z
Entrypoint=/bin/sh
Exec=-c "until python3 -c \"import socket;s=socket.create_connection(('$${DATABASE_HOST:-127.0.0.1}',$${DATABASE_PORT:-5432}),1);s.close()\" 2>/dev/null; do echo 'Waiting for postgres...'; sleep 3; done && awx-manage migrate && DJANGO_SUPERUSER_PASSWORD=$${AWX_ADMIN_PASSWORD:-Bb3GfGNhP_tp2j7MeCUDUw} awx-manage createsuperuser --noinput --username $${AWX_ADMIN_USER:-admin} --email admin@localhost 2>/dev/null || true && exec /usr/bin/launch_awx_web.sh"

[Service]
Restart=always

[Install]
WantedBy=default.target
awx-awx-task.container
[Unit]
Description=awx-awx-task container
Requires=awx-awx-init.service
After=awx-awx-init.service

[Container]
Image=quay.io/ansible/awx:latest
Pod=awx.pod
ContainerName=awx-awx-task
Pull=always
Environment=DATABASE_USER=awx
Environment=DATABASE_PASSWORD=Bb3GfGNhP_tp2j7MeCUDUw
Environment=DATABASE_NAME=awx
Environment=DATABASE_HOST=127.0.0.1
Environment=DATABASE_PORT=5432
Environment=REDIS_HOST=127.0.0.1
Environment=REDIS_PORT=6379
Environment=SECRET_KEY=sH-5EVaeQo7PL4TV7xoudA
Environment=AWX_ADMIN_USER=admin
Environment=AWX_ADMIN_PASSWORD=Bb3GfGNhP_tp2j7MeCUDUw
Volume=awx-tower:/etc/tower:Z
Volume=awx-projects:/var/lib/awx/projects:Z
Entrypoint=/usr/bin/launch_awx_task.sh

[Service]
Restart=always

[Install]
WantedBy=default.target
awx-postgres.container
[Unit]
Description=awx-postgres container
Requires=awx-awx-init.service
After=awx-awx-init.service

[Container]
Image=docker.io/postgres:15
Pod=awx.pod
ContainerName=awx-postgres
Environment=POSTGRES_USER=awx
Environment=POSTGRES_PASSWORD=Bb3GfGNhP_tp2j7MeCUDUw
Environment=POSTGRES_DB=awx
Volume=awx-db:/var/lib/postgresql/data:Z
User=999
Group=999

[Service]
Restart=always

[Install]
WantedBy=default.target
awx-redis.container
[Unit]
Description=awx-redis container
Requires=awx-awx-init.service
After=awx-awx-init.service

[Container]
Image=docker.io/redis:7-alpine
Pod=awx.pod
ContainerName=awx-redis
Volume=awx-redis:/data:Z
Exec=redis-server --appendonly yes

[Service]
Restart=always

[Install]
WantedBy=default.target

Save all of them to ~/.config/containers/systemd/:

mkdir -p ~/.config/containers/systemd/
nano ~/.config/containers/systemd/awx.pod
nano ~/.config/containers/systemd/awx-awx-init.container
nano ~/.config/containers/systemd/awx-awx-web.container
nano ~/.config/containers/systemd/awx-awx-task.container
nano ~/.config/containers/systemd/awx-postgres.container
nano ~/.config/containers/systemd/awx-redis.container
2

Start the pod

The pod service pulls in every container service. Autostart at boot comes from the [Install] section of the units, no enable needed.

systemctl --user daemon-reload
systemctl --user start awx-pod.service
3

Status & Logs

systemctl --user status awx-pod.service
journalctl --user -u awx-awx-init.service -f
journalctl --user -u awx-awx-web.service -f
journalctl --user -u awx-awx-task.service -f
journalctl --user -u awx-postgres.service -f
journalctl --user -u awx-redis.service -f
podman pod ps && podman ps
Day-to-day logs, restart, update, remove (click to open)

Logs & shell into a container

podman logs -f awx-awx-web
podman logs -f awx-awx-task
podman logs -f awx-postgres
podman logs -f awx-redis
podman exec -it awx-awx-web /bin/sh

Restart

systemctl --user restart awx-pod.service

Update an image by hand

Pull the new version, the restart recreates the containers from it:

podman pull <image>:<tag>
systemctl --user restart awx-pod.service

Stop & remove

Stop through systemd, otherwise Restart=always brings the pod back. To remove it for good, delete the unit files and reload:

systemctl --user stop awx-pod.service
rm ~/.config/containers/systemd/awx.pod
rm ~/.config/containers/systemd/awx-awx-init.container
rm ~/.config/containers/systemd/awx-awx-web.container
rm ~/.config/containers/systemd/awx-awx-task.container
rm ~/.config/containers/systemd/awx-postgres.container
rm ~/.config/containers/systemd/awx-redis.container
systemctl --user daemon-reload

Delete the data — permanent, only after stop & remove

podman volume rm awx-tower
podman volume rm awx-nginx
podman volume rm awx-projects
podman volume rm awx-db
podman volume rm awx-redis

A docker-compose.yml for podman-compose. Handy if you already work with Compose; there are no per-container systemd services.

1

Install podman-compose as root

Run this as root, e.g. together with the prerequisites before su - awx.

apt install -y podman-compose
2

Save the compose file

docker-compose.yml
services:
  awx-init:
    image: quay.io/ansible/awx:latest
    pull_policy: always
    restart: 'no'
    entrypoint:
    - sh
    command:
    - -c
    - cp -r /etc/nginx/. /init-nginx/ && echo aW1wb3J0IG9zCgpEQVRBQkFTRVMgPSB7CiAgICAnZGVmYXVsdCc6IHsKICAgICAgICAnQVRPTUlDX1JFUVVFU1RTJzogVHJ1ZSwKICAgICAgICAnRU5HSU5FJzogJ2F3eC5tYWluLmRiLnByb2ZpbGVkX3BnJywKICAgICAgICAnTkFNRSc6IG9zLmVudmlyb24uZ2V0KCdEQVRBQkFTRV9OQU1FJywgJ2F3eCcpLAogICAgICAgICdVU0VSJzogb3MuZW52aXJvbi5nZXQoJ0RBVEFCQVNFX1VTRVInLCAnYXd4JyksCiAgICAgICAgJ1BBU1NXT1JEJzogb3MuZW52aXJvbi5nZXQoJ0RBVEFCQVNFX1BBU1NXT1JEJywgJ2NoYW5nZW1lJyksCiAgICAgICAgJ0hPU1QnOiBvcy5lbnZpcm9uLmdldCgnREFUQUJBU0VfSE9TVCcsICcxMjcuMC4wLjEnKSwKICAgICAgICAnUE9SVCc6IG9zLmVudmlyb24uZ2V0KCdEQVRBQkFTRV9QT1JUJywgJzU0MzInKSwKICAgIH0KfQpBTExPV0VEX0hPU1RTID0gWycqJ10KU0VDUkVUX0tFWSA9IG9zLmVudmlyb24uZ2V0KCdTRUNSRVRfS0VZJywgJ2NoYW5nZW1lJykKREpBTkdPX1JFRElTX0lHTk9SRV9FWENFUFRJT05TID0gVHJ1ZQoKX3JlZGlzX2hvc3QgPSBvcy5lbnZpcm9uLmdldCgnUkVESVNfSE9TVCcsICcxMjcuMC4wLjEnKQpfcmVkaXNfcG9ydCA9IG9zLmVudmlyb24uZ2V0KCdSRURJU19QT1JUJywgJzYzNzknKQpfcmVkaXNfdXJsID0gJ3JlZGlzOi8ve306e30nLmZvcm1hdChfcmVkaXNfaG9zdCwgX3JlZGlzX3BvcnQpCgpDQUNIRVMgPSB7CiAgICAnZGVmYXVsdCc6IHsKICAgICAgICAnQkFDS0VORCc6ICdhd3gubWFpbi5jYWNoZS5BV1hSZWRpc0NhY2hlJywKICAgICAgICAnTE9DQVRJT04nOiAne30vMScuZm9ybWF0KF9yZWRpc191cmwpLAogICAgfQp9CkNIQU5ORUxfTEFZRVJTID0gewogICAgJ2RlZmF1bHQnOiB7CiAgICAgICAgJ0JBQ0tFTkQnOiAnY2hhbm5lbHNfcmVkaXMuY29yZS5SZWRpc0NoYW5uZWxMYXllcicsCiAgICAgICAgJ0NPTkZJRyc6IHsKICAgICAgICAgICAgJ2hvc3RzJzogWyhfcmVkaXNfaG9zdCwgaW50KF9yZWRpc19wb3J0KSldLAogICAgICAgICAgICAnY2FwYWNpdHknOiAxMDAwMCwKICAgICAgICAgICAgJ2dyb3VwX2V4cGlyeSc6IDE1Nzc4NDc2MCwKICAgICAgICB9CiAgICB9Cn0KQlJPS0VSX1VSTCA9IF9yZWRpc191cmwK
      | base64 -d > /init-tower/settings.py && cp /etc/tower/uwsgi.ini /init-tower/uwsgi.ini
      && echo d29ya2VyX3Byb2Nlc3NlcyBhdXRvOwplcnJvcl9sb2cgL2Rldi9zdGRlcnIgd2FybjsKcGlkIC90bXAvbmdpbngucGlkOwoKaW5jbHVkZSAvdXNyL3NoYXJlL25naW54L21vZHVsZXMvKi5jb25mOwoKZXZlbnRzIHsKICAgIHdvcmtlcl9jb25uZWN0aW9ucyAxMDI0Owp9CgpodHRwIHsKICAgIGNsaWVudF9ib2R5X3RlbXBfcGF0aCAvdG1wL25naW54LWNsaWVudC1ib2R5OwogICAgcHJveHlfdGVtcF9wYXRoIC90bXAvbmdpbngtcHJveHk7CiAgICBmYXN0Y2dpX3RlbXBfcGF0aCAvdG1wL25naW54LWZhc3RjZ2k7CiAgICB1d3NnaV90ZW1wX3BhdGggL3RtcC9uZ2lueC11d3NnaTsKICAgIHNjZ2lfdGVtcF9wYXRoIC90bXAvbmdpbngtc2NnaTsKICAgIGluY2x1ZGUgL2V0Yy9uZ2lueC9taW1lLnR5cGVzOwogICAgZGVmYXVsdF90eXBlIGFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbTsKICAgIGFjY2Vzc19sb2cgL2Rldi9zdGRvdXQ7CiAgICBzZW5kZmlsZSBvbjsKICAgIGtlZXBhbGl2ZV90aW1lb3V0IDY1OwoKICAgIHNlcnZlciB7CiAgICAgICAgbGlzdGVuIDgwNTIgZGVmYXVsdF9zZXJ2ZXI7CiAgICAgICAgc2VydmVyX25hbWUgXzsKICAgICAgICBjbGllbnRfbWF4X2JvZHlfc2l6ZSAwOwoKICAgICAgICBsb2NhdGlvbiB+IF4vd2Vic29ja2V0IHsKICAgICAgICAgICAgcHJveHlfcGFzcyBodHRwOi8vMTI3LjAuMC4xOjgwNTE7CiAgICAgICAgICAgIHByb3h5X2h0dHBfdmVyc2lvbiAxLjE7CiAgICAgICAgICAgIHByb3h5X3NldF9oZWFkZXIgVXBncmFkZSAkaHR0cF91cGdyYWRlOwogICAgICAgICAgICBwcm94eV9zZXRfaGVhZGVyIENvbm5lY3Rpb24gIlVwZ3JhZGUiOwogICAgICAgICAgICBwcm94eV9jb25uZWN0X3RpbWVvdXQgNzVzOwogICAgICAgICAgICBwcm94eV9yZWFkX3RpbWVvdXQgMTIwczsKICAgICAgICB9CgogICAgICAgIGxvY2F0aW9uIC9zdGF0aWMvIHsKICAgICAgICAgICAgYWxpYXMgL3Zhci9saWIvYXd4L3B1YmxpYy9zdGF0aWMvOwogICAgICAgIH0KCiAgICAgICAgbG9jYXRpb24gLyB7CiAgICAgICAgICAgIHV3c2dpX3JlYWRfdGltZW91dCAxMjBzOwogICAgICAgICAgICB1d3NnaV9wYXNzIDEyNy4wLjAuMTo4MDUwOwogICAgICAgICAgICBpbmNsdWRlIC9ldGMvbmdpbngvdXdzZ2lfcGFyYW1zOwogICAgICAgICAgICBwcm94eV9zZXRfaGVhZGVyIFgtRm9yd2FyZGVkLUZvciAkcHJveHlfYWRkX3hfZm9yd2FyZGVkX2ZvcjsKICAgICAgICAgICAgdXdzZ2lfcGFyYW0gSFRUUF9YX0ZPUldBUkRFRF9GT1IgJHByb3h5X2FkZF94X2ZvcndhcmRlZF9mb3I7CiAgICAgICAgfQogICAgfQp9Cg==
      | base64 -d > /init-nginx/nginx.conf
    volumes:
    - awx-tower:/init-tower:Z
    - awx-nginx:/init-nginx:Z
  awx-web:
    image: quay.io/ansible/awx:latest
    pull_policy: always
    restart: always
    environment:
    - DATABASE_USER=awx
    - DATABASE_PASSWORD=Bb3GfGNhP_tp2j7MeCUDUw
    - DATABASE_NAME=awx
    - DATABASE_HOST=127.0.0.1
    - DATABASE_PORT=5432
    - REDIS_HOST=127.0.0.1
    - REDIS_PORT=6379
    - SECRET_KEY=sH-5EVaeQo7PL4TV7xoudA
    - AWX_ADMIN_USER=admin
    - AWX_ADMIN_PASSWORD=Bb3GfGNhP_tp2j7MeCUDUw
    entrypoint:
    - /bin/sh
    command:
    - -c
    - until python3 -c "import socket;s=socket.create_connection(('$${DATABASE_HOST:-127.0.0.1}',$${DATABASE_PORT:-5432}),1);s.close()"
      2>/dev/null; do echo 'Waiting for postgres...'; sleep 3; done && awx-manage
      migrate && DJANGO_SUPERUSER_PASSWORD=$${AWX_ADMIN_PASSWORD:-Bb3GfGNhP_tp2j7MeCUDUw}
      awx-manage createsuperuser --noinput --username $${AWX_ADMIN_USER:-admin} --email
      admin@localhost 2>/dev/null || true && exec /usr/bin/launch_awx_web.sh
    depends_on:
      awx-init:
        condition: service_completed_successfully
      postgres:
        condition: service_started
      redis:
        condition: service_started
    volumes:
    - awx-tower:/etc/tower:Z
    - awx-nginx:/etc/nginx:Z
    - awx-projects:/var/lib/awx/projects:Z
    network_mode: service:postgres
  awx-task:
    image: quay.io/ansible/awx:latest
    pull_policy: always
    restart: always
    environment:
    - DATABASE_USER=awx
    - DATABASE_PASSWORD=Bb3GfGNhP_tp2j7MeCUDUw
    - DATABASE_NAME=awx
    - DATABASE_HOST=127.0.0.1
    - DATABASE_PORT=5432
    - REDIS_HOST=127.0.0.1
    - REDIS_PORT=6379
    - SECRET_KEY=sH-5EVaeQo7PL4TV7xoudA
    - AWX_ADMIN_USER=admin
    - AWX_ADMIN_PASSWORD=Bb3GfGNhP_tp2j7MeCUDUw
    entrypoint:
    - /usr/bin/launch_awx_task.sh
    depends_on:
      awx-init:
        condition: service_completed_successfully
      postgres:
        condition: service_started
      redis:
        condition: service_started
    volumes:
    - awx-tower:/etc/tower:Z
    - awx-projects:/var/lib/awx/projects:Z
    network_mode: service:postgres
  postgres:
    image: docker.io/postgres:15
    restart: always
    environment:
    - POSTGRES_USER=awx
    - POSTGRES_PASSWORD=Bb3GfGNhP_tp2j7MeCUDUw
    - POSTGRES_DB=awx
    user: 999:999
    depends_on:
      awx-init:
        condition: service_completed_successfully
    volumes:
    - awx-db:/var/lib/postgresql/data:Z
    ports:
    - 8052:8052
  redis:
    image: docker.io/redis:7-alpine
    restart: always
    command:
    - redis-server
    - --appendonly
    - 'yes'
    depends_on:
      awx-init:
        condition: service_completed_successfully
      postgres:
        condition: service_started
    volumes:
    - awx-redis:/data:Z
    network_mode: service:postgres
volumes:
  awx-tower: null
  awx-nginx: null
  awx-projects: null
  awx-db: null
  awx-redis: null
mkdir -p ~/awx
nano ~/awx/docker-compose.yml
3

Start, status & logs

cd ~/awx
podman-compose up -d
podman-compose ps
podman-compose logs -f

# Stop:
podman-compose down
4

Start after reboot

podman-restart.service starts every container with restart: always at boot:

systemctl --user enable podman-restart.service
5

Update images

cd ~/awx
podman-compose pull
podman-compose up -d
Day-to-day logs, restart, update, remove (click to open)

Logs & shell into a container

podman logs -f awx_awx-web_1
podman logs -f awx_awx-task_1
podman logs -f awx_postgres_1
podman logs -f awx_redis_1
podman exec -it awx_awx-web_1 /bin/sh

Restart

cd ~/awx
podman-compose restart

Stop & remove

cd ~/awx
podman-compose down

Delete the data — permanent, only after stop & remove

podman volume rm awx_awx-tower
podman volume rm awx_awx-nginx
podman volume rm awx_awx-projects
podman volume rm awx_awx-db
podman volume rm awx_awx-redis

Plain podman pod create / podman create commands. Good for a quick test or to see what happens under the hood.

1

Save the script

awx.sh
#!/bin/bash
set -e

# ── Create named volumes ───────────────────────────────────────────
# Named volumes are created automatically — no UID/GID permission adjustment needed.
podman volume exists awx-tower || podman volume create awx-tower
podman volume exists awx-nginx || podman volume create awx-nginx
podman volume exists awx-projects || podman volume create awx-projects
podman volume exists awx-db || podman volume create awx-db
podman volume exists awx-redis || podman volume create awx-redis

# ── Create pod ─────────────────────────────────────────────────────
podman pod create \
  --name awx \
  -p 8052:8052

# ── Init container: awx-awx-init ────────────────────────────────────────
podman create \
  --pod awx \
  --name awx-awx-init \
  --init-ctr=once \
  --pull always \
  -v awx-tower:/init-tower:Z \
  -v awx-nginx:/init-nginx:Z \
  --entrypoint sh \
  quay.io/ansible/awx:latest \
  -c 'cp -r /etc/nginx/. /init-nginx/ && echo aW1wb3J0IG9zCgpEQVRBQkFTRVMgPSB7CiAgICAnZGVmYXVsdCc6IHsKICAgICAgICAnQVRPTUlDX1JFUVVFU1RTJzogVHJ1ZSwKICAgICAgICAnRU5HSU5FJzogJ2F3eC5tYWluLmRiLnByb2ZpbGVkX3BnJywKICAgICAgICAnTkFNRSc6IG9zLmVudmlyb24uZ2V0KCdEQVRBQkFTRV9OQU1FJywgJ2F3eCcpLAogICAgICAgICdVU0VSJzogb3MuZW52aXJvbi5nZXQoJ0RBVEFCQVNFX1VTRVInLCAnYXd4JyksCiAgICAgICAgJ1BBU1NXT1JEJzogb3MuZW52aXJvbi5nZXQoJ0RBVEFCQVNFX1BBU1NXT1JEJywgJ2NoYW5nZW1lJyksCiAgICAgICAgJ0hPU1QnOiBvcy5lbnZpcm9uLmdldCgnREFUQUJBU0VfSE9TVCcsICcxMjcuMC4wLjEnKSwKICAgICAgICAnUE9SVCc6IG9zLmVudmlyb24uZ2V0KCdEQVRBQkFTRV9QT1JUJywgJzU0MzInKSwKICAgIH0KfQpBTExPV0VEX0hPU1RTID0gWycqJ10KU0VDUkVUX0tFWSA9IG9zLmVudmlyb24uZ2V0KCdTRUNSRVRfS0VZJywgJ2NoYW5nZW1lJykKREpBTkdPX1JFRElTX0lHTk9SRV9FWENFUFRJT05TID0gVHJ1ZQoKX3JlZGlzX2hvc3QgPSBvcy5lbnZpcm9uLmdldCgnUkVESVNfSE9TVCcsICcxMjcuMC4wLjEnKQpfcmVkaXNfcG9ydCA9IG9zLmVudmlyb24uZ2V0KCdSRURJU19QT1JUJywgJzYzNzknKQpfcmVkaXNfdXJsID0gJ3JlZGlzOi8ve306e30nLmZvcm1hdChfcmVkaXNfaG9zdCwgX3JlZGlzX3BvcnQpCgpDQUNIRVMgPSB7CiAgICAnZGVmYXVsdCc6IHsKICAgICAgICAnQkFDS0VORCc6ICdhd3gubWFpbi5jYWNoZS5BV1hSZWRpc0NhY2hlJywKICAgICAgICAnTE9DQVRJT04nOiAne30vMScuZm9ybWF0KF9yZWRpc191cmwpLAogICAgfQp9CkNIQU5ORUxfTEFZRVJTID0gewogICAgJ2RlZmF1bHQnOiB7CiAgICAgICAgJ0JBQ0tFTkQnOiAnY2hhbm5lbHNfcmVkaXMuY29yZS5SZWRpc0NoYW5uZWxMYXllcicsCiAgICAgICAgJ0NPTkZJRyc6IHsKICAgICAgICAgICAgJ2hvc3RzJzogWyhfcmVkaXNfaG9zdCwgaW50KF9yZWRpc19wb3J0KSldLAogICAgICAgICAgICAnY2FwYWNpdHknOiAxMDAwMCwKICAgICAgICAgICAgJ2dyb3VwX2V4cGlyeSc6IDE1Nzc4NDc2MCwKICAgICAgICB9CiAgICB9Cn0KQlJPS0VSX1VSTCA9IF9yZWRpc191cmwK | base64 -d > /init-tower/settings.py && cp /etc/tower/uwsgi.ini /init-tower/uwsgi.ini && echo d29ya2VyX3Byb2Nlc3NlcyBhdXRvOwplcnJvcl9sb2cgL2Rldi9zdGRlcnIgd2FybjsKcGlkIC90bXAvbmdpbngucGlkOwoKaW5jbHVkZSAvdXNyL3NoYXJlL25naW54L21vZHVsZXMvKi5jb25mOwoKZXZlbnRzIHsKICAgIHdvcmtlcl9jb25uZWN0aW9ucyAxMDI0Owp9CgpodHRwIHsKICAgIGNsaWVudF9ib2R5X3RlbXBfcGF0aCAvdG1wL25naW54LWNsaWVudC1ib2R5OwogICAgcHJveHlfdGVtcF9wYXRoIC90bXAvbmdpbngtcHJveHk7CiAgICBmYXN0Y2dpX3RlbXBfcGF0aCAvdG1wL25naW54LWZhc3RjZ2k7CiAgICB1d3NnaV90ZW1wX3BhdGggL3RtcC9uZ2lueC11d3NnaTsKICAgIHNjZ2lfdGVtcF9wYXRoIC90bXAvbmdpbngtc2NnaTsKICAgIGluY2x1ZGUgL2V0Yy9uZ2lueC9taW1lLnR5cGVzOwogICAgZGVmYXVsdF90eXBlIGFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbTsKICAgIGFjY2Vzc19sb2cgL2Rldi9zdGRvdXQ7CiAgICBzZW5kZmlsZSBvbjsKICAgIGtlZXBhbGl2ZV90aW1lb3V0IDY1OwoKICAgIHNlcnZlciB7CiAgICAgICAgbGlzdGVuIDgwNTIgZGVmYXVsdF9zZXJ2ZXI7CiAgICAgICAgc2VydmVyX25hbWUgXzsKICAgICAgICBjbGllbnRfbWF4X2JvZHlfc2l6ZSAwOwoKICAgICAgICBsb2NhdGlvbiB+IF4vd2Vic29ja2V0IHsKICAgICAgICAgICAgcHJveHlfcGFzcyBodHRwOi8vMTI3LjAuMC4xOjgwNTE7CiAgICAgICAgICAgIHByb3h5X2h0dHBfdmVyc2lvbiAxLjE7CiAgICAgICAgICAgIHByb3h5X3NldF9oZWFkZXIgVXBncmFkZSAkaHR0cF91cGdyYWRlOwogICAgICAgICAgICBwcm94eV9zZXRfaGVhZGVyIENvbm5lY3Rpb24gIlVwZ3JhZGUiOwogICAgICAgICAgICBwcm94eV9jb25uZWN0X3RpbWVvdXQgNzVzOwogICAgICAgICAgICBwcm94eV9yZWFkX3RpbWVvdXQgMTIwczsKICAgICAgICB9CgogICAgICAgIGxvY2F0aW9uIC9zdGF0aWMvIHsKICAgICAgICAgICAgYWxpYXMgL3Zhci9saWIvYXd4L3B1YmxpYy9zdGF0aWMvOwogICAgICAgIH0KCiAgICAgICAgbG9jYXRpb24gLyB7CiAgICAgICAgICAgIHV3c2dpX3JlYWRfdGltZW91dCAxMjBzOwogICAgICAgICAgICB1d3NnaV9wYXNzIDEyNy4wLjAuMTo4MDUwOwogICAgICAgICAgICBpbmNsdWRlIC9ldGMvbmdpbngvdXdzZ2lfcGFyYW1zOwogICAgICAgICAgICBwcm94eV9zZXRfaGVhZGVyIFgtRm9yd2FyZGVkLUZvciAkcHJveHlfYWRkX3hfZm9yd2FyZGVkX2ZvcjsKICAgICAgICAgICAgdXdzZ2lfcGFyYW0gSFRUUF9YX0ZPUldBUkRFRF9GT1IgJHByb3h5X2FkZF94X2ZvcndhcmRlZF9mb3I7CiAgICAgICAgfQogICAgfQp9Cg== | base64 -d > /init-nginx/nginx.conf'

# ── Container: awx-awx-web ─────────────────────────────────────────────
podman create \
  --pod awx \
  --name awx-awx-web \
  --restart always \
  --pull always \
  -e DATABASE_USER='awx' \
  -e DATABASE_PASSWORD='Bb3GfGNhP_tp2j7MeCUDUw' \
  -e DATABASE_NAME='awx' \
  -e DATABASE_HOST='127.0.0.1' \
  -e DATABASE_PORT='5432' \
  -e REDIS_HOST='127.0.0.1' \
  -e REDIS_PORT='6379' \
  -e SECRET_KEY='sH-5EVaeQo7PL4TV7xoudA' \
  -e AWX_ADMIN_USER='admin' \
  -e AWX_ADMIN_PASSWORD='Bb3GfGNhP_tp2j7MeCUDUw' \
  -v awx-tower:/etc/tower:Z \
  -v awx-nginx:/etc/nginx:Z \
  -v awx-projects:/var/lib/awx/projects:Z \
  --entrypoint /bin/sh \
  quay.io/ansible/awx:latest \
  -c 'until python3 -c "import socket;s=socket.create_connection(('"'"'${DATABASE_HOST:-127.0.0.1}'"'"',${DATABASE_PORT:-5432}),1);s.close()" 2>/dev/null; do echo '"'"'Waiting for postgres...'"'"'; sleep 3; done && awx-manage migrate && DJANGO_SUPERUSER_PASSWORD=${AWX_ADMIN_PASSWORD:-Bb3GfGNhP_tp2j7MeCUDUw} awx-manage createsuperuser --noinput --username ${AWX_ADMIN_USER:-admin} --email admin@localhost 2>/dev/null || true && exec /usr/bin/launch_awx_web.sh'

# ── Container: awx-awx-task ─────────────────────────────────────────────
podman create \
  --pod awx \
  --name awx-awx-task \
  --restart always \
  --pull always \
  -e DATABASE_USER='awx' \
  -e DATABASE_PASSWORD='Bb3GfGNhP_tp2j7MeCUDUw' \
  -e DATABASE_NAME='awx' \
  -e DATABASE_HOST='127.0.0.1' \
  -e DATABASE_PORT='5432' \
  -e REDIS_HOST='127.0.0.1' \
  -e REDIS_PORT='6379' \
  -e SECRET_KEY='sH-5EVaeQo7PL4TV7xoudA' \
  -e AWX_ADMIN_USER='admin' \
  -e AWX_ADMIN_PASSWORD='Bb3GfGNhP_tp2j7MeCUDUw' \
  -v awx-tower:/etc/tower:Z \
  -v awx-projects:/var/lib/awx/projects:Z \
  --entrypoint /usr/bin/launch_awx_task.sh \
  quay.io/ansible/awx:latest

# ── Container: awx-postgres ─────────────────────────────────────────────
podman create \
  --pod awx \
  --name awx-postgres \
  --restart always \
  -e POSTGRES_USER='awx' \
  -e POSTGRES_PASSWORD='Bb3GfGNhP_tp2j7MeCUDUw' \
  -e POSTGRES_DB='awx' \
  -v awx-db:/var/lib/postgresql/data:Z \
  --user 999:999 \
  docker.io/postgres:15

# ── Container: awx-redis ─────────────────────────────────────────────
podman create \
  --pod awx \
  --name awx-redis \
  --restart always \
  -v awx-redis:/data:Z \
  docker.io/redis:7-alpine \
  redis-server --appendonly yes

# ── Start pod ──────────────────────────────────────────────────────
podman pod start awx

# ── Stop & clean up ───────────────────────────────────────────────
# podman pod stop awx
# podman pod rm awx
nano awx.sh
2

Run it, status & logs

bash awx.sh
podman pod ps && podman ps
podman logs -f <container-name>
3

Start after reboot

podman-restart.service starts every container with --restart always at boot:

systemctl --user enable podman-restart.service
4

Remove or rebuild

The script creates the pod once. To apply changes or new images, remove the pod and run the script again (named volumes stay):

podman pod rm -f awx
bash awx.sh
Day-to-day logs, restart, update, remove (click to open)

Logs & shell into a container

podman logs -f awx-awx-web
podman logs -f awx-awx-task
podman logs -f awx-postgres
podman logs -f awx-redis
podman exec -it awx-awx-web /bin/sh

Restart

podman pod stop awx
podman pod start awx

Stop & remove

podman pod rm -f awx

Delete the data — permanent, only after stop & remove

podman volume rm awx-tower
podman volume rm awx-nginx
podman volume rm awx-projects
podman volume rm awx-db
podman volume rm awx-redis

Ports < 1024 (e.g. 80, 443)

Rootless cannot open privileged ports. Solution:

sysctl net.ipv4.ip_unprivileged_port_start=80

Make persistent in /etc/sysctl.d/99-podman.conf.

Containers communicate via localhost

All containers in the pod share the same network namespace. Always use localhost, not container names.

# Correct (e.g. app → db):
localhost:5432

# Wrong (doesn't work in a pod):
db-container:5432

List open ports

Which ports are the running containers publishing? See the PORTS column:

podman ps

Custom DNS for the pod

Set a custom DNS server (e.g. local Pi-hole) in the generator under DNS server. Every format picks it up.

Set volume ownership

Fix permission errors by adjusting UID/GID in the user namespace:

podman unshare chown 1000:1000 /path/to/volume

SELinux volume labels

On SELinux systems (RHEL, Fedora) set the volume suffix:

/host/path:/container/path:Z   # private
/host/path:/container/path:z   # shared

List all volumes

podman volume ls
podman volume inspect <volume-name>

Volume backup

Back up data from a named volume:

podman run --rm \
  -v <volume-name>:/data:ro \
  -v $(pwd):/backup \
  busybox tar czf /backup/backup.tar.gz /data

Cleanup

Remove unused images, containers and volumes:

podman system prune -f        # containers + images
podman image prune -f         # untagged images only
podman volume prune -f        # unused volumes

Check for outdated images

List local images; auto-update only covers units with AutoUpdate= (Kube and Quadlet tabs):

podman images
podman auto-update --dry-run

All containers, including stopped ones

podman pod ps
podman ps -a

Resource usage

podman stats --no-stream

Customize this stack
Change ports, volumes, environment variables and more in the generator.
Open in Generator