Ansible Cheatsheet

Dense single-page reference. Flags, modules, boilerplate, filters. Assumes you've read Quickstart or already know what things mean.

CLI one-liners

# Playbook run
ansible-playbook -i inventories/production/ site.yml
ansible-playbook site.yml --check --diff                    # dry-run + diff
ansible-playbook site.yml -l web01                          # limit to one host
ansible-playbook site.yml -l 'web:&prod'                    # web AND prod
ansible-playbook site.yml --tags nginx,config
ansible-playbook site.yml --skip-tags verify
ansible-playbook site.yml --start-at-task "Deploy config"
ansible-playbook site.yml --step                            # prompt per task
ansible-playbook site.yml -e "version=1.2.3"                # extra vars (wins)
ansible-playbook site.yml -e @vars.yml                      # extra vars from file
ansible-playbook site.yml --ask-vault-pass

# Ad-hoc one-shot
ansible -i inv all -m ping
ansible -i inv web -m shell -a 'systemctl is-active nginx' -b

# Inventory introspection
ansible-inventory -i inv --list
ansible-inventory -i inv --host web01
ansible-inventory -i inv --graph

# List what a playbook would do
ansible-playbook site.yml --list-hosts
ansible-playbook site.yml --list-tasks
ansible-playbook site.yml --list-tags

# Vault
ansible-vault create secrets.yml
ansible-vault edit   secrets.yml
ansible-vault view   secrets.yml
ansible-vault encrypt_string 's3cret' --name 'api_token'
ansible-vault rekey  secrets.yml

# Galaxy / collections
ansible-galaxy collection install -r collections/requirements.yml
ansible-galaxy role install -r roles/requirements.yml
ansible-galaxy collection list

# Lint / syntax
ansible-playbook site.yml --syntax-check
ansible-lint
yamllint .

Ad-hoc modules

ansible all -m ping
ansible all -m setup                                         # gather all facts
ansible all -m setup -a 'filter=ansible_distribution*'
ansible all -m command -a 'uptime'                           # no shell features
ansible all -m shell   -a 'ps aux | grep nginx'              # pipes/redirects OK
ansible all -m copy    -a 'src=f.conf dest=/etc/f.conf mode=0644' -b
ansible all -m file    -a 'path=/srv/app state=directory mode=0755' -b
ansible all -m service -a 'name=nginx state=restarted' -b
ansible all -m package -a 'name=htop state=present' -b
ansible all -m user    -a 'name=deploy shell=/bin/bash groups=sudo append=yes' -b
ansible all -m lineinfile -a 'path=/etc/hosts line="10.0.0.5 api"' -b
ansible all -m uri     -a 'url=http://localhost:8080/healthz return_content=yes'

Playbook boilerplate

---
- name: Configure web tier
  hosts: web
  become: true
  gather_facts: true

  vars:
    app_port: 8080

  vars_files:
    - vars/common.yml
    - vars/secrets.yml              # vault-encrypted

  pre_tasks:
    - name: Assert required vars are set
      ansible.builtin.assert:
        that:
          - app_version is defined

  roles:
    - role: baseline
    - role: nginx
      tags: [nginx]

  tasks:
    - name: Inline extra step
      ansible.builtin.debug:
        msg: "Deployed {{ app_version }}"

  handlers:
    - name: Restart nginx
      ansible.builtin.service:
        name: nginx
        state: restarted

Task boilerplate

- name: Deploy nginx config
  ansible.builtin.template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0644'
    backup: true
    validate: 'nginx -t -c %s'
  become: true
  tags: [nginx, config]
  when: ansible_os_family == 'RedHat'
  notify: Restart nginx

Handler boilerplate

handlers:
  - name: Restart nginx
    ansible.builtin.service:
      name: nginx
      state: restarted
    listen: "restart web"          # any task can notify "restart web"

  - name: Reload firewall
    ansible.builtin.service:
      name: firewalld
      state: reloaded

# Force handlers to run mid-play
- name: Flush handlers now
  ansible.builtin.meta: flush_handlers

when / register / failed_when / changed_when

- name: Is nginx running?
  ansible.builtin.command: systemctl is-active nginx
  register: nginx_state
  changed_when: false              # command modules always report 'changed' otherwise
  failed_when: false               # don't fail the play if inactive

- name: Restart only if nginx isn't running
  ansible.builtin.service:
    name: nginx
    state: started
  when: nginx_state.stdout != 'active'

- name: Skip on Debian
  ansible.builtin.package:
    name: httpd
    state: present
  when: ansible_os_family != 'Debian'

- name: Multiple conditions
  ansible.builtin.debug:
    msg: "production web host"
  when:
    - inventory_hostname in groups['web']
    - env == 'production'

- name: Conditional with a registered var
  ansible.builtin.debug:
    msg: "healthcheck passed"
  when:
    - health.status == 200
    - health.json.ok | default(false) | bool

Loops

- name: Install baseline packages
  ansible.builtin.package:
    name: "{{ item }}"
    state: present
  loop:
    - htop
    - jq
    - curl

- name: Shortcut — package module accepts a list directly
  ansible.builtin.package:
    name: [htop, jq, curl]
    state: present

- name: Loop with dicts and a custom label
  ansible.builtin.user:
    name: "{{ item.name }}"
    groups: "{{ item.groups }}"
    append: true
  loop:
    - { name: alice, groups: [sudo, docker] }
    - { name: bob,   groups: [sudo] }
  loop_control:
    label: "{{ item.name }}"        # prettier output

- name: Loop until condition met (retry)
  ansible.builtin.uri:
    url: http://localhost:8080/healthz
  register: hc
  until: hc.status == 200
  retries: 10
  delay: 3

# Legacy — prefer loop: in new code
- ansible.builtin.package:
    name: "{{ item }}"
  with_items: [htop, jq]

Variable precedence (short form)

Lowest to highest, simplified. Later beats earlier:

  1. role defaults/main.yml
  2. inventory variables (host + group vars in inventory file)
  3. inventory group_vars/all
  4. inventory group_vars/<group>
  5. playbook group_vars/<group>
  6. playbook host_vars/<host>
  7. host facts (gathered or cached)
  8. Play vars:, vars_files:, vars_prompt:
  9. Task vars:
  10. Role vars/main.yml
  11. block vars
  12. set_fact / include_vars
  13. Extra vars (-e) — always wins

Full 22-position list with caveats on Variable Precedence.

Jinja2 filters — most used

{{ myvar | default('fallback') }}
{{ myvar | default(omit) }}                  # omit the param entirely

{{ users | map(attribute='name') | list }}
{{ users | selectattr('active', 'true') | list }}
{{ users | rejectattr('locked') | list }}

{{ dict1 | combine(dict2, recursive=True) }}

{{ mylist | length }}
{{ mylist | unique }}
{{ mylist | join(', ') }}
{{ mylist | flatten(levels=1) }}

{{ path | basename }}
{{ path | dirname }}
{{ path | realpath }}

{{ 'Hello {name}' | format(name='world') }}
{{ 'abc' | regex_replace('^a', 'A') }}
{{ 'abc' | regex_search('b.') }}

{{ data | to_nice_yaml(indent=2) }}
{{ data | to_json }}
{{ raw   | from_yaml }}
{{ raw   | from_json }}

{{ password | password_hash('sha512') }}     # for user.password
{{ 'file.txt' | hash('sha256') }}

{{ ip | ipaddr('network') }}                 # needs ansible.utils / community.general

Vault

# Create / edit an encrypted file
ansible-vault create group_vars/prod/vault.yml
ansible-vault edit   group_vars/prod/vault.yml

# Encrypt / decrypt files in place
ansible-vault encrypt secrets.yml
ansible-vault decrypt secrets.yml

# Inline encrypted string
ansible-vault encrypt_string --vault-id prod@prompt \
  's3cret' --name 'api_token'
# → paste the output directly into a YAML var file

# Multiple vault IDs (dev and prod secrets in the same repo)
ansible-playbook site.yml \
  --vault-id dev@~/.vault/dev.txt \
  --vault-id prod@~/.vault/prod.txt

# Vault password from a file (CI-friendly)
ansible-playbook site.yml --vault-password-file ~/.vault/prod.txt

# Recommended layout
group_vars/
├── all/
│   ├── vars.yml        # plain
│   └── vault.yml       # encrypted; reference via vault_* vars

Inventory patterns

# Target expressions for -l / hosts:
all                       # every host
web                       # group 'web'
web,db                    # union
'web:db'                  # union (colon form)
'web:&prod'               # intersection: web AND prod
'web:!stage'              # difference: web but not stage
'web[0]'                  # first host in group
'web[0:2]'                # first three hosts
'~web\d+\.example\.com'   # regex (~ prefix)

# INI inventory with groups
[web]
web01.example.com
web02.example.com

[db]
db01.example.com

[prod:children]
web
db

[web:vars]
ansible_user=deploy

# YAML inventory equivalent
---
all:
  children:
    prod:
      children:
        web:
          hosts:
            web01.example.com: {}
            web02.example.com: {}
        db:
          hosts:
            db01.example.com: {}
      vars:
        env: production

Tags

# On tasks
- name: Deploy config
  ansible.builtin.template: { src: x.j2, dest: /etc/x }
  tags: [nginx, config]

# On a play (applies to all tasks in the play)
- hosts: web
  tags: [web]
  tasks: …

# On a role call
- role: nginx
  tags: [nginx]

# CLI
ansible-playbook site.yml --tags nginx
ansible-playbook site.yml --tags "nginx,config"
ansible-playbook site.yml --skip-tags verify
ansible-playbook site.yml --list-tags

# Special tags
tags: always     # runs even if --tags specified (unless --skip-tags always)
tags: never      # never runs unless explicitly requested via --tags
                 # useful for destructive one-off tasks

Full semantics on Tags, including import_role vs include_role.

Debug flags

-v         # show task results
-vv        # + input data
-vvv       # + connection details (SSH debug)
-vvvv      # + all the things; use sparingly

--check               # dry-run — no changes made
--diff                # show file-level diffs
--check --diff        # combine — the before-you-apply pair
--start-at-task "X"   # begin from a named task
--step                # prompt before each task (y/n/c)
--list-hosts          # who would this play target?
--list-tasks          # what tasks would run?
--list-tags           # what tags are defined?
-e '{"debug":true}'   # extra var as JSON (can be wins-all)

Common errors → cause

Error fragmentCause
UNREACHABLE! ... ssh: Permission denied (publickey)SSH key/user wrong — check ansible_user and key in ssh-agent
UNREACHABLE! ... Connection timed outHost down, firewall, wrong hostname/IP, SSH port not 22
FAILED! ... Missing sudo passwordbecome_method needs password — use --ask-become-pass or passwordless sudo
FAILED! ... The conditional check ... failedReferencing a variable that doesn't exist — use default()
ERROR! 'dict object' has no attribute 'x'Expected key missing from a dict var — add | default({}) or guard with is defined
ERROR! Vault format unhexlify errorWrong vault password, or mixed vault IDs with no --vault-id
ERROR! We were unable to read either as JSON nor YAMLYAML syntax error — run ansible-playbook --syntax-check and yamllint
skipping: no hosts matched-l pattern too restrictive, or inventory group empty
fatal: ... MODULE FAILURE with no other infoRun with -vvv — usually missing python deps or SELinux on remote
Related: Quickstart for a beginner walkthrough, Debugging for deeper troubleshooting, Variable Precedence for the full 22-position list.