Deploy Ansible AWX 24.6 with Docker Compose, PostgreSQL backend, and enterprise-grade RBAC. Set up dynamic inventory management with multiple sources and implement automated job workflows with notifications for production environments.
Prerequisites
- 4GB RAM minimum
- 20GB disk space
- Docker and Docker Compose installed
- Domain name for SSL configuration
What this solves
Ansible AWX 24.6 provides a web-based interface for managing Ansible automation at enterprise scale. This tutorial shows you how to deploy AWX with Docker Compose, configure role-based access control (RBAC) with teams and organizations, set up dynamic inventory management from multiple sources, and implement automated job templates with workflow notifications for production environments.
Step-by-step installation
Update system packages and install prerequisites
Start by updating your system and installing Docker, Docker Compose, and other required packages.
sudo apt update && sudo apt upgrade -y
sudo apt install -y docker.io docker-compose-v2 curl git pwgen
Configure Docker service and user permissions
Enable Docker service and add your user to the docker group to run containers without sudo.
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
newgrp docker
Create AWX installation directory structure
Create directories for AWX installation files, data persistence, and configuration.
sudo mkdir -p /opt/awx/{data,logs,ssl}
sudo chown -R $USER:$USER /opt/awx
cd /opt/awx
Download AWX operator and create namespace
Clone the official AWX operator repository and prepare the installation manifests.
git clone https://github.com/ansible/awx-operator.git
cd awx-operator
git checkout 2.19.1
Generate secure passwords and secrets
Create strong passwords for PostgreSQL database and AWX admin user.
cd /opt/awx
echo "POSTGRES_PASSWORD=$(pwgen -s 32 1)" > .env
echo "AWX_ADMIN_PASSWORD=$(pwgen -s 16 1)" >> .env
echo "SECRET_KEY=$(pwgen -s 50 1)" >> .env
chmod 600 .env
Create Docker Compose configuration
Set up the complete Docker Compose stack with PostgreSQL, Redis, and AWX services.
version: '3.8'
services:
postgres:
image: postgres:15
container_name: awx_postgres
environment:
POSTGRES_DB: awx
POSTGRES_USER: awx
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- postgres_data:/var/lib/postgresql/data/pgdata
restart: unless-stopped
networks:
- awx_network
redis:
image: redis:7-alpine
container_name: awx_redis
restart: unless-stopped
networks:
- awx_network
command: redis-server --appendonly yes
volumes:
- redis_data:/data
awx_web:
image: quay.io/ansible/awx:24.6.1
container_name: awx_web
hostname: awxweb
user: root
restart: unless-stopped
ports:
- "8080:8052"
environment:
http_proxy:
https_proxy:
no_proxy:
SECRET_KEY: ${SECRET_KEY}
DATABASE_NAME: awx
DATABASE_USER: awx
DATABASE_PASSWORD: ${POSTGRES_PASSWORD}
DATABASE_HOST: postgres
DATABASE_PORT: 5432
REDIS_HOST: redis
REDIS_PORT: 6379
volumes:
- awx_data:/var/lib/awx
- ./logs:/var/log/tower
depends_on:
- postgres
- redis
networks:
- awx_network
awx_task:
image: quay.io/ansible/awx:24.6.1
container_name: awx_task
hostname: awx
user: root
restart: unless-stopped
environment:
http_proxy:
https_proxy:
no_proxy:
SECRET_KEY: ${SECRET_KEY}
DATABASE_NAME: awx
DATABASE_USER: awx
DATABASE_PASSWORD: ${POSTGRES_PASSWORD}
DATABASE_HOST: postgres
DATABASE_PORT: 5432
REDIS_HOST: redis
REDIS_PORT: 6379
RUN_ENVIRONMENT: docker
volumes:
- awx_data:/var/lib/awx
- ./logs:/var/log/tower
depends_on:
- postgres
- redis
- awx_web
networks:
- awx_network
command: /usr/bin/launch_awx_task.sh
volumes:
postgres_data:
redis_data:
awx_data:
networks:
awx_network:
driver: bridge
Deploy AWX stack with Docker Compose
Start all AWX services and verify they are running correctly.
docker-compose up -d
docker-compose logs -f awx_web
Wait for AWX initialization and create admin user
Monitor the logs until AWX completes initialization, then create the admin user.
# Wait for AWX to be ready (this may take 5-10 minutes)
docker-compose exec awx_task awx-manage check --database --display-configuration
Create admin user
docker-compose exec awx_task awx-manage createsuperuser --username admin --email admin@example.com
Configure enterprise RBAC with teams and organizations
Access AWX web interface
Open your browser and navigate to AWX to begin RBAC configuration.
# Check AWX is accessible
curl -I http://localhost:8080
Get admin password from environment file
grep AWX_ADMIN_PASSWORD /opt/awx/.env
Navigate to http://your-server-ip:8080 and log in with username 'admin' and the generated password.
Create organizations for multi-tenancy
Set up organizations to separate different business units or environments.
# Create organizations via AWX CLI
docker-compose exec awx_task awx organizations create --name "Production" --description "Production Environment"
docker-compose exec awx_task awx organizations create --name "Development" --description "Development Environment"
docker-compose exec awx_task awx organizations create --name "QA" --description "Quality Assurance Environment"
Create teams with specific roles
Define teams within organizations and assign appropriate permissions.
# Create teams for different roles
docker-compose exec awx_task awx teams create --name "Infrastructure Admins" --organization "Production"
docker-compose exec awx_task awx teams create --name "Application Deployers" --organization "Production"
docker-compose exec awx_task awx teams create --name "Developers" --organization "Development"
docker-compose exec awx_task awx teams create --name "QA Engineers" --organization "QA"
Create users and assign to teams
Add users with different privilege levels and assign them to appropriate teams.
# Create users
docker-compose exec awx_task awx users create --username infra_admin --email infra@example.com --first-name "Infrastructure" --last-name "Admin" --password "$(pwgen -s 16 1)"
docker-compose exec awx_task awx users create --username app_deployer --email deploy@example.com --first-name "App" --last-name "Deployer" --password "$(pwgen -s 16 1)"
docker-compose exec awx_task awx users create --username developer --email dev@example.com --first-name "Dev" --last-name "User" --password "$(pwgen -s 16 1)"
Configure role-based permissions
Assign specific roles to teams to control access to resources and operations.
# Grant admin role to Infrastructure Admins team
docker-compose exec awx_task awx role grants create --team "Infrastructure Admins" --role "Admin" --organization "Production"
Grant execute role to Application Deployers
docker-compose exec awx_task awx role grants create --team "Application Deployers" --role "Execute" --organization "Production"
Grant read role to Developers
docker-compose exec awx_task awx role grants create --team "Developers" --role "Read" --organization "Development"
Set up dynamic inventory management
Create inventory sources for AWS EC2
Configure dynamic inventory to automatically discover AWS EC2 instances.
# Create AWS inventory
docker-compose exec awx_task awx inventory create --name "AWS Production" --organization "Production"
Add AWS EC2 source
docker-compose exec awx_task awx inventory_sources create --name "AWS EC2" --inventory "AWS Production" --source "ec2" --credential "AWS Credentials"
Configure VMware vCenter inventory source
Set up dynamic inventory for VMware virtual machines.
# Create VMware inventory
docker-compose exec awx_task awx inventory create --name "VMware Infrastructure" --organization "Production"
Configure vCenter source
docker-compose exec awx_task awx inventory_sources create --name "vCenter VMs" --inventory "VMware Infrastructure" --source "vmware" --credential "VMware Credentials"
Set up network device inventory from NetBox
Configure NetBox as an inventory source for network infrastructure.
# Create network inventory
docker-compose exec awx_task awx inventory create --name "Network Devices" --organization "Production"
Add NetBox source
docker-compose exec awx_task awx inventory_sources create --name "NetBox Devices" --inventory "Network Devices" --source "netbox" --credential "NetBox API"
Configure inventory synchronization schedules
Set up automatic inventory updates to keep device lists current.
# Create hourly sync schedule for AWS
docker-compose exec awx_task awx schedules create --name "AWS Hourly Sync" --inventory-source "AWS EC2" --rrule "DTSTART:20240101T000000Z RRULE:FREQ=HOURLY;INTERVAL=1"
Create daily sync for VMware
docker-compose exec awx_task awx schedules create --name "VMware Daily Sync" --inventory-source "vCenter VMs" --rrule "DTSTART:20240101T060000Z RRULE:FREQ=DAILY;INTERVAL=1"
Implement job templates and workflow automation
Create project for Ansible playbooks
Set up a Git-based project to store your Ansible automation content.
# Create project from Git repository
docker-compose exec awx_task awx projects create --name "Infrastructure Playbooks" --organization "Production" --scm-type "git" --scm-url "https://github.com/your-org/ansible-playbooks.git" --scm-branch "main"
Configure job templates for common tasks
Create reusable job templates for system maintenance and application deployment.
# Create system update job template
docker-compose exec awx_task awx job_templates create --name "System Updates" --job-type "run" --inventory "AWS Production" --project "Infrastructure Playbooks" --playbook "system-update.yml" --credential "SSH Key"
Create application deployment template
docker-compose exec awx_task awx job_templates create --name "Deploy Application" --job-type "run" --inventory "AWS Production" --project "Infrastructure Playbooks" --playbook "deploy-app.yml" --credential "SSH Key"
Create workflow templates for complex automation
Build workflows that chain multiple job templates with conditional logic.
# Create deployment workflow
docker-compose exec awx_task awx workflow_job_templates create --name "Full Application Deployment" --organization "Production" --description "Complete deployment workflow with rollback"
Configure notification templates
Set up email and Slack notifications for job completion and failure alerts.
# Create email notification
docker-compose exec awx_task awx notification_templates create --name "Email Alerts" --notification-type "email" --organization "Production" --host "smtp.example.com" --port "587" --username "alerts@example.com" --password "smtp_password"
Create Slack notification
docker-compose exec awx_task awx notification_templates create --name "Slack Alerts" --notification-type "slack" --organization "Production" --token "xoxb-slack-bot-token" --channels "#ops-alerts"
Enable notifications on job templates
Associate notification templates with job templates for automated alerting.
# Enable success and failure notifications
docker-compose exec awx_task awx job_templates associate --job-template "Deploy Application" --notification-template-success "Email Alerts"
docker-compose exec awx_task awx job_templates associate --job-template "Deploy Application" --notification-template-error "Slack Alerts"
Configure SSL/TLS with reverse proxy
Set up NGINX reverse proxy for SSL termination and production deployment.
server {
listen 80;
server_name awx.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name awx.example.com;
ssl_certificate /etc/ssl/certs/awx.crt;
ssl_certificate_key /etc/ssl/private/awx.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_send_timeout 300;
}
location /websocket/ {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Origin "";
}
}
Enable NGINX configuration and restart
Activate the AWX site configuration and restart NGINX.
sudo ln -s /etc/nginx/sites-available/awx /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
Verify your setup
# Check all containers are running
docker-compose ps
Verify AWX services
docker-compose exec awx_task awx-manage check --database
Test web interface
curl -I https://awx.example.com
Check inventory sources
docker-compose exec awx_task awx inventory_sources list
Verify job templates
docker-compose exec awx_task awx job_templates list
Test notifications
docker-compose exec awx_task awx notification_templates list
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| AWX web interface not loading | Services still initializing | Wait 10 minutes and check docker-compose logs awx_web |
| Database connection errors | PostgreSQL not ready | Restart services: docker-compose restart |
| Inventory sync failures | Invalid credentials | Verify cloud provider credentials in AWX interface |
| Job execution failures | SSH key authentication | Add valid SSH credentials to AWX credential store |
| Notification not sending | SMTP/API configuration | Test notification template from AWX web interface |
| Permission denied errors | Incorrect RBAC setup | Review user roles and team assignments |
| Websocket connection issues | Proxy configuration | Check NGINX proxy settings for websocket support |
chown and minimal permissions like 755 for directories and 644 for files.Next steps
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Default values
AWX_PORT="${1:-8080}"
AWX_DIR="/opt/awx"
# Usage message
usage() {
echo "Usage: $0 [port]"
echo " port: AWX web interface port (default: 8080)"
exit 1
}
# Validation
if [[ $# -gt 1 ]]; then
usage
fi
if [[ "$AWX_PORT" =~ ^[0-9]+$ ]] && [[ "$AWX_PORT" -ge 1024 ]] && [[ "$AWX_PORT" -le 65535 ]]; then
:
else
echo -e "${RED}Error: Port must be between 1024-65535${NC}"
exit 1
fi
# Error handling and cleanup
cleanup() {
echo -e "${RED}Installation failed. Cleaning up...${NC}"
cd /
if [[ -d "$AWX_DIR" ]]; then
sudo rm -rf "$AWX_DIR"
fi
docker-compose down 2>/dev/null || true
}
trap cleanup ERR
# Check if running as root
if [[ $EUID -eq 0 ]]; then
echo -e "${RED}Error: Do not run this script as root${NC}"
exit 1
fi
# Check sudo access
if ! sudo -n true 2>/dev/null; then
echo -e "${YELLOW}This script requires sudo access${NC}"
sudo -v
fi
# Auto-detect distribution
echo -e "${GREEN}[1/10] Detecting Linux distribution...${NC}"
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update && apt upgrade -y"
PKG_INSTALL="apt install -y"
DOCKER_PKG="docker.io docker-compose-v2"
FIREWALL_CMD="ufw"
;;
almalinux|rocky|centos|rhel|ol)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
DOCKER_PKG="docker docker-compose"
FIREWALL_CMD="firewall-cmd"
;;
fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
DOCKER_PKG="docker docker-compose"
FIREWALL_CMD="firewall-cmd"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
DOCKER_PKG="docker"
FIREWALL_CMD="firewall-cmd"
;;
*)
echo -e "${RED}Unsupported distribution: $ID${NC}"
exit 1
;;
esac
echo -e "${GREEN}Detected: $PRETTY_NAME${NC}"
else
echo -e "${RED}Cannot detect Linux distribution${NC}"
exit 1
fi
# Update system packages
echo -e "${GREEN}[2/10] Updating system packages...${NC}"
sudo $PKG_UPDATE
# Install prerequisites
echo -e "${GREEN}[3/10] Installing prerequisites...${NC}"
sudo $PKG_INSTALL curl git pwgen $DOCKER_PKG
# Install docker-compose if not available (Amazon Linux)
if [[ "$ID" == "amzn" ]]; then
if ! command -v docker-compose &> /dev/null; then
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod 755 /usr/local/bin/docker-compose
fi
fi
# Configure Docker service
echo -e "${GREEN}[4/10] Configuring Docker service...${NC}"
sudo systemctl enable docker
sudo systemctl start docker
sudo usermod -aG docker $USER
# Create AWX directory structure
echo -e "${GREEN}[5/10] Creating AWX directory structure...${NC}"
sudo mkdir -p $AWX_DIR/{data,logs,ssl}
sudo chown -R $USER:$USER $AWX_DIR
cd $AWX_DIR
# Generate secure passwords
echo -e "${GREEN}[6/10] Generating secure passwords...${NC}"
cat > .env << EOF
POSTGRES_PASSWORD=$(pwgen -s 32 1)
AWX_ADMIN_PASSWORD=$(pwgen -s 16 1)
SECRET_KEY=$(pwgen -s 50 1)
EOF
chmod 600 .env
# Create Docker Compose configuration
echo -e "${GREEN}[7/10] Creating Docker Compose configuration...${NC}"
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
postgres:
image: postgres:15
container_name: awx_postgres
environment:
POSTGRES_DB: awx
POSTGRES_USER: awx
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- postgres_data:/var/lib/postgresql/data/pgdata
restart: unless-stopped
networks:
- awx_network
redis:
image: redis:7-alpine
container_name: awx_redis
restart: unless-stopped
networks:
- awx_network
command: redis-server --appendonly yes
volumes:
- redis_data:/data
awx_web:
image: quay.io/ansible/awx:24.6.1
container_name: awx_web
hostname: awxweb
user: root
restart: unless-stopped
ports:
- "${AWX_PORT}:8052"
environment:
SECRET_KEY: ${SECRET_KEY}
DATABASE_NAME: awx
DATABASE_USER: awx
DATABASE_PASSWORD: ${POSTGRES_PASSWORD}
DATABASE_HOST: postgres
DATABASE_PORT: 5432
REDIS_HOST: redis
REDIS_PORT: 6379
volumes:
- awx_data:/var/lib/awx
- ./logs:/var/log/tower
depends_on:
- postgres
- redis
networks:
- awx_network
awx_task:
image: quay.io/ansible/awx:24.6.1
container_name: awx_task
hostname: awx
user: root
restart: unless-stopped
environment:
SECRET_KEY: ${SECRET_KEY}
DATABASE_NAME: awx
DATABASE_USER: awx
DATABASE_PASSWORD: ${POSTGRES_PASSWORD}
DATABASE_HOST: postgres
DATABASE_PORT: 5432
REDIS_HOST: redis
REDIS_PORT: 6379
RUN_ENVIRONMENT: docker
volumes:
- awx_data:/var/lib/awx
- ./logs:/var/log/tower
depends_on:
- postgres
- redis
- awx_web
networks:
- awx_network
command: /usr/bin/launch_awx_task.sh
volumes:
postgres_data:
redis_data:
awx_data:
networks:
awx_network:
driver: bridge
EOF
# Update port in compose file
sed -i "s/\${AWX_PORT}/$AWX_PORT/g" docker-compose.yml
# Configure firewall
echo -e "${GREEN}[8/10] Configuring firewall...${NC}"
if [[ "$PKG_MGR" == "apt" ]]; then
if command -v ufw &> /dev/null; then
sudo ufw allow $AWX_PORT/tcp
fi
else
if command -v firewall-cmd &> /dev/null; then
sudo firewall-cmd --permanent --add-port=$AWX_PORT/tcp
sudo firewall-cmd --reload
fi
fi
# Deploy AWX stack
echo -e "${GREEN}[9/10] Deploying AWX stack...${NC}"
newgrp docker << COMMANDS
docker-compose up -d
COMMANDS
# Wait for services to start
echo -e "${YELLOW}Waiting for AWX services to start (this may take 2-3 minutes)...${NC}"
sleep 60
# Verification
echo -e "${GREEN}[10/10] Verifying installation...${NC}"
if docker-compose ps | grep -q "Up"; then
echo -e "${GREEN}✓ Docker containers are running${NC}"
else
echo -e "${RED}✗ Some containers failed to start${NC}"
exit 1
fi
# Display completion message
echo -e "${GREEN}AWX 24.6 installation completed successfully!${NC}"
echo -e "${YELLOW}Access AWX at: http://$(hostname -I | awk '{print $1}'):$AWX_PORT${NC}"
echo -e "${YELLOW}Default login: admin${NC}"
echo -e "${YELLOW}Password stored in: $AWX_DIR/.env${NC}"
echo ""
echo -e "${GREEN}Admin password:${NC}"
grep AWX_ADMIN_PASSWORD $AWX_DIR/.env | cut -d'=' -f2
Review the script before running. Execute with: bash install.sh