Ansible CLI
.mdVerified 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 lineDry-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 itAnsible 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 insteadInventory#
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 hostStatic 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 layoutmyrole/
├── 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 pathA 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: restartedHandlers 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 oneTwo 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 availablebecome 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 idansible-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.