I use Ansible1 a lot, both at work and for the infrastructure2, see my infra repo.


Use pipx:

$ pipx install --include-deps ansible

To install additonal Python dependencies, use inject:

$ pipx inject ansible $dependency

Upgrading existing Ansible:

$ pipx upgrade --include-injected ansible

Note on using ansible.utils.ipaddr

Ansible is using the deprecated IPAddress.is_private function in netaddr to check if an IP address is private or public3.

{{ [ansible_default_ipv4.address] | ansible.utils.ipaddr("private") }}

This will now result in:

TASK [role: task] ***************************
An exception occurred during task execution.

fatal: []: FAILED! => {
    "changed": false

AttributeError: 'IPNetwork' object has no attribute 'is_private'

Until utils.ipaddr is fixed in the base ansible_collections is fixed, older version of netaddr where IPAddress.is_private needs to be used.

$ pipx inject ansible netaddr==0.10.0

This has been fixed in ansible-collections/ansible.utils4, but has not been released yet.


Re-use SSH connections

For faster ssh connections, enable pipelining and use the ControlPersis features of ssh to re-use SSH connections instead of always reconnecting:

forks = 20

ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o PreferredAuthentications=publickey
control_path = /tmp/ansible-ssh-%%h-%%p-%%r
pipelining = true

This will re-use connections for 60s and then close them.

Output formats

Ansible calls output formats "callbacks"5:

  • debug: indents Ansible's output and data structures, this is a good and sane default to use.

The output format/callback can be set in the ansible.cfg config files:

stdout_callback = debug

The output format can also be set via an environment variable:


Since environment variables take precedence over the config file, this will use the output format debug regardless of config file values.


By default, the ansible_managed variable will render to "Ansible managed", but it can provide more useful information, such as the path of the template in a Git repo.

ansible_managed = ansible_managed:{{{{ lookup('pipe', 'git rev-parse --show-toplevel')|basename }}}}/roles/{{{{ role_name }}}}/{{{{ template_path }}}}{{{{ lookup('pipe', 'git log -1 ' + template_fullpath|quote) | default(false, true) | ternary("", ",UNCOMITTED") }}}}

This will include the path to the source template, as well as the short commit hash that the file is templated from.

Default to showing diff

To make Ansible show diffs by default:

always = true

Makes --diff the default for ansible-playbook and friends.


Prevent ansible from printing every ok task with:


This will only print failed or changed tasks to stdout, makes it easy to parse the output into a convergence report.


2 - Ansible for (mirrored to GitHub: benediktkr/infra)


Ansible Callback-Plugins, includes GIFs of the output in action.