ansible-runner: one interface to Ansible for scripts, CLIs and services
A tool and python library that helps when interfacing with Ansible directly or as part of another system whether that be through a container image interface, as a standalone tool, or as a Python module that can be imported. The goal is to provide a stable and consistent interface abstraction to Ansible.
At a glance
- What is it?
- Ansible Runner is an Ansible tool and Python library that gives other programs a consistent way to drive Ansible, either as a standalone command, a container image interface or an importable module. It is not the playbook engine itself, and the two release lines it maintains have to be chosen deliberately.
- Who is it for?
- Adopt ansible-runner when your system needs to launch Ansible from code rather than from a shell script that parses stdout, and when you want the same call to work whether it runs in process or in a container. Do not adopt it to replace Ansible itself, since the runner is an interface layer around the existing tooling and you still need Ansible present.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 42 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Three shapes of the same abstraction
Ansible Runner exists to sit between a program and Ansible. The stated goal is to provide a stable and consistent interface abstraction to Ansible, and the project offers that abstraction in three forms: as a standalone tool, as a container image interface, or as a Python module that can be imported.
Those three forms are for three different callers. The standalone tool is for a shell script, a CI job or a human at a terminal who wants to run a playbook and get structured results rather than scrolling output. The Python module is for a service that has to start a playbook, watch it and react to what happens, without shelling out and parsing free text. The container image interface is for environments where running Ansible in the same process space is not acceptable or not possible.
The audience is therefore infrastructure and platform engineers writing automation around Ansible, not end users writing playbooks. Someone with a good playbook and no surrounding system gains little here, because the runner is not where playbooks are authored. It is where playbooks are invoked from somewhere else.
The private data directory shape shown in demo/
The repository ships a demo/ directory, and its three subdirectories, env/, inventory/ and project/, show the layout the runner expects around an Ansible run. That directory is the unit of work: it holds the environment, the inventory and the project content, and the runner is pointed at a directory rather than at a playbook path.
This is the key structural idea, and it explains the rest of the design. Because the runner addresses a directory, the inputs to a run are files on disk, which means the same run can be handed to a container, captured as an artefact, or replayed later. The interface is file shaped rather than argument shaped.
The demo directory in the repository is the closest thing to a worked example, and it is deliberately small: three folders and no prose explanation in the README. The detailed usage rules are not in the README; they live in the documentation site, which the README links as the latest documentation at docs.ansible.com/projects/runner/en/latest, with a readthedocs badge and the project homepage ansible-runner.rtfd.io.
Process isolation and the container runtime
The package description is more specific than the README headline: it calls ansible-runner a consistent Ansible Python API and CLI with container and process isolation runtime capabilities. Isolation is the part that matters for an embedding service.
The dependency list gives a hint about the mechanism. The runtime dependencies are pexpect, packaging and pyyaml, and pexpect is a pseudo terminal and process control library, which is the piece you reach for when you drive a command line program and need to interact with it rather than simply wait for it to exit. Driving Ansible through a pseudo terminal is what lets the runner feed input, capture output and manage a long running run.
On top of that sit two isolation modes. The container image interface runs the same work in a container, where the Ansible process has its own filesystem boundary and its own view of the environment, which is the mode you want when the run is untrusted or when you need reproducible dependencies. The process isolation mode keeps everything in one environment but separates the run from the caller.
The practical consequence is that the same private data directory can be executed by different backends. That is what makes the abstraction worth having.
Installing from PyPI and the ansible-runner command
The README does not include an install command, so there is nothing to copy into a terminal here. The route it points at is the PyPI project page for ansible-runner, linked from the PyPI badge at the top of the README, and the usage details are in the linked documentation rather than in the repository.
What the repository does tell you about the package is in pyproject.toml. The build backend is setuptools with a setuptools-scm plugin, and the version is dynamic, meaning the version number is derived from the repository tags rather than written by hand:
[build-system]
requires = ["setuptools>=77.0.3, <=80.9.0", "setuptools-scm[toml]>=6.2, <=9.2.0"]
build-backend = "setuptools.build_meta"The minimum interpreter and the console entry point are also declared there:
requires-python = ">=3.10"[project.scripts]
ansible-runner = "ansible_runner.__main__:main"So after installation you get a command named ansible-runner, and the classifiers list Python 3.10 through 3.14 with a Python 3 only marker, and the operating system classifier is POSIX. The library is developed against a src/ layout with test/, pytest.ini, tox.ini, a .coveragerc and a .pre-commit-config.yaml, and the docs are built with .readthedocs.yaml.
Two release lines to choose between
The release history is the part that needs a decision rather than an upgrade. The recent tags are 2.3.7 from 2025-10-27, 2.4.3 from 2026-03-16 and 2.3.8 from 2026-04-21. Note the ordering: the higher minor version was published first, and the lower one was published afterwards.
That is the signature of parallel maintenance lines rather than a straight line of releases. 2.4.3 and 2.3.8 are not a step forward and back, they are the head of the 2.4 series and the head of the 2.3 series, each receiving its own patch releases on its own schedule. A project that pins ansible-runner therefore has to choose a line and keep pinning within it, and the choice of which line to sit on is a decision about which upstream development it wants to follow.
The repository is not archived and the last push was on 2026-09-10, so work is landing. The project also publishes its planning through GitHub Milestones, which the README describes as the place to track what is for the next release, and there is a SECURITY.md, a CODEOWNERS file and a .cherry_picker.toml for backporting fixes, which is the kind of infrastructure a project with two supported lines tends to accumulate.
The licence is declared twice and reads differently
The licence story needs care. In pyproject.toml the project declares license = "Apache-2.0" with license-files = ["LICENSE.md"], which is the modern PEP 639 style declaration supported by the pinned setuptools range. The file itself is named LICENSE.md rather than LICENSE, and GitHub does not classify it as one of the standard licences.
The discrepancy between a metadata field that says Apache-2.0 and a licence file the platform cannot categorise is not something to guess about. If your organisation has a licence allowlist that keys on the file or on the classifier, the LICENSE.md file is the artefact to check, and the Apache-2.0 string in the packaging metadata is a pointer to it rather than a substitute for reading it.
The same caution applies to what you redistribute. ansible-runner is an interface layer, but the Ansible it drives brings its own licensing and its own collection dependencies, and those are outside this repository. This is a note about which documents to read, not a legal opinion.
Why not call the ansible command yourself
The honest alternative is to skip the runner and invoke Ansible directly from a script, capturing its output. That approach has no extra dependency, and for a CI job that runs one playbook and checks the exit status, it is enough.
The difference shows up in three places. First, structure. Reading a human readable log for status means parsing text, while the runner is built around a directory of inputs and gives you a consistent way to address a run. Second, isolation. A direct call runs Ansible in your process space with your environment and your installed collections, whereas the runner adds process isolation as an explicit capability and offers a container image interface where the run gets its own filesystem and its own dependency set. Third, embedding. A Python module import is a supported interface, so a service can start a run programmatically without a subprocess boundary.
The cost of the runner is the dependency itself and a second abstraction to learn, and the README leaves you to learn it from the external documentation. For a single scripted playbook, direct invocation is the smaller design. For a service that runs many playbooks, needs to separate untrusted runs, or wants to keep Ansible from the host environment, the runner earns its place.
Editorial conclusion
Adopt ansible-runner when your system needs to launch Ansible from code rather than from a shell script that parses stdout, and when you want the same call to work whether it runs in process or in a container. Do not adopt it to replace Ansible itself, since the runner is an interface layer around the existing tooling and you still need Ansible present. Before you pin a version, check which maintenance line you want, because 2.4.3 and 2.3.8 were both published in 2026 and 2.3.7 predates both, confirm that your Python is 3.10 or later, and read the LICENSE.md file rather than relying on the Apache-2.0 string in pyproject.toml.
Frequently asked questions
What is ansible-runner and how does it differ from Ansible itself?
ansible-runner is a tool and Python library that provides a consistent interface abstraction to Ansible, usable as a standalone tool, a container image interface or an importable Python module. Ansible still does the automation work; the runner is the layer that invokes it from another program.
Which Python versions does ansible-runner support?
pyproject.toml sets requires-python to >=3.10, and the classifiers list Python 3.10 through 3.14 with a Python 3 only marker. The operating system classifier is POSIX.
How do I install ansible-runner and what command does it provide?
The README does not give an install command and points to the PyPI project page for ansible-runner. The package declares a console script named ansible-runner, mapped to ansible_runner.__main__:main.
Is ansible-runner still maintained?
The repository is not archived and the last push was on 2026-09-10. Recent tags include 2.4.3 on 2026-03-16 and 2.3.8 on 2026-04-21, which are two parallel release lines rather than a single sequence.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ansible-ansible-runner)