Ansible Builder — Build Custom Execution Environments

Introduction

Ansible Builder (ansible-builder) is a command-line tool that creates container images called Execution Environments (EEs). EEs package ansible-core, collections, Python libraries, and system dependencies into a single container — ensuring consistent playbook execution across development, CI/CD, and production.

Install ansible-builder

# Install via pip
pip install ansible-builder

# Verify installation
ansible-builder --version

Execution Environment Definition

The execution-environment.yml file defines what goes into your EE:

---
version: 3

# Base image
images:
  base_image:
    name: quay.io/ansible/ansible-runner:latest

# Dependencies
dependencies:
  galaxy:
    collections:
      - amazon.aws >= 8.0.0
      - community.general >= 9.0.0
      - community.vmware >= 5.0.0
      - kubernetes.core >= 4.0.0
      - ansible.posix >= 1.6.0
  python:
    - boto3 >= 1.35.0
    - botocore >= 1.35.0
    - pyVmomi >= 8.0.3
    - kubernetes >= 30.0.0
    - jmespath >= 1.0.1
    - netaddr >= 1.0.0
  system:
    - openssh-clients [platform:rpm]
    - sshpass [platform:rpm]
    - openssh-client [platform:dpkg]
    - sshpass [platform:dpkg]

# Additional build steps
additional_build_steps:
  prepend_galaxy:
    - ENV ANSIBLE_GALAXY_CLI_COLLECTION_OPTS="--pre"
  prepend_final:
    - RUN whoami
    - RUN pip3 install --upgrade pip
  append_final:
    - RUN ansible --version
    - LABEL maintainer="your-team@example.com"

# Build options
build_arg_defaults:
  ANSIBLE_GALAXY_CLI_COLLECTION_OPTS: "--pre"

Build the EE Image

# Basic build
ansible-builder build -t my-ee:latest

# Build with specific tag
ansible-builder build -t registry.example.com/ansible/my-ee:1.0.0

# Build with verbose output
ansible-builder build -t my-ee:latest -v 3

# Build using a specific container runtime
ansible-builder build -t my-ee:latest --container-runtime docker
ansible-builder build -t my-ee:latest --container-runtime podman

# Build from a specific file
ansible-builder build -t my-ee:latest -f execution-environment.yml

# Create Containerfile without building
ansible-builder create --file execution-environment.yml

EE Definition File Versions

# Version 1 (legacy)
---
version: 1
dependencies:
  galaxy: requirements.yml
  python: requirements.txt
  system: bindep.txt

# Version 3 (current — inline dependencies)
---
version: 3
dependencies:
  galaxy:
    collections:
      - name: amazon.aws
        version: ">=8.0.0"
  python:
    - boto3>=1.35.0
  system:
    - gcc [compile]

Use Separate Requirements Files

# execution-environment.yml
---
version: 3
dependencies:
  galaxy: requirements.yml
  python: requirements.txt
  system: bindep.txt
# requirements.yml
---
collections:
  - name: amazon.aws
    version: ">=8.0.0"
  - name: community.general
  - name: ansible.posix
# requirements.txt
boto3>=1.35.0
pyVmomi>=8.0.3
jmespath>=1.0.1
# bindep.txt
openssh-clients [platform:centos-9 platform:rhel-9]
sshpass [platform:centos-9 platform:rhel-9]
openssh-client [platform:debian platform:ubuntu]
gcc [compile platform:centos-9 platform:rhel-9]

Run Playbooks with Custom EE

# Using ansible-navigator
ansible-navigator run site.yml \
  --eei my-ee:latest \
  --mode stdout

# Using ansible-runner
ansible-runner run . \
  --container-image my-ee:latest \
  -p site.yml

# Push to registry
podman push my-ee:latest registry.example.com/ansible/my-ee:latest
docker push my-ee:latest registry.example.com/ansible/my-ee:latest

Multi-Stage Build Example

---
version: 3

images:
  base_image:
    name: quay.io/ansible/ansible-runner:latest

dependencies:
  galaxy:
    collections:
      - amazon.aws
      - community.general
      - community.vmware
      - kubernetes.core
      - ansible.posix
      - community.crypto
  python:
    - boto3
    - pyVmomi
    - kubernetes
    - cryptography
  system:
    - gcc [compile]
    - python3-devel [compile]
    - libffi-devel [compile]

additional_build_steps:
  append_final:
    - RUN pip3 cache purge
    - RUN rm -rf /tmp/*
    - RUN ansible-galaxy collection list

Inspect an EE

# List installed collections
podman run --rm my-ee:latest ansible-galaxy collection list

# Check ansible version
podman run --rm my-ee:latest ansible --version

# Check Python packages
podman run --rm my-ee:latest pip3 list

# Interactive shell
podman run --rm -it my-ee:latest /bin/bash

CI/CD Pipeline

# .github/workflows/build-ee.yml
name: Build Execution Environment
on:
  push:
    paths: ['execution-environment.yml', 'requirements.*', 'bindep.txt']

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install ansible-builder
        run: pip install ansible-builder
      - name: Build EE
        run: ansible-builder build -t my-ee:${{ github.sha }}
      - name: Push to GHCR
        run: |
          echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
          docker tag my-ee:${{ github.sha }} ghcr.io/${{ github.repository }}/my-ee:latest
          docker push ghcr.io/${{ github.repository }}/my-ee:latest

Troubleshooting

# Build fails on collection install
ansible-builder build -t my-ee:latest -v 3 2>&1 | grep -i error

# Galaxy timeout — use proxy or mirror
export ANSIBLE_GALAXY_SERVER_LIST=my_galaxy
ansible-builder build -t my-ee:latest

# Check generated Containerfile
ansible-builder create
cat context/Containerfile

# Disk space issues
podman system prune -a
docker system prune -a

Conclusion

ansible-builder build -t is the core command for creating Execution Environments. Define your dependencies in execution-environment.yml, build with a single command, and push to a container registry. EEs eliminate "works on my machine" problems and ensure every playbook run uses identical dependencies.