Skip to content

Running Ansible

Ansible looks for collections in ~/.ansible/collections and the paths in ansible.cfg, so it can load collections the project doesn't list. alai run and alai env set the environment so Ansible loads only the project's collections.

$ alai run ansible-playbook site.yml -i inventory.ini

In the example below, a project adds the ansible.utils collection and then runs an ad hoc ansible command with a module from that collection:

Running a module from a collection just added

alai run first syncs the project as alai sync does. It then runs the command with environment variables that make Ansible find only the project's collections. The command's output, signals and exit code are passed through unchanged, so it behaves as if it was run directly.

alai env doesn't sync the project, and prints the environment variables Ansible needs to see the project's collections without running a command. Use alai env to configure a shell or editor:

$ eval "$(alai env)"
$ ansible-doc community.general.ufw

Which collections Ansible sees

alai doesn't install or choose Ansible, so Ansible must already be on the PATH. How Ansible is installed is up to you, whether through the system packages, pip in a virtual environment, pipx, uv or something else.

Currently alai run sets the following two environment variables:

Variable Value Why
ANSIBLE_COLLECTIONS_PATH The collections directory Collections load from there, instead of ~/.ansible/collections or the paths in ansible.cfg
ANSIBLE_COLLECTIONS_SCAN_SYS_PATH False Collections installed in Python's site-packages, such as those the ansible package bundles, aren't loaded

The variables alai sets and their values aren't a public interface, and alai can change them at any time. The table is only here to explain how alai run and alai env work.

With the variables alai sets, Ansible only sees the configured collections_dir. The variables also override any collections_path set in ansible.cfg.

ansible-playbook always checks the collections/ directory next to the playbook, and no setting turns that off. alai's default collections_dir is that same directory, so keeping the default means Ansible sees only one set of collections.

alai doesn't check the running Ansible version against the project's requires_ansible, and leaves that check to Ansible. Ansible's COLLECTIONS_ON_ANSIBLE_VERSION_MISMATCH option sets what happens when a collection's requires_ansible doesn't match the running Ansible. The option can be warning, which is the default, ignore or error, and can be set in ansible.cfg:

[defaults]
collections_on_ansible_version_mismatch = "error"

The option can also be set with an environment variable when running Ansible:

$ ANSIBLE_COLLECTIONS_ON_ANSIBLE_VERSION_MISMATCH=error \
    alai run ansible-playbook ...

Collections the lock doesn't list

Unlike alai sync, alai run keeps collections in the collections directory that alai.lock doesn't list, so a collection you're working on before adding it to the lock isn't deleted. alai run warns about those collections and still lets Ansible use them:

$ alai run ansible-playbook site.yml
warning: Ansible can load collections alai.lock doesn't list here, from collections: my.work
warning: alai run --exact or alai sync removes them

To remove those collections as alai sync would, run with --exact. To keep them without the warning, run with --inexact.

Options

alai run takes the options of alai sync that choose what's installed, for example the groups, --locked, --frozen, the upgrade and reinstall options, and the index, cache and collections directory settings.

Option
--no-sync Run without locking or installing anything. Can't be used with --locked or --frozen
--exact Remove installed collections alai.lock doesn't list before running, rather than warn of them
--inexact Keep installed collections alai.lock doesn't list without warning of them. Can't be used with --exact

alai run needs an alai.toml, as without a project there's no collections directory to point Ansible at.

In a collection project

In a collection project, alai run and alai env set two more variables:

Variable Value Why
ANSIBLE_TEST_CONTENT_ROOT <collections_dir>/ansible_collections/<namespace>/<name>, the collection's link ansible-test needs the collection at a path ending in ansible_collections/<namespace>/<name>, which the link gives it wherever the checkout is
PYDEVD_RESOLVE_SYMLINKS 1 The collection is loaded through its link, so debuggers built on debugpy, such as VS Code's, show its files in the checkout rather than through the collection cache

A playbook project with an editable directory source loads that collection through a link too, so PYDEVD_RESOLVE_SYMLINKS is set there as well.

Tools like ansible-test can then run from a checkout anywhere, rather than only in the expected ansible_collections/<namespace>/<name> directory:

$ alai run ansible-test sanity --docker
$ alai run ansible-test integration --local my_target
$ alai run ansible-doc my_ns.my_col.my_module

ansible-test units --local and --venv don't work

ansible-test units --local and every command with --venv don't work in a collection project, as ansible-test resolves the collection's link. Use --docker instead.

To run a playbook in the collection's playbooks/ directory, give its fully qualified name <namespace>.<name>.<playbook> rather than its path:

$ alai run ansible-playbook my_ns.my_col.playbook

When a playbook is run by its path, plugin short names in it no longer resolve to the collection that holds the playbook.

Running a collection's playbook by name

Ansible loads the collection project through its link in the collections directory, so every path Ansible shows for the collection is under that directory rather than where you checked it out. Such a path starts with ~/.cache/alai/envs/<id>/ansible_collections/<namespace>/<name>/, and shows up in tracebacks, verbose output, ansible-doc's output, a plugin's __file__ and ansible-test's messages. The files at those paths are the same files as in your checkout, as Ansible only reaches them through the link.

In a shell, CI or an editor

To use the project's collections from a shell or editor, alai env prints the environment variables that tell Ansible where to find them. Pass its output to your shell's eval or the equivalent, and see Editors for how to configure a particular editor.

alai env never locks or installs collections, so it's quick enough to run whenever a shell enters the project. To make sure the collections are installed, run alai sync before alai env:

$ alai sync && eval "$(alai env)"
Use How
A POSIX shell eval "$(alai env)"
fish alai env | source
PowerShell alai env --output-format pwsh | Out-String | Invoke-Expression
direnv eval "$(alai env)" in .envrc
A CI step alai sync --locked, then alai env --output-format dotenv >> "$GITHUB_ENV" on GitHub Actions
An editor alai env --output-format dotenv > .vscode/alai.env, for editors that read KEY=VALUE lines (see Editors for running and debugging Ansible in VS Code)

--output-format can be sh, fish, pwsh or dotenv.