Verified12 commandsAI-assisted

Ansible CLI

.md

Verified against ansible-core 2.20.0, flags verified via `<cmd> --help` and `ansible-galaxy role init` tested against a live role scaffold; the roles-directory layout and `ansible-lint` sections verified via official docs (docs.ansible.com/ansible/latest/playbook_guide/playbooks_reuse_roles.html, ansible.readthedocs.io/projects/lint), not executed locally, 2026-08-21 · official docs

Ad-hoc one-off commands, running playbooks, encrypting secrets with Vault, and inspecting inventory.

Ad-hoc commands (no playbook)#

ansible all -i inventory.ini -m ping                          # connectivity check against every host
ansible webservers -i inventory.ini -a "systemctl status nginx"   # -a with no -m defaults to the 'command' module
ansible webservers -i inventory.ini -m yum -a "name=nginx state=present" -b   # -b = become (sudo)
ansible webservers -i inventory.ini -m ping -l "web01,web02"   # limit to a subset of the matched pattern

-m ping is the standard first check when troubleshooting connectivity — it verifies SSH access and that Python is reachable on the target, without changing anything. -a without -m implicitly uses the command module, running the string as a raw shell command.

Running a playbook#

ansible-playbook -i inventory.ini site.yml
ansible-playbook -i inventory.ini site.yml --limit web01
ansible-playbook -i inventory.ini site.yml --tags "deploy,config"
ansible-playbook -i inventory.ini site.yml --skip-tags "slow-tests"
ansible-playbook -i inventory.ini site.yml -e "app_version=1.4.2"   # pass extra vars from the command line

Dry-run and diff before applying#

ansible-playbook -i inventory.ini site.yml --check              # predict changes without making them
ansible-playbook -i inventory.ini site.yml --check --diff       # + show the actual file/template diffs
ansible-playbook -i inventory.ini site.yml --syntax-check       # validate YAML/module syntax only, no connection
ansible-playbook -i inventory.ini site.yml --list-tasks         # show what would run, in order

--check mode is not a guarantee — modules that don't support check mode (some shell/command tasks) either skip or report inaccurately. Treat it as a strong signal for well-behaved modules, not an absolute preview for every task type.

Debugging a playbook run#

ansible-playbook -i inventory.ini site.yml -v      # verbose (stack -vv, -vvv, -vvvv for more detail per level)
ansible-playbook -i inventory.ini site.yml --start-at-task="Install package"   # resume from a specific task
ansible-playbook -i inventory.ini site.yml --step  # confirm each task interactively before running it

Ansible Vault — encrypting secrets#

ansible-vault create secrets.yml                    # create a new encrypted file
ansible-vault edit secrets.yml                       # decrypt, open in $EDITOR, re-encrypt on save
ansible-vault view secrets.yml                        # print decrypted contents, don't write to disk
ansible-vault encrypt group_vars/prod/vault.yml        # encrypt an existing plaintext file in place
ansible-vault decrypt group_vars/prod/vault.yml
ansible-playbook -i inventory.ini site.yml --ask-vault-pass       # prompt for the vault password at runtime
ansible-playbook -i inventory.ini site.yml --vault-password-file=.vault-pass   # read it from a file instead

Inventory#

ansible-inventory -i inventory.ini --list          # full inventory as JSON
ansible-inventory -i inventory.ini --graph          # human-readable group/host tree
ansible-inventory -i inventory.ini --host web01      # variables resolved for one specific host

Static inventory files use INI or YAML — group hosts under [groupname] headers in INI, with [groupname:vars] for group-level variables and [groupname:children] to nest groups. --graph is the fastest way to sanity-check that a nested group structure actually resolved the way you intended before running a playbook against it.

Roles — directory structure and scaffolding#

ansible-galaxy role init myrole    # scaffold the standard role directory layout
myrole/ ├── defaults/main.yml # lowest-precedence variables, meant to be overridden ├── files/ # static files referenced by the `copy` module ├── handlers/main.yml # tasks triggered by `notify` ├── meta/main.yml # role metadata + dependencies on other roles ├── tasks/main.yml # the role's actual task list ├── templates/ # Jinja2 templates referenced by the `template` module ├── tests/ # a minimal test inventory + playbook └── vars/main.yml # higher-precedence variables, not meant to be overridden

A role is included from a playbook with roles: [myrole] or, for finer control over ordering relative to other tasks, import_role/include_role. Ansible discovers a role by name in roles/ next to the playbook, or in any path listed in ANSIBLE_ROLES_PATH — no explicit path is needed if it lives in the conventional location.

Installing roles and collections from Galaxy#

ansible-galaxy install geerlingguy.docker              # install a single role from Ansible Galaxy
ansible-galaxy install -r requirements.yml              # install everything listed in a requirements file
ansible-galaxy role list                                 # show installed roles + versions
ansible-galaxy collection install community.general      # install a collection instead of a role
ansible-galaxy collection install -r requirements.yml -p ./collections   # into a project-local path

A requirements.yml can pin both roles and collections with version constraints in one file — check it into the repo alongside the playbooks so ansible-galaxy install -r requirements.yml reproduces the exact dependency set on any machine, rather than relying on whatever happens to already be installed.

Handlers — running tasks only on change#

tasks:
  - name: Update nginx config
    ansible.builtin.template:
      src: nginx.conf.j2
      dest: /etc/nginx/nginx.conf
    notify: Restart nginx

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

Handlers only run if a task that notifys them actually reports changed, and by default they run once, after all regular tasks in the play finish — not immediately after the notifying task. Use --force-handlers on ansible-playbook to run notified handlers even if a later task in the play fails, or meta: flush_handlers in the task list to run them early, mid-play.

Tags in depth#

ansible-playbook -i inventory.ini site.yml --list-tags     # see every tag defined in the playbook, without running it
ansible-playbook -i inventory.ini site.yml --tags "always,deploy"   # combine the implicit 'always' tag with a specific one

Two tag names are special: a task or role tagged always runs on every invocation regardless of --tags/--skip-tags (unless explicitly skipped), and one tagged never is skipped by default unless its exact tag is requested with --tags. Tags applied to a roles: entry or an import_playbook/import_tasks propagate down to every task inside it — useful for tagging an entire role's inclusion in one place instead of every task within it.

Privilege escalation with become#

ansible-playbook -i inventory.ini site.yml -b                          # become the default target user (root)
ansible-playbook -i inventory.ini site.yml -b --become-user deploy     # become a specific non-root user
ansible-playbook -i inventory.ini site.yml -b -K                       # prompt for the become password
ansible-playbook -i inventory.ini site.yml -b --become-method su       # use su instead of the default sudo
ansible-doc -t become -l                                                # list every become plugin available

become can also be set per-task or per-play in YAML (become: true, become_user: deploy) rather than globally on the CLI — task/play-level settings override whatever was passed on the command line, so a task explicitly marked become: false stays unprivileged even if -b was passed to ansible-playbook.

Linting playbooks with ansible-lint#

ansible-lint site.yml                    # lint a single playbook
ansible-lint                              # lint the whole current directory (roles, playbooks, etc.)
ansible-lint --profile production         # run the stricter production rule profile instead of the default
ansible-lint -x yaml[line-length]         # exclude a specific rule by its rule id

ansible-lint is a separate PyPI/pipx package (pipx install ansible-lint), not bundled with ansible-core — it catches style and correctness issues ansible-playbook --syntax-check doesn't, like deprecated module names, missing name: keys, and risky command/shell usage where a dedicated module exists.