Status: COMPLETED. This plan was executed and all phases were delivered as of v2026.04.0. This document is retained for historical reference only. For the current state, see Release Notes v2026.04.0.
Latest release: AWX 24.6.1 (July 2, 2024) Status: Releases are paused. Red Hat is refactoring AWX into a service-oriented architecture. Devel branch: Active, already uses Python 3.12, Django 5.2.8, dispatcherd instead of Celery. Alternative: Ascender (fork by CIQ/Rocky Linux team) - version 25.3.3 (Feb 2026).
| Problem | Description |
|---|---|
| OpenSSL pin | Dockerfile pins openssl-3.0.7 which no longer exists in CentOS Stream 9 repositories |
| Django conflict | django-ansible-base requires Django >=4.2.16, but AWX pins 4.2.10 |
| Python 3.12.8+ crash | argparse._parse_known_args() got a new intermixed parameter, breaks AWX CLI |
| Missing VERSION file | Build from source fails without a VERSION file in the root |
| Node.js 18 | Entering maintenance LTS, needs migration to Node.js 20+ |
# Fork the AWX repository
git clone https://github.com/ansible/awx.git awx-fork
cd awx-fork
git checkout -b modernization 24.6.1
# Fork the AWX Operator repository
git clone https://github.com/ansible/awx-operator.git awx-operator-forkBefore any changes, ensure the original build works:
# Build development image
make docker-compose-build
# Run tests
make docker-compose-test
# Run unit tests
make test_unitDocument all errors that appear during the 24.6.1 build as these are the first bugs to fix.
Set up the GitHub Actions pipeline with:
- Build matrix: Python 3.11 / 3.12 / 3.13
- OS matrix: CentOS Stream 9 / Ubuntu 24.04
- Automatic unit tests on every push
- Container image build and scanning (Trivy/Grype)
Goal: Make AWX 24.6.1 actually buildable and runnable.
File: tools/ansible/roles/dockerfile/templates/Dockerfile.j2
- openssl-3.0.7 \
+ openssl \Remove the hard-coded version and use the one from the repository.
File: requirements/requirements.txt
- Django==4.2.10
+ Django==4.2.16File: requirements/requirements.txt
- sqlparse==0.5.1
+ sqlparse==0.5.2Update django-ansible-base to a compatible version. Cherry-pick the fix from PR #15596.
File: awx/main/utils/common.py (or wherever HelpfulArgumentParser is located)
Cherry-pick the fix from PR #15692 - add the intermixed parameter to the override method:
# Before (broken on Python 3.12.8+):
def _parse_known_args(self, arg_strings, namespace):
# After:
def _parse_known_args(self, arg_strings, namespace, intermixed=False):echo "24.6.2-custom" > VERSIONmake docker-compose-build # Must pass without errors
make docker-compose # Must start up
make test_unit # All tests must passGoal: Move from CentOS Stream 9 to a more modern base image.
Create an alternative Dockerfile for Ubuntu 24.04:
File: tools/ansible/roles/dockerfile/templates/Dockerfile.ubuntu.j2
Key differences compared to CentOS:
aptinstead ofdnf- Different package names (e.g.,
libpq-devinstead ofpostgresql-devel) - Python 3.12 comes from the system (no need for
dnf module) - Node.js 20 from NodeSource repository
FROM ubuntu:24.04 AS base
RUN apt-get update && apt-get install -y \
python3.12 python3.12-venv python3.12-dev \
python3-pip \
libpq-dev libxml2-dev libxslt1-dev \
libffi-dev libssl-dev \
git curl wget \
nginx \
&& rm -rf /var/lib/apt/lists/*# Add to Makefile
BASE_IMAGE ?= centos # or ubuntu
ifeq ($(BASE_IMAGE),ubuntu)
DOCKERFILE_TEMPLATE = Dockerfile.ubuntu.j2
else
DOCKERFILE_TEMPLATE = Dockerfile.j2
endifAdd docker buildx support for AMD64 and ARM64:
make docker-compose-buildx ARCH="linux/amd64,linux/arm64"Goal: Update all dependencies to the latest compatible versions.
Do NOT update everything at once. Work in groups with testing after each group:
| Priority | Group | Packages |
|---|---|---|
| 1 | Core Framework | Django 4.2.16 → 5.2.x, DRF, Channels, Daphne |
| 2 | Database and Cache | psycopg, redis, hiredis |
| 3 | Cryptography | cryptography, pyopenssl, pyjwt, pynacl |
| 4 | Async/Network | aiohttp, twisted, autobahn |
| 5 | Cloud Providers | boto3, azure-*, kubernetes, openshift |
| 6 | Observability | opentelemetry-*, prometheus-client |
| 7 | Other | all remaining ~130 packages |
This is the most critical change. I suggest a two-step approach:
Step A: Django 4.2.10 → 4.2.16 (minor bump, minimal risk)
- Fixes the dependency conflict with
django-ansible-base - Does not introduce breaking changes (LTS version)
- Test:
make test_unit && make test_coverage
Step B: Django 4.2.16 → 5.2.x (major bump, higher risk)
- Needed only if Python 3.13 support is desired
- Breaking changes in Django 5.x:
DEFAULT_AUTO_FIELDmust be explicitly set- Deprecated
django.utils.timezone.utc(usedatetime.timezone.utc) - Changes in
FormandModelFormrendering HttpResponse.headersdictionary access instead of__setitem__
- Requires review of every AWX Django model and view
# In Dockerfile template:
- dnf module enable nodejs:18
+ # For CentOS:
+ dnf module enable nodejs:20
+ # For Ubuntu:
+ curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
+ apt-get install -y nodejsRun UI build and verify:
make clean/ui uiUse pip-audit for security checks:
pip install pip-audit
pip-audit -r requirements/requirements.txtUse pip-compile (from pip-tools) for consistent resolution:
pip install pip-tools
pip-compile requirements/requirements.in --output-file requirements/requirements.txtGoal: Create a more official docker-compose for production since that is what the community demands.
┌─────────┐
│ Nginx │:443/:80
└────┬────┘
│
┌──────────┼──────────┐
│ │ │
┌─────┴────┐ ┌──┴───┐ ┌───┴─────┐
│ AWX Web │ │ AWX │ │ AWX │
│ (API/UI) │ │ Task │ │Receptor │
└─────┬────┘ └──┬───┘ └───┬─────┘
│ │ │
┌─────────┼─────────┼─────────┘
│ │ │
┌───┴────┐ ┌─┴──┐ ┌───┴────────┐
│Postgres│ │Redis│ │ Receptor │
│ 15 │ │ 7 │ │ Worker(s) │
└────────┘ └────┘ └────────────┘
awx-docker-prod/
├── docker-compose.yml # Main compose file
├── docker-compose.override.yml # Local overrides
├── .env # Environment variables
├── nginx/
│ ├── nginx.conf # Nginx configuration
│ └── ssl/ # TLS certificates
├── settings/
│ ├── settings.py # AWX custom settings
│ ├── credentials.py # Credential configuration
│ └── receptor.conf # Receptor configuration
├── backup/
│ └── backup.sh # Backup script for PostgreSQL
└── scripts/
├── init.sh # Initialization (migrations, admin user)
├── healthcheck.sh # Health check script
└── upgrade.sh # Upgrade procedure
Create a custom settings.py that AWX loads:
# settings/settings.py
DATABASES = {
'default': {
'ATOMIC_REQUESTS': True,
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('DATABASE_NAME', 'awx'),
'USER': os.environ.get('DATABASE_USER', 'awx'),
'PASSWORD': os.environ.get('DATABASE_PASSWORD', ''),
'HOST': os.environ.get('DATABASE_HOST', 'postgres'),
'PORT': os.environ.get('DATABASE_PORT', '5432'),
}
}
CACHES = {
'default': {
'BACKEND': 'awx.main.cache.AWXRedisCache',
'LOCATION': 'redis://redis:6379/1',
}
}
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
'hosts': [('redis', 6379)],
'capacity': 10000,
},
},
}
BROADCAST_WEBSOCKET_PORT = 8052
BROADCAST_WEBSOCKET_PROTOCOL = 'http'#!/bin/bash
# backup/backup.sh
BACKUP_DIR="/backups/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
# PostgreSQL dump
docker compose exec -T postgres pg_dump -U awx awx | gzip > "$BACKUP_DIR/awx_db.sql.gz"
# AWX secret key
cp .env "$BACKUP_DIR/env.backup"
# Projects
tar czf "$BACKUP_DIR/projects.tar.gz" -C volumes/ projects/
echo "Backup saved to: $BACKUP_DIR"| Test Type | Tool | What is Tested |
|---|---|---|
| Unit | pytest | API, models, serializers, utility functions |
| Integration | pytest + Docker | Full stack with real database and Redis |
| UI | Cypress/Playwright | Frontend workflows |
| API | pytest + requests | REST API endpoints |
| Performance | locust/k6 | Concurrent users, job throughput |
| Security | pip-audit, Trivy | CVE scanning of dependencies and images |
| Compatibility | tox | Python 3.11, 3.12, 3.13 |
Before each release, the following MUST work:
- Build:
make docker-compose-buildwithout errors on both base images - Start:
docker compose upstarts all services without crashes - Login: Admin can log in to the Web UI
- Job: An Ansible playbook can be created and run
- Inventory: An inventory can be added and synced (static + dynamic)
- Credentials: A credential can be created and used
- API: All CRUD endpoints work (
/api/v2/) - WebSocket: Real-time log streaming works during jobs
- Backup/Restore: PostgreSQL dump and restore work
- Upgrade: Migration from the previous version works without data loss
Establish a performance baseline on standard hardware (4 CPU, 8GB RAM):
| Metric | Target |
|---|---|
| Startup time | < 60 seconds |
| Login response | < 500ms |
| API listing (100 items) | < 1s |
| Job launch latency | < 5s |
| Concurrent users | >= 20 |
| Concurrent jobs | >= 10 |
I suggest CalVer (calendar-based) since Red Hat plans the same:
Format: YYYY.MM.PATCH
Example: 2026.03.0, 2026.03.1 (hotfix)
1. Feature freeze (one week before release)
2. Create release branch: release/2026.03
3. Run the full test matrix
4. Fix blocker bugs
5. Tag: git tag -a v2026.03.0
6. Build production images
7. Push to container registry (ghcr.io or quay.io)
8. Update AWX Operator with the new version
9. Write release notes
10. Publish on GitHub Releases
# Build and push
docker buildx build \
--platform linux/amd64,linux/arm64 \
--tag ghcr.io/USERNAME/awx:2026.03.0 \
--tag ghcr.io/USERNAME/awx:latest \
--push .The AWX devel branch is still being developed. We need to sync regularly:
# Add upstream remote
git remote add upstream https://github.com/ansible/awx.git
# Weekly sync
git fetch upstream devel
git log --oneline upstream/devel..HEAD # See differences
# Cherry-pick relevant commits
git cherry-pick <commit-hash>- Always: Security fixes (CVE)
- Always: Bug fixes for existing functionality
- Selectively: New features from the service architecture
- Carefully: Large refactoring commits (can break stability)
Send all generic fixes (not specific to our fork) as PRs upstream:
git checkout -b fix/openssl-pinning upstream/devel
# Make the fix
git push origin fix/openssl-pinning
# Create a PR on github.com/ansible/awx| Component | Minimum | Recommended |
|---|---|---|
| CPU | 4 cores | 8 cores |
| RAM | 8 GB | 16 GB |
| Disk | 40 GB SSD | 100 GB SSD |
| OS | Ubuntu 22.04+ / Rocky 9+ | Ubuntu 24.04 LTS |
| Docker | 24.0+ | 27.0+ |
| Docker Compose | v2.20+ | v2.30+ |
#!/bin/bash
# deploy.sh - Single-command deploy on a bare server
# 1. Install Docker
curl -fsSL https://get.docker.com | sh
systemctl enable docker --now
# 2. Clone our configuration
git clone https://github.com/USERNAME/awx-docker-prod.git /opt/awx
cd /opt/awx
# 3. Configuration
cp .env.example .env
# Edit .env with actual values
# 4. Start
docker compose up -d
# 5. Initialization
docker compose exec forail-web awx-manage migrate --noinput
docker compose exec forail-web awx-manage createsuperuser \
--username admin --email admin@example.com --noinput
docker compose exec forail-web awx-manage update_password \
--username admin --password "$ADMIN_PASSWORD"Add Prometheus metrics and Grafana dashboard:
# docker-compose.override.yml
services:
prometheus:
image: prom/prometheus:latest
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=adminAWX already has built-in OpenTelemetry and Prometheus support - it just needs scraping configured.
Before starting with our own fork, it is worth considering Ascender:
| Aspect | Our Fork | Ascender |
|---|---|---|
| Version | Based on 24.6.1 | 25.3.3 (Feb 2026) |
| Maintenance | By ourselves | CIQ team (Rocky Linux) |
| Base Image | CentOS/Ubuntu | Rocky Linux 9 |
| Deploy | Docker + K8s | K8s (multiple distros) + Single VM |
| Support | Community | Commercial option |
| Upstream sync | Manual | Regular |
| Risk | High (self-maintained) | Low (professional team) |
Recommendation: If the goal is just to run AWX on a new server, Ascender is a more pragmatic choice. If the goal is learning and full control, a custom fork makes sense.
Week 1-2: Phase 1 - Build stabilization ✓ COMPLETED
Week 2-3: Phase 2 - Rebranding (Forail) ✓ COMPLETED
Week 3-6: Phase 3 - Dependency modernization ✓ COMPLETED
Week 6-12: Phase 4 - Backend refactoring ✓ COMPLETED
Week 12-18: Phase 5 - Frontend refactoring ✓ COMPLETED
Week 18-20: Phase 6 - Dockerfile modernization ✓ COMPLETED
Week 20-22: Phase 7 - Docker Compose production ✓ COMPLETED
Week 22-24: Phase 8 - Testing and QA ✓ COMPLETED
Week 24-25: Phase 9 - Release (2026.03.0) → IN PROGRESS
Total: ~20 weeks (5 months) for a single developer. With a team of 2-3 people, it can be reduced to 2-3 months.