DS DevShelfHub Projects · AI tools
Cheatsheets / Ansible
Cheatsheet · Dev tooling

Ansible: Playbooks, Roles, Inventory and Vault Reference Guide

By DevShelfHub

Inventory, modules, playbooks, roles, variables, templates, vault, handlers — the day-to-day Ansible surface for ansible-core 2.17+. Covers declarative YAML automation from ad-hoc commands through rolling deployments with full-qualified collection names (FQCN).

96 items 8 min Playbooks Roles Inventory

Start hereQuick start · 6 you’ll reach for daily

Pingansible all -m ping
Run playansible-playbook site.yml
Dry run--check --diff
Limit--limit web --tags db
Vaultansible-vault encrypt …
Becomebecome: true

Target versions · paceVersions

Targets: ansible-core ≥ 2.17 python ≥ 3.10 (control) collections from galaxy.ansible.com

Snippets use modern fully-qualified collection names (FQCN): e.g. ansible.builtin.apt, not just apt. The shortform still works but FQCN ages better and is required for non-builtin modules. Install community bundle separately: pipx inject ansible-core ansible.

install · ad-hoc · vaultSetup

bash
# Install
pipx install ansible-core               # core engine
pipx inject ansible-core ansible        # add the community collection bundle
ansible --version

# Quick connectivity check
ansible -i inventory.ini all -m ping

# Run an ad-hoc command
ansible -i inventory.ini web -m shell -b -a "uptime"

# Run a playbook
ansible-playbook -i inventory.ini site.yml --check       # dry-run
ansible-playbook -i inventory.ini site.yml --diff        # show file diffs
ansible-playbook -i inventory.ini site.yml --limit web   # subset of hosts
ansible-playbook -i inventory.ini site.yml --tags db     # subset of tasks

# Vault
ansible-vault create  secrets.yml
ansible-vault edit    secrets.yml
ansible-vault encrypt vars/prod.yml
ansible-playbook ... --ask-vault-pass

who you manageInventory

[group]INI groups. Hosts listed below.
host ansible_user=… ansible_port=…Per-host connection vars.
[parent:children]Group of groups.
[group:vars]Group-scoped vars in inventory.
group_vars/all.ymlVars for every host. Lowest-priority defaults.
group_vars/web.yml / host_vars/h1.ymlGroup / host specific overrides.
Dynamic inventory (script / plugin)AWS / GCP / Azure / Hetzner plugins read live infra.
ansible-inventory -i … --list / --graphSanity-check what Ansible sees.
ansible_connection: local / ssh / docker / kubectlPick the connection plugin.
yaml
# inventory.ini — simple, static
[web]
web1.example.com ansible_user=deploy
web2.example.com ansible_user=deploy ansible_port=2222

[db]
db1.example.com

[prod:children]
web
db

[prod:vars]
env=prod
ansible_python_interpreter=/usr/bin/python3

# group_vars/all.yml — variables applied to every host
---
app_user: app
timezone: Europe/London

# host_vars/web1.example.com.yml — host-specific overrides
---
extra_workers: 4

YAML playsPlaybooks

- name: … hosts: … tasks: […]Smallest viable play.
become: true / become_user: deployRun as root / another user (sudo by default).
gather_facts: falseSkip the initial setup module. Faster.
vars / vars_files / vars_promptInline / from-file / from-stdin.
pre_tasks / tasks / post_tasks / handlersOrdered phases of a play.
strategy: linear / free / mitogenHow hosts run tasks in parallel.
serial: 1 / "10%"Rolling update. One host at a time / fraction.
max_fail_percentage: 25Abort the play if too many hosts fail.
any_errors_fatal: trueFirst failure stops the whole play.
yaml
---
# site.yml
- name: Configure web servers
  hosts: web
  become: true
  vars:
    nginx_version: "1.27.*"
  vars_files:
    - vars/prod.yml
  pre_tasks:
    - name: Ensure cache is fresh
      ansible.builtin.apt: { update_cache: yes, cache_valid_time: 3600 }
  roles:
    - { role: base,     tags: [base] }
    - { role: nginx,    tags: [web, nginx] }
    - { role: app,      tags: [app] }
  post_tasks:
    - name: Confirm health
      ansible.builtin.uri:
        url: http://127.0.0.1/healthz
        status_code: 200
      register: health
      retries: 6
      delay: 5
      until: health.status == 200

the work unitTasks & modules

ansible.builtin.copy / template / fileFile ops. copy for static, template for Jinja.
ansible.builtin.apt / dnf / yum / packagePackage managers. package dispatches by OS.
ansible.builtin.service / systemdService state + enabled-at-boot.
ansible.builtin.user / groupManage local accounts.
ansible.builtin.cronCron entries.
ansible.builtin.lineinfile / blockinfileEdit single lines / blocks in config files.
ansible.builtin.command vs shellcommand avoids the shell; use shell only for pipes / redirects.
register: varCapture result. Use var.stdout, .rc, …
when: foo == "bar"Conditional execution.
loop / loop_control / itemIteration. Replaces legacy with_items.
changed_when / failed_whenOverride Ansible’s changed/failed inference.
check_mode: truePer-task dry-run override.
delegate_to / run_onceRun on a different host / single host.
ignore_errors: trueUse sparingly. Better: failed_when.

reusable chunksRoles

ansible-galaxy role init my_roleScaffold the standard layout.
roles//tasks/main.ymlEntry point. Imports other task files.
roles//defaults/main.ymlLowest-priority defaults. Override-friendly.
roles//vars/main.ymlRole-owned vars. High priority.
roles//handlers/main.ymlHandlers triggered by notify.
roles//templates/*.j2Jinja templates. Referenced by base name.
roles//files/Static assets for copy.
roles//meta/main.ymlDependencies on other roles.
include_role / import_roleDynamic vs static role inclusion in a play.
ansible-galaxy install -r requirements.ymlPull external roles / collections.
yaml
# roles/nginx/tasks/main.yml
---
- name: Install nginx
  ansible.builtin.apt:
    name: "nginx={{ nginx_version }}"
    state: present
  notify: Restart nginx

- name: Render site config
  ansible.builtin.template:
    src: site.conf.j2
    dest: /etc/nginx/sites-available/{{ site_name }}.conf
    mode: "0644"
  notify: Reload nginx

- name: Enable the site
  ansible.builtin.file:
    src: /etc/nginx/sites-available/{{ site_name }}.conf
    dest: /etc/nginx/sites-enabled/{{ site_name }}.conf
    state: link
  notify: Reload nginx

# roles/nginx/handlers/main.yml
---
- name: Reload nginx
  ansible.builtin.service: { name: nginx, state: reloaded }

- name: Restart nginx
  ansible.builtin.service: { name: nginx, state: restarted }

precedence is everythingVariables

role defaultsLowest priority.
inventory file / group_vars / host_varsStandard config-as-data.
play vars / vars_files / vars_promptPer-play.
role vars (roles//vars/)Higher than play vars.
set_fact: foo: barSet during execution. Persists for the play.
registerModule output. Highest play-scoped priority.
--extra-vars / -eCLI override. Highest priority overall.
ansible_facts.* (gathered)Discovered host metadata: OS, IPs, mounts.
hostvars["h1"].varRead variables from another host.

Jinja2Templates

ansible.builtin.template: src=app.conf.j2 dest=/etc/app.confRenders Jinja with play vars in scope.
{{ var }}Variable interpolation.
{% if cond %} … {% endif %}Conditional.
{% for x in items %} … {% endfor %}Loops.
{{ value | default("x") }}Filter with fallback.
{{ value | to_json }}Common filters: to_json, to_yaml, b64encode.
{{ ansible_managed }}Auto-comment indicating the file is Ansible-managed.
lookup("file", "path")Inline-load a file at render time.
lookup("env", "HOME")Read controller env vars.

react to changeHandlers

notify: Reload nginxTrigger a handler on change.
handlers: [- name: Reload nginx, service: …]Defined alongside the play / role.
Handlers run at end of play, once per nameN triggers → one execution.
meta: flush_handlersForce handlers to run mid-play.
listen: "event-name"Decouple handler trigger from name. Multiple handlers can listen.
force_handlers: trueRun handlers even on subsequent failures.

secrets in repoVault

ansible-vault create file.ymlNew encrypted file.
ansible-vault edit file.ymlEdit in $EDITOR.
ansible-vault encrypt / decrypt / view / rekeyFull lifecycle.
!vault | $ANSIBLE_VAULT;…Inline encrypted variable inside a YAML file.
ansible-vault encrypt_string "password"Produce the inline form.
--ask-vault-pass / --vault-password-file=fileProvide password to playbook.
vault_identity_list / labelsMultiple vault passwords for different envs.
Treat vault as obfuscation + access controlNot a substitute for a secret manager + access logging.

slice workTags & loops

tags: [base, web]Mark tasks / roles. Run subset with --tags / --skip-tags.
always / neverSpecial tags. always runs unless explicitly skipped.
loop: ["a", "b"]Iterate. {{ item }} per iteration.
loop_control: { label: "{{ item.name }}", pause: 1 }Cleaner output + per-iteration sleep.
until / retries / delayRetry a task until a condition holds.
block / rescue / alwaysTry/except for tasks.
async / pollFire-and-forget long-running tasks.

how Ansible reaches hostsConnection & become

ansible_connection: ssh (default)Native SSH client.
ansible_connection: localRun on the control node itself.
ansible_connection: docker / community.docker.dockerTalk to running containers.
ansible_connection: kubernetes.core.kubectlManage pods over kubectl.
become: true / become_method: sudo|su|doasPrivilege escalation.
become_user: deployBecome a specific non-root user.
--ask-become-pass / -KPrompt for sudo password.
ssh-agent + ssh_argsAgent forwarding, bastion hops, custom ports.
ansible_python_interpreterPin which Python Ansible runs as on the target.

install · configure · serveEnd-to-end · Static site host

Install nginx, drop a server block, link it into sites-enabled, deploy a tiny HTML page, reload on change — in one play.

yaml
---
# site.yml — provision + deploy a tiny static-site host.
- name: Bootstrap webserver
  hosts: web
  become: true
  tasks:

    - name: Install packages
      ansible.builtin.apt:
        name: [nginx, curl]
        state: present
        update_cache: yes

    - name: Drop nginx config
      ansible.builtin.copy:
        dest: /etc/nginx/sites-available/site.conf
        content: |
          server {
              listen 80 default_server;
              root /var/www/site;
              index index.html;
          }
        mode: "0644"
      notify: Reload nginx

    - name: Enable site
      ansible.builtin.file:
        src: /etc/nginx/sites-available/site.conf
        dest: /etc/nginx/sites-enabled/site.conf
        state: link
      notify: Reload nginx

    - name: Deploy index.html
      ansible.builtin.copy:
        dest: /var/www/site/index.html
        content: "

{{ ansible_hostname }} is up

" mode: "0644" handlers: - name: Reload nginx ansible.builtin.service: { name: nginx, state: reloaded }

Best practiceGood to know

Use fully-qualified collection names. ansible.builtin.apt, not bare apt. The short form still works for builtin modules but breaks the moment you add a custom collection with a same-named module.
--check --diff first, always. Dry-run gives you a textual diff of the changes Ansible would make. Catches accidental production typos at zero cost.
Idempotent > clever. A second run should make zero changes. If command / shell is your hammer, the play isn’t idempotent. Reach for native modules + creates / removes hints.

Common trapsWatch out for

Variable precedence is non-obvious. --extra-vars beats everything; role vars/ beats play vars; role defaults/ sits at the bottom. When “my variable isn’t taking effect”, walk the precedence list.
command with shell features fails silently. cat file | grep x through command treats the pipe as an argument to cat. Use shell — and know you’ve given up portability.
Handlers don’t run if the play aborts. A failure before flush_handlers means nginx never reloads. Set force_handlers: true when correctness matters more than failure speed.

Go deeperSee also

Ansible FAQ

What is Ansible and what is it used for?

Ansible is an agentless IT automation tool that uses SSH and YAML playbooks to configure servers, deploy applications, and orchestrate infrastructure. It is idempotent — running the same playbook twice leaves the system in the same state.

How do I run an Ansible playbook?

Run ansible-playbook site.yml -i inventory.ini. Add --check --diff for a dry-run that shows what would change without applying it. Use --limit web to target a single group and --tags db to run only tagged tasks.

What is an Ansible role?

A role is a reusable, self-contained unit of automation with a standard directory layout: tasks/, handlers/, defaults/, vars/, templates/, and files/. Roles are referenced in playbooks with roles: [my_role] and can be shared via Ansible Galaxy.

How does Ansible Vault work?

Ansible Vault encrypts sensitive files or individual strings using AES-256. Encrypt with ansible-vault encrypt secrets.yml and decrypt at runtime with --ask-vault-pass or a vault password file. Inline encrypted values use the !vault YAML tag.

Does Ansible need an agent installed on managed hosts?

No. Ansible is agentless. It connects to managed hosts over SSH (or WinRM for Windows) using standard credentials. The control node needs Python and the ansible-core package; managed hosts only need Python and an SSH server.

What is the difference between ansible and ansible-playbook?

ansible runs a single ad-hoc module against hosts (e.g. ansible all -m ping). ansible-playbook runs a full YAML playbook with multiple plays and tasks. Use ad-hoc commands for one-off checks and playbooks for repeatable automation.