Deploy Ansible AWX with Docker Compose for centralized automation management. Configure enterprise RBAC, dynamic inventory sources, and workflow templates for scalable infrastructure orchestration across multiple environments.
Prerequisites
- Docker and Docker Compose installed
- 4GB RAM minimum
- 20GB disk space
- SSH access to target hosts
- Cloud provider credentials (optional)
What this solves
Ansible AWX provides a web-based interface and REST API for managing Ansible playbooks, inventories, and job scheduling at enterprise scale. This tutorial sets up AWX with comprehensive role-based access control, dynamic inventory management from cloud providers, and workflow templates for complex multi-stage automation pipelines.
Step-by-step installation
Update system packages
Start by updating your package manager and installing essential dependencies for Docker and AWX.
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget git python3-pip python3-venv docker.io docker-compose-plugin
Configure Docker service
Enable Docker service and add your user to the docker group for container management.
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
newgrp docker
Create AWX installation directory
Set up a dedicated directory structure for AWX configuration and persistent data storage.
sudo mkdir -p /opt/awx
sudo mkdir -p /opt/awx/data/projects
sudo mkdir -p /opt/awx/data/inventories
sudo chown -R $USER:$USER /opt/awx
cd /opt/awx
Download AWX Operator
Clone the official AWX Operator repository which provides Docker Compose deployment templates.
git clone https://github.com/ansible/awx-operator.git
cd awx-operator
Create AWX Docker Compose configuration
Generate the main Docker Compose file with PostgreSQL database, Redis cache, and AWX web service containers.
version: '3.8'
services:
postgres:
image: postgres:15
container_name: awx-postgres
environment:
POSTGRES_DB: awx
POSTGRES_USER: awx
POSTGRES_PASSWORD: awxpassword123
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- postgres_data:/var/lib/postgresql/data/pgdata
networks:
- awx-network
restart: unless-stopped
redis:
image: redis:7-alpine
container_name: awx-redis
command: redis-server --requirepass redispassword123
networks:
- awx-network
restart: unless-stopped
awx-web:
image: quay.io/ansible/awx:24.6.1
container_name: awx-web
hostname: awx-web
user: root
environment:
AWX_ADMIN_USER: admin
AWX_ADMIN_PASSWORD: adminpassword123
DATABASE_USER: awx
DATABASE_PASSWORD: awxpassword123
DATABASE_NAME: awx
DATABASE_HOST: postgres
DATABASE_PORT: 5432
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_PASSWORD: redispassword123
SECRET_KEY: awxsecretkey12345678901234567890
volumes:
- /opt/awx/data/projects:/var/lib/awx/projects
- /opt/awx/data/inventories:/var/lib/awx/inventories
- awx_data:/var/lib/awx
ports:
- "8080:8052"
depends_on:
- postgres
- redis
networks:
- awx-network
restart: unless-stopped
awx-task:
image: quay.io/ansible/awx:24.6.1
container_name: awx-task
hostname: awx-task
user: root
environment:
DATABASE_USER: awx
DATABASE_PASSWORD: awxpassword123
DATABASE_NAME: awx
DATABASE_HOST: postgres
DATABASE_PORT: 5432
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_PASSWORD: redispassword123
SECRET_KEY: awxsecretkey12345678901234567890
AWX_SKIP_MIGRATIONS: false
volumes:
- /opt/awx/data/projects:/var/lib/awx/projects
- /opt/awx/data/inventories:/var/lib/awx/inventories
- awx_data:/var/lib/awx
depends_on:
- postgres
- redis
networks:
- awx-network
restart: unless-stopped
command: ["/usr/bin/launch_awx_task.sh"]
volumes:
postgres_data:
awx_data:
networks:
awx-network:
driver: bridge
Deploy AWX containers
Start all AWX services and wait for the database initialization to complete.
docker compose up -d
docker compose logs -f awx-task
Configure firewall access
Open port 8080 for AWX web interface access while maintaining security.
sudo ufw allow 8080/tcp
sudo ufw reload
Configure enterprise RBAC
Access AWX web interface
Navigate to the AWX interface and complete initial login with the admin credentials.
echo "AWX URL: http://$(hostname -I | awk '{print $1}'):8080"
echo "Username: admin"
echo "Password: adminpassword123"
Create organizational structure
Set up organizations to separate different business units or environments with isolated access controls.
In the AWX web interface:
- Navigate to Access → Organizations
- Click Add to create a new organization
- Enter organization details:
Name: Production Environment
Description: Production infrastructure automation
Default Execution Environment: AWX EE (latest)
Max Hosts: 500
Repeat for additional organizations like "Staging Environment" and "Development Environment".
Configure user roles and permissions
Create role-based access control with granular permissions for different user types.
Create teams with specific roles:
# Infrastructure Team
Name: Infrastructure Admins
Organization: Production Environment
Permissions: Admin on all resources
Application Team
Name: Application Developers
Organization: Development Environment
Permissions: Execute on job templates, Read on inventories
Operations Team
Name: Operations Engineers
Organization: Production Environment
Permissions: Execute on job templates, Admin on credentials
Set up credential management
Configure secure credential storage for SSH keys, cloud providers, and vault integration.
Navigate to Resources → Credentials and create:
Name: Production SSH Key
Credential Type: Machine
Username: ansible
SSH Private Key: [paste your private key]
Privilege Escalation Method: sudo
Privilege Escalation Username: root
Name: AWS Production
Credential Type: Amazon Web Services
Access Key: AKIA...
Secret Key: [your secret key]
STS Token: [optional]
Configure dynamic inventory management
Set up AWS dynamic inventory
Configure automatic host discovery from AWS EC2 instances with dynamic grouping.
Navigate to Resources → Inventories → Add:
Name: AWS Production Inventory
Organization: Production Environment
Description: Dynamic EC2 instance discovery
Add inventory source:
Name: AWS EC2 Source
Source: Amazon EC2
Credential: AWS Production
Regions: us-east-1,us-west-2
Instance Filters: tag:Environment=production
Update Options:
- Overwrite
- Overwrite Variables
- Update on Launch
Cache Timeout: 3600
Configure inventory grouping variables
Set up host and group variables for dynamic inventory categorization and configuration.
# Web Servers Group Variables
ansible_user: ubuntu
ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
web_server_port: 80
nginx_worker_processes: auto
Database Servers Group Variables
ansible_user: ubuntu
db_port: 5432
postgresql_version: 15
max_connections: 200
Application Servers Group Variables
ansible_user: ubuntu
app_env: production
java_heap_size: 2g
tomcat_version: 10
Set up Azure and GCP inventory sources
Add additional cloud provider inventory sources for multi-cloud environments.
Name: Azure Production Source
Source: Microsoft Azure Resource Manager
Credential: Azure Service Principal
Subscription ID: your-subscription-id
Resource Groups: production-rg,staging-rg
Update Options:
- Overwrite
- Update on Launch
Create workflow templates and job orchestration
Create project for playbooks
Set up a Git-based project to store and version control your Ansible playbooks.
Navigate to Resources → Projects → Add:
Name: Infrastructure Playbooks
Organization: Production Environment
SCM Type: Git
SCM URL: https://github.com/your-org/ansible-playbooks.git
SCM Branch/Tag/Commit: main
Credential: GitHub Token
Update Options:
- Clean
- Delete on Update
- Update Revision on Launch
Cache Timeout: 300
Create job templates
Configure reusable job templates for common automation tasks with standardized parameters.
Name: Deploy Web Server
Job Type: Run
Inventory: AWS Production Inventory
Project: Infrastructure Playbooks
Playbook: playbooks/deploy-webserver.yml
Credential: Production SSH Key
Limit: tag_Role_webserver
Options:
- Prompt on Launch (Extra Variables)
- Enable Concurrent Jobs
- Use Fact Storage
Timeout: 1800
Forks: 10
Build workflow templates
Create complex multi-step workflows that orchestrate multiple job templates with conditional logic.
Navigate to Resources → Workflow Templates → Add:
Name: Complete Application Deployment
Organization: Production Environment
Description: Full application deployment with database migration
Options:
- Enable Concurrent Jobs
- Ask for Inventory on Launch
Configure workflow nodes:
1. Database Backup
Job Template: Backup Production DB
On Success: → Database Migration
On Failure: → Send Alert
- Database Migration
Job Template: Run DB Migration
On Success: → Deploy Application
On Failure: → Restore Database
- Deploy Application
Job Template: Deploy Web Server
On Success: → Health Check
On Failure: → Rollback Deployment
- Health Check
Job Template: Application Health Check
On Success: → Update Load Balancer
On Failure: → Rollback Deployment
- Update Load Balancer
Job Template: Configure HAProxy
On Success: → Send Success Notification
Configure job scheduling
Set up automated job execution schedules for routine maintenance and compliance tasks.
Navigate to a job template and add schedules:
# Daily Security Updates
Name: Daily Security Updates
Start Date/Time: Today 02:00 AM
Local Time Zone: UTC
Repeat Frequency: Daily
Run Every: 1 Day
Days of Data to Keep: 30
Weekly Backup
Name: Weekly Full Backup
Start Date/Time: Sunday 01:00 AM
Local Time Zone: UTC
Repeat Frequency: Weekly
Run Every: 1 Week
On Days: Sunday
Set up notification channels
Configure Slack, email, and webhook notifications for job status updates and alerts.
Name: Infrastructure Alerts
Notification Type: Slack
Slack Webhook URL: https://hooks.slack.com/services/...
Slack Channel: #infrastructure
Slack Username: AWX Bot
Notification Messages:
- Job Start: Started job {{ job_friendly_name }}
- Job Success: ✅ {{ job_friendly_name }} completed successfully
- Job Failure: ❌ {{ job_friendly_name }} failed: {{ job_result_stdout }}
Integrate with Ansible Vault
Configure Vault integration
Set up secure secret management using Ansible Vault for sensitive variables and credentials. This builds on the foundation covered in our Ansible Vault configuration guide.
Name: Production Vault Password
Credential Type: Vault
Vault Password: your-vault-password
Vault Identifier: production
Apply vault credentials to job templates that use encrypted variables.
Set up HashiCorp Vault integration
Configure external HashiCorp Vault integration for enterprise secret management.
Name: HashiCorp Vault Production
Credential Type: HashiCorp Vault Secret Lookup
Server URL: https://vault.example.com:8200
Token: s.yourvaulttoken
CACert: [optional CA certificate]
Role ID: [for AppRole authentication]
Configure logging and monitoring
Enable external logging
Configure centralized logging to external systems for audit trails and troubleshooting.
# Add logging configuration to awx-web service
logging:
driver: syslog
options:
syslog-address: "tcp://your-log-server:514"
tag: "awx-web"
Add logging configuration to awx-task service
logging:
driver: syslog
options:
syslog-address: "tcp://your-log-server:514"
tag: "awx-task"
Set up monitoring integration
Configure Prometheus metrics export for comprehensive AWX monitoring and alerting.
# In AWX web interface: Settings → System
Prometheus Metrics Enabled: True
Metrics Export Path: /api/v2/metrics/
Metrics Authentication: Token
Add to docker-compose.yml awx-web service
environment:
ENABLE_METRICS: "True"
METRICS_PATH: "/api/v2/metrics/"
Verify your setup
# Check AWX container status
docker compose ps
View AWX logs
docker compose logs awx-web --tail 50
Test AWX API access
curl -k -u admin:adminpassword123 http://localhost:8080/api/v2/ping/
Check database connection
docker exec awx-postgres psql -U awx -d awx -c "SELECT version();"
Verify inventory sync
curl -k -u admin:adminpassword123 http://localhost:8080/api/v2/inventories/
Test job template execution
curl -k -X POST -u admin:adminpassword123 \
-H "Content-Type: application/json" \
http://localhost:8080/api/v2/job_templates/1/launch/
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| AWX web interface not accessible | Container startup failure or port conflict | docker compose logs awx-web and check port 8080 availability |
| Database connection errors | PostgreSQL container not ready or wrong credentials | Verify postgres container status and environment variables |
| Job templates fail with permission denied | SSH key permissions or sudo configuration issues | Set SSH key permissions to 600, configure passwordless sudo |
| Dynamic inventory not updating | Cloud provider credentials or API permissions | Test credentials and verify IAM permissions for resource discovery |
| Workflow templates not executing | Node dependency configuration or credential assignment | Check workflow visualizer and verify all nodes have required credentials |
| High memory usage | Large inventory sizes or concurrent job execution | Increase container memory limits and tune job concurrency settings |
Next steps
- Configure Ansible Vault for secret management - Secure credential handling
- Integrate Jaeger with Kubernetes - Application performance monitoring
- Configure AWX backup automation - Database and configuration backups
- Set up AWX high availability clustering - Multi-node AWX deployment
- Integrate AWX with GitLab CI/CD - Automated deployment pipelines
Running this in production?
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'
BLUE='\033[0;34m'
NC='\033[0m'
# Default values
AWX_DOMAIN="${1:-localhost}"
ADMIN_PASSWORD="${2:-$(openssl rand -base64 32)}"
DB_PASSWORD="$(openssl rand -base64 32)"
REDIS_PASSWORD="$(openssl rand -base64 32)"
SECRET_KEY="$(openssl rand -base64 48)"
# Usage function
usage() {
echo "Usage: $0 [domain] [admin_password]"
echo " domain: Domain name for AWX (default: localhost)"
echo " admin_password: Admin password (default: auto-generated)"
exit 1
}
# Logging functions
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
# Cleanup function
cleanup() {
log_error "Installation failed. Cleaning up..."
cd /tmp
if [ -d "/opt/awx" ]; then
docker compose -f /opt/awx/docker-compose.yml down 2>/dev/null || true
sudo rm -rf /opt/awx
fi
exit 1
}
# Set trap for cleanup on error
trap cleanup ERR
# Check if running as root or with sudo
if [ "$EUID" -eq 0 ]; then
log_error "Please run this script as a regular user with sudo privileges"
exit 1
fi
if ! sudo -n true 2>/dev/null; then
log_error "This script requires sudo privileges"
exit 1
fi
# Auto-detect distribution
if [ ! -f /etc/os-release ]; then
log_error "/etc/os-release not found. Cannot detect distribution."
exit 1
fi
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update && apt upgrade -y"
PKG_INSTALL="apt install -y"
DOCKER_COMPOSE_PKG="docker-compose-plugin"
FIREWALL_CMD="ufw"
;;
almalinux|rocky|centos|rhel|ol)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
DOCKER_COMPOSE_PKG="docker-compose-plugin"
FIREWALL_CMD="firewall-cmd"
;;
fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
DOCKER_COMPOSE_PKG="docker-compose-plugin"
FIREWALL_CMD="firewall-cmd"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
DOCKER_COMPOSE_PKG="docker-compose"
FIREWALL_CMD="firewall-cmd"
;;
*)
log_error "Unsupported distribution: $ID"
exit 1
;;
esac
log_info "Detected distribution: $PRETTY_NAME"
# Step 1: Update system packages
echo -e "\n${BLUE}[1/8] Updating system packages...${NC}"
sudo $PKG_UPDATE
# Step 2: Install dependencies
echo -e "\n${BLUE}[2/8] Installing dependencies...${NC}"
if [ "$ID" = "ubuntu" ] || [ "$ID" = "debian" ]; then
sudo $PKG_INSTALL curl wget git python3-pip python3-venv docker.io $DOCKER_COMPOSE_PKG openssl
else
sudo $PKG_INSTALL curl wget git python3-pip python3-virtualenv docker $DOCKER_COMPOSE_PKG openssl
fi
# Step 3: Configure Docker
echo -e "\n${BLUE}[3/8] Configuring Docker service...${NC}"
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
# Check if user is already in docker group in current session
if ! groups | grep -q docker; then
log_warning "User added to docker group. You may need to log out and back in for changes to take effect."
fi
# Step 4: Configure firewall
echo -e "\n${BLUE}[4/8] Configuring firewall...${NC}"
if command -v ufw >/dev/null 2>&1; then
sudo ufw allow 8080/tcp
sudo ufw --force enable 2>/dev/null || true
elif command -v firewall-cmd >/dev/null 2>&1; then
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload
fi
# Step 5: Create AWX directories
echo -e "\n${BLUE}[5/8] Creating AWX directories...${NC}"
sudo mkdir -p /opt/awx/{data/{projects,inventories},certs}
sudo chown -R $USER:$USER /opt/awx
# Step 6: Create Docker Compose configuration
echo -e "\n${BLUE}[6/8] Creating Docker Compose configuration...${NC}"
cat > /opt/awx/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: ${DB_PASSWORD}
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- postgres_data:/var/lib/postgresql/data/pgdata
networks:
- awx-network
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U awx"]
interval: 30s
timeout: 10s
retries: 3
redis:
image: redis:7-alpine
container_name: awx-redis
command: redis-server --requirepass ${REDIS_PASSWORD}
networks:
- awx-network
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "auth", "${REDIS_PASSWORD}", "ping"]
interval: 30s
timeout: 10s
retries: 3
awx-web:
image: quay.io/ansible/awx:24.6.1
container_name: awx-web
hostname: awx-web
user: root
environment:
AWX_ADMIN_USER: admin
AWX_ADMIN_PASSWORD: ${ADMIN_PASSWORD}
DATABASE_USER: awx
DATABASE_PASSWORD: ${DB_PASSWORD}
DATABASE_NAME: awx
DATABASE_HOST: postgres
DATABASE_PORT: 5432
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_PASSWORD: ${REDIS_PASSWORD}
SECRET_KEY: ${SECRET_KEY}
volumes:
- /opt/awx/data/projects:/var/lib/awx/projects:Z
- /opt/awx/data/inventories:/var/lib/awx/inventories:Z
- awx_data:/var/lib/awx:Z
ports:
- "8080:8052"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- awx-network
restart: unless-stopped
awx-task:
image: quay.io/ansible/awx:24.6.1
container_name: awx-task
hostname: awx-task
user: root
environment:
DATABASE_USER: awx
DATABASE_PASSWORD: ${DB_PASSWORD}
DATABASE_NAME: awx
DATABASE_HOST: postgres
DATABASE_PORT: 5432
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_PASSWORD: ${REDIS_PASSWORD}
SECRET_KEY: ${SECRET_KEY}
AWX_SKIP_MIGRATIONS: false
volumes:
- /opt/awx/data/projects:/var/lib/awx/projects:Z
- /opt/awx/data/inventories:/var/lib/awx/inventories:Z
- awx_data:/var/lib/awx:Z
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- awx-network
restart: unless-stopped
command: ["/usr/bin/launch_awx_task.sh"]
volumes:
postgres_data:
awx_data:
networks:
awx-network:
driver: bridge
EOF
chmod 644 /opt/awx/docker-compose.yml
# Step 7: Deploy AWX containers
echo -e "\n${BLUE}[7/8] Deploying AWX containers...${NC}"
cd /opt/awx
# Pull images first
docker compose pull
# Start services
docker compose up -d
log_info "Waiting for AWX to initialize (this may take 5-10 minutes)..."
# Wait for AWX web interface to be ready
timeout=600
elapsed=0
while [ $elapsed -lt $timeout ]; do
if curl -s -o /dev/null -w "%{http_code}" http://localhost:8080 | grep -q "200\|302"; then
break
fi
sleep 10
elapsed=$((elapsed + 10))
echo "Waiting for AWX... ($elapsed/${timeout}s)"
done
if [ $elapsed -ge $timeout ]; then
log_error "AWX failed to start within timeout period"
exit 1
fi
# Step 8: Verification
echo -e "\n${BLUE}[8/8] Performing verification checks...${NC}"
# Check container status
if ! docker compose ps | grep -q "running"; then
log_error "Some AWX containers are not running"
docker compose ps
exit 1
fi
# Check web interface
if ! curl -s -o /dev/null -w "%{http_code}" http://localhost:8080 | grep -q "200\|302"; then
log_error "AWX web interface is not accessible"
exit 1
fi
# Save credentials
cat > /opt/awx/credentials.txt << EOF
AWX Installation Completed Successfully!
Web Interface: http://${AWX_DOMAIN}:8080
Admin Username: admin
Admin Password: ${ADMIN_PASSWORD}
Database Password: ${DB_PASSWORD}
Redis Password: ${REDIS_PASSWORD}
Secret Key: ${SECRET_KEY}
Installation Date: $(date)
EOF
chmod 600 /opt/awx/credentials.txt
log_success "AWX installation completed successfully!"
echo ""
log_info "AWX Web Interface: http://${AWX_DOMAIN}:8080"
log_info "Admin Username: admin"
log_info "Admin Password: ${ADMIN_PASSWORD}"
echo ""
log_info "Credentials saved to: /opt/awx/credentials.txt"
log_warning "Please secure the credentials file and change default passwords!"
Review the script before running. Execute with: bash install.sh