Ansible remains the default choice for configuration management and lightweight orchestration precisely because it hasn't changed its core model: SSH (or WinRM) plus Python, no agent to install, YAML playbooks that read close to plain English. What has changed by 2026 is the tooling around it — execution environments, the collection ecosystem, and how it fits into a GitOps-driven pipeline rather than being run ad hoc from a laptop.
Execution environments replace ad hoc Python setups
The old failure mode — "works on my machine" because of a locally installed collection or a Python library version mismatch — is what execution environments (container images built with ansible-builder, bundling Ansible core, a defined set of collections, and their Python dependencies) are designed to eliminate. Running playbooks through ansible-navigator against an execution environment means the automation runs identically whether it's triggered from a developer's laptop, a CI runner, or AWX/Ansible Automation Platform.
version: 3
images:
base_image:
name: quay.io/ansible/ansible-runner:latest
dependencies:
galaxy: requirements.yml
python: requirements.txt
system: bindep.txt
# requirements.yml
collections:
- name: community.general
version: ">=8.0.0"
- name: amazon.aws
version: ">=7.0.0"
Collections, not a monolithic core
Since the 2.10 split, Ansible ships as a thin core plus independently versioned collections (community.general, ansible.posix, cloud-vendor collections like amazon.aws). Pin collection versions in requirements.yml the same way you'd pin a package.json dependency — an unpinned ansible-galaxy collection install pulling the latest version of a cloud collection is a common source of a playbook that worked last month silently breaking this month.
Run ansible-lint in CI against every playbook change. It catches the class of bugs that are easy to miss by eye — missing become: true, unquoted Jinja expressions, tasks without a name, and use of deprecated module arguments — before they reach a target host.
Idempotency is still the design contract, not a nice-to-have
A playbook re-run against a host that's already in the desired state should report zero changes. This is what separates Ansible modules from a raw shell script and is the property that makes drift detection and safe re-runs possible. The most common way teams break it is reaching for ansible.builtin.shell or command where a proper module exists — a shell command has no built-in concept of "already done," so it runs and reports "changed" every time regardless of actual state.
When a shell or command task is genuinely unavoidable, the creates: or removes: argument gives you a cheap idempotency check by testing for a file's existence. It's not a substitute for a real module, but it's better than a task that reports "changed" on every run regardless of actual system state.
Ansible inside a GitOps pipeline
The 2026-era pattern is less "run ansible-playbook from a laptop" and more "a merge to main triggers a pipeline that runs the playbook against a defined inventory, with the run's output posted back to the PR." Ansible Automation Platform's job templates, or a plain CI runner invoking ansible-playbook inside the execution environment container, both fit this model — the important shift is that inventory and playbook changes go through the same review process as application code, not an out-of-band change.
Dynamic inventory over static files
Static hosts.ini files rot the moment infrastructure becomes elastic. Cloud inventory plugins (amazon.aws.aws_ec2, azure.azcollection.azure_rm) query the provider's API at run time and group hosts by tags automatically, so a playbook targeting tag_Environment_production stays correct as instances are created and terminated, with no inventory file to maintain by hand.
| Old habit | 2026 practice |
|---|---|
| Local pip install of ansible + collections | Execution environment container, versioned |
Unpinned ansible-galaxy install | Pinned versions in requirements.yml |
| Static hosts.ini | Dynamic cloud inventory plugin |
| Playbook run from a laptop | Triggered from CI/CD against reviewed changes |
Wrapping up
Ansible's core value proposition — agentless, idempotent, readable — hasn't moved. What's matured is the packaging around it: execution environments for reproducibility, pinned collections instead of a moving-target core, and running playbooks through the same review and CI gates as any other code change. Skip the execution environment step at your peril; it's the fix for the single most common "worked yesterday" bug report in Ansible-managed infrastructure.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.