ANSIBLE Updated 2026-08-25 167+ commands Verified against official docs

Ansible Cheat Sheet

150+ Ansible commands for ad-hoc tasks, playbooks, inventory, modules, vault, roles, and Galaxy. Includes an Ansible Module Quick-Reference. Verified against official Ansible docs.

Ctrl+K

Everything runs in your browser. No commands or data are sent to any server.

New to Ansible? Start with these

5 essential commands to get you started. The full reference is right below.

Check the installed Ansible version

ansible --version

Confirm which Ansible version, config file, and Python interpreter are actually being used before debugging anything else.

Test connectivity to every host

ansible all -m ping

The first command to run against any new inventory to confirm SSH connectivity and Python are working before anything else.

Dry-run a playbook without changing anything

ansible-playbook site.yml --check

Preview exactly what a playbook would change before committing to running it for real, especially on production.

List every available module

ansible-doc -l

Browse every module available in your current Ansible install and any collections you have installed, before writing a task from memory.

Scaffold a new role

ansible-galaxy init my_role

Generate the standard role directory structure (tasks, handlers, defaults, templates, and more) instead of building it by hand.

167 commands

Check the installed Ansible version

Beginner
ansible --version

↓ Click command to explain

When to use this

Confirm which Ansible version, config file, and Python interpreter are actually being used before debugging anything else.

Gotcha

Ansible 2.10+ splits ansible-core from a much larger collection of community modules, so the version output also tells you whether a module you expect is bundled or needs installing separately.

Test connectivity to every host

Beginner
ansible all -m ping

↓ Click command to explain

-m Specifies which module to run

When to use this

The first command to run against any new inventory to confirm SSH connectivity and Python are working before anything else.

Gotcha

The ping module does not use ICMP at all. It just verifies Ansible can log in and execute a Python script on the remote host, which is a much more useful check.

Test connectivity to one group

Beginner
ansible webservers -m ping

↓ Click command to explain

When to use this

Narrow a connectivity check to a specific inventory group instead of the entire fleet.

List every available module

Beginner
ansible-doc -l

↓ Click command to explain

-l Lists all installed modules with a one-line summary

When to use this

Browse every module available in your current Ansible install and any collections you have installed, before writing a task from memory.

Gotcha

The list only includes modules from collections you actually have installed. A module documented online but missing from this list usually means the collection needs installing with ansible-galaxy collection install.

Read documentation for one module

Beginner
ansible-doc apache2_module

↓ Click command to explain

When to use this

Read the full parameter list, defaults, and examples for a specific module directly in the terminal, without a browser.

Print a copy-paste task snippet for a module

Beginner
ansible-doc -s copy

↓ Click command to explain

-s Outputs a ready-to-use YAML task snippet with every parameter listed

When to use this

Generate a starter YAML task block for a module with every available parameter commented in, then delete what you do not need.

List every configuration setting Ansible understands

Intermediate
ansible-config list

↓ Click command to explain

When to use this

See every configuration option Ansible supports, its default value, and which environment variable or ini setting controls it.

Show the current effective configuration

Intermediate
ansible-config dump

↓ Click command to explain

When to use this

See exactly what configuration Ansible is actually using right now, merged from defaults, ansible.cfg, and environment variables.

Gotcha

This is the first command to run when a setting in ansible.cfg does not seem to be taking effect, since it shows exactly which source won.

Print the active ansible.cfg file

Intermediate
ansible-config view

↓ Click command to explain

When to use this

Print the exact ansible.cfg file currently in effect, useful when multiple config files exist across a project and a home directory.

Run a one-off command with the command module

Beginner
ansible all -m command -a 'uptime'

↓ Click command to explain

-a Passes arguments to the module being run

When to use this

Run a quick one-off command against the whole fleet without writing a playbook, such as checking uptime after a maintenance window.

Gotcha

command is the default module when -m is omitted, so ansible all -a 'uptime' does exactly the same thing.

Run a shell one-liner with pipes

Beginner
ansible all -m shell -a 'df -h | grep /var'

↓ Click command to explain

When to use this

Run a command that needs shell features like pipes, redirects, or environment variable expansion, which the plain command module cannot do.

Gotcha

Reach for shell only when you actually need a shell feature. Prefer command, or better yet a dedicated module, for anything simpler since shell input is harder to make idempotent and safe.

Gather and display all facts about hosts

Beginner
ansible all -m setup

↓ Click command to explain

When to use this

Dump every fact Ansible can discover about a host: OS, memory, network interfaces, mounted disks, and more.

Gotcha

The full output is huge. Pipe it through a filter or use the filter= argument to narrow it down when you only care about one or two facts.

Gather only specific facts

Intermediate
ansible all -m setup -a 'filter=ansible_distribution*'

↓ Click command to explain

filter= Restricts the fact output to keys matching a glob pattern

When to use this

Check just the OS distribution and version facts across a fleet without wading through hundreds of unrelated fact keys.

Run a command as root with privilege escalation

Beginner
ansible all -m command -a 'whoami' --become

↓ Click command to explain

--become Escalates privileges on the remote host using sudo (or another become method)

When to use this

Confirm privilege escalation is configured correctly before running a task that actually needs root, such as installing a package.

Gotcha

whoami should print root when --become works. If it still prints your login user, check that your sudoers entry allows passwordless sudo or add --ask-become-pass.

Limit an ad-hoc command to specific hosts

Beginner
ansible webservers -m ping --limit 'web1.example.com'

↓ Click command to explain

--limit Further restricts execution to a subset of the matched hosts

When to use this

Test a single host inside a larger group before running the same command against the whole group.

Increase parallelism with more forks

Intermediate
ansible all -m ping --forks 20

↓ Click command to explain

--forks Sets how many hosts Ansible connects to in parallel, default is 5

When to use this

Speed up a run across a large fleet by increasing how many hosts Ansible talks to simultaneously.

Gotcha

Raising forks too high can overwhelm the control node's file descriptors and CPU, or trigger rate limiting on the target infrastructure. Increase gradually and watch for connection errors.

Connect as a specific remote user

Beginner
ansible all -m ping -u deploy

↓ Click command to explain

-u Sets the remote username to connect as, overriding the inventory or config default

When to use this

Connect as a non-default SSH user, such as a dedicated deploy account instead of your personal login.

Use a specific SSH private key

Intermediate
ansible all -m ping --private-key ~/.ssh/id_deploy

↓ Click command to explain

--private-key Points to a specific SSH private key file for this connection

When to use this

Authenticate with a dedicated deploy key instead of whatever key is loaded in your SSH agent by default.

Prompt for an SSH password instead of a key

Intermediate
ansible all -m ping --ask-pass

↓ Click command to explain

--ask-pass Prompts interactively for the SSH password instead of using key-based auth

When to use this

Connect to hosts that only accept password authentication, such as a freshly imaged box before your key has been deployed to it.

Gotcha

This requires the sshpass package to be installed on the control node, or Ansible will error out immediately.

Condense output to one line per host

Intermediate
ansible all -a 'uptime' -o

↓ Click command to explain

-o Condenses each host's result onto a single line instead of a multi-line block

When to use this

Scan results across a large fleet quickly by flattening each host's output to a single line.

List which hosts a pattern actually matches

Beginner
ansible all --list-hosts

↓ Click command to explain

--list-hosts Prints the matched hosts without running anything against them

When to use this

Sanity-check which hosts a pattern resolves to before running anything against them for real.

Copy a file to every host ad-hoc

Beginner
ansible all -m copy -a 'src=/etc/hosts dest=/tmp/hosts'

↓ Click command to explain

When to use this

Push a single file out to every matched host without writing a full playbook for a one-off task.

Run a playbook

Beginner
ansible-playbook site.yml

↓ Click command to explain

When to use this

Apply every play and task in a playbook file against the hosts it targets.

Gotcha

A minimal playbook is a YAML list of plays, each with a hosts key and a tasks list, for example: - hosts: webservers\n tasks:\n - name: Install nginx\n apt: {name: nginx, state: present}

Dry-run a playbook without changing anything

Beginner
ansible-playbook site.yml --check

↓ Click command to explain

--check Runs in dry-run mode, reporting what would change without actually changing it

When to use this

Preview exactly what a playbook would change before committing to running it for real, especially on production.

Gotcha

Not every module fully supports check mode. Modules that shell out to external commands, like the command or shell module, cannot predict their own effect and are usually skipped or flagged as unsupported.

Show a diff of file changes in check mode

Beginner
ansible-playbook site.yml --check --diff

↓ Click command to explain

--diff Shows a before/after diff for any file that template, copy, or lineinfile would change

When to use this

See the exact line-by-line diff of every config file a playbook would touch, combined with --check for a full dry-run preview.

Validate playbook YAML syntax

Beginner
ansible-playbook site.yml --syntax-check

↓ Click command to explain

--syntax-check Parses the playbook and reports syntax errors without connecting to any hosts

When to use this

Catch YAML indentation errors and structural mistakes before wasting time connecting to real infrastructure.

Gotcha

Syntax check only validates YAML structure. It does not catch a typo in a module name or an invalid parameter, those only surface once the playbook actually runs.

Limit a playbook run to specific hosts

Beginner
ansible-playbook site.yml --limit webservers

↓ Click command to explain

--limit Restricts the run to a subset of hosts matched by the playbook's hosts key

When to use this

Run a playbook that targets a broad group against only one server first, to test it safely.

Run only tasks with specific tags

Intermediate
ansible-playbook site.yml --tags 'config,deploy'

↓ Click command to explain

--tags Runs only tasks (and their handlers) tagged with one of the listed tag names

When to use this

Re-run just the deployment steps of a large playbook without re-running slow provisioning steps that already succeeded.

Skip tasks with specific tags

Intermediate
ansible-playbook site.yml --skip-tags 'slow'

↓ Click command to explain

--skip-tags Runs every task except those tagged with one of the listed tag names

When to use this

Skip a known slow or optional section of a playbook, such as a full package cache refresh, during a quick iteration loop.

Pass a variable on the command line

Beginner
ansible-playbook site.yml -e 'env=production'

↓ Click command to explain

-e Sets extra variables, which take the highest precedence of any variable source

When to use this

Override a variable at run time, such as switching a playbook between staging and production without editing any file.

Gotcha

Extra vars set with -e override every other variable source, including group_vars, host_vars, and role defaults. This is exactly why it is the right tool for a one-off override.

Pass variables from a file

Intermediate
ansible-playbook site.yml -e '@vars.yml'

↓ Click command to explain

-e @file Loads extra variables from a YAML or JSON file, the @ prefix marks it as a file path

When to use this

Pass a whole set of environment-specific variables from a file instead of listing them individually on the command line.

Increase output verbosity

Beginner
ansible-playbook site.yml -v

↓ Click command to explain

-v Shows the result data for each task, not just its status

When to use this

See the actual result data returned by each task, useful when a task succeeds but you want to confirm exactly what it did.

List every task a playbook would run

Beginner
ansible-playbook site.yml --list-tasks

↓ Click command to explain

--list-tasks Prints every task in the playbook, in order, without executing anything

When to use this

Review the full task list of a playbook you did not write before running it, without needing to open every file.

List which hosts a playbook targets

Beginner
ansible-playbook site.yml --list-hosts

↓ Click command to explain

--list-hosts Prints every host the playbook's plays would target, without executing anything

When to use this

Confirm exactly which hosts a playbook resolves to across all of its plays before running it.

List every tag defined in a playbook

Intermediate
ansible-playbook site.yml --list-tags

↓ Click command to explain

--list-tags Prints every tag referenced anywhere in the playbook

When to use this

Discover which tags a playbook actually supports before deciding what to pass to --tags or --skip-tags.

Confirm each task interactively before running it

Intermediate
ansible-playbook site.yml --step

↓ Click command to explain

--step Prompts to confirm, skip, or continue before each task runs

When to use this

Walk through a playbook one task at a time on a production host, confirming each step before it executes.

Resume a playbook from a specific task

Intermediate
ansible-playbook site.yml --start-at-task='Install packages'

↓ Click command to explain

--start-at-task Skips every task before the one matching this name and starts there

When to use this

Resume a long playbook from the task that failed last time, instead of re-running everything from the beginning.

Gotcha

The task name must match exactly, including case. Earlier tasks are still parsed for variables and roles but their actions are not executed.

Run a playbook against a specific inventory

Beginner
ansible-playbook site.yml -i inventory/production

↓ Click command to explain

-i Points to a specific inventory file or directory instead of the configured default

When to use this

Target the production inventory explicitly when a project keeps separate inventory files per environment.

Run a playbook with privilege escalation and a password prompt

Beginner
ansible-playbook site.yml --become --ask-become-pass

↓ Click command to explain

--become Escalates privileges for every task in the play
--ask-become-pass Prompts interactively for the sudo password instead of assuming passwordless sudo

When to use this

Run a playbook that needs root access on hosts where sudo requires a password rather than being passwordless.

Run a playbook with more parallelism

Intermediate
ansible-playbook site.yml --forks 50

↓ Click command to explain

--forks Sets how many hosts are processed in parallel for this run

When to use this

Speed up a large fleet-wide rollout by raising parallelism above the default of 5 hosts at a time.

Run a playbook against the control node itself

Intermediate
ansible-playbook site.yml --connection=local

↓ Click command to explain

--connection=local Executes tasks directly on the control node instead of connecting over SSH

When to use this

Run a playbook that configures the machine Ansible itself is running on, skipping SSH entirely.

Run a playbook that includes vault-encrypted files

Intermediate
ansible-playbook site.yml --vault-password-file ~/.vault_pass

↓ Click command to explain

--vault-password-file Reads the vault password from a file instead of prompting interactively

When to use this

Run a playbook non-interactively in CI when it references vault-encrypted variable files.

Gotcha

Never commit the vault password file itself to version control. Store it as a CI secret and write it to a temp file at pipeline run time.

Escalate privileges as a specific user

Advanced
ansible-playbook site.yml --become-user=deploy

↓ Click command to explain

--become-user Sets which user to become, default is root

When to use this

Run tasks as a dedicated service account instead of root, when the target application should not run privileged.

Clear cached facts before running

Advanced
ansible-playbook site.yml --flush-cache

↓ Click command to explain

--flush-cache Clears any cached facts before gathering fresh ones for this run

When to use this

Force fresh fact gathering when fact caching is enabled and stale cached facts are causing a playbook to behave unexpectedly.

Gotcha

Only relevant when fact caching (fact_caching in ansible.cfg) is enabled. Without it, facts are already gathered fresh on every run.

Static inventory in INI format

Beginner
[webservers] web1.example.com web2.example.com ansible_host=10.0.1.5 [databases] db1.example.com

↓ Click command to explain

When to use this

The classic static inventory format, grouping hosts under bracketed group names. Still the most common format for small, fixed environments.

Gotcha

A per-host variable like ansible_host can be set inline after the hostname, useful when the inventory name differs from the actual address to connect to.

Static inventory in YAML format

Beginner
all: children: webservers: hosts: web1.example.com: web2.example.com: ansible_host: 10.0.1.5 databases: hosts: db1.example.com:

↓ Click command to explain

When to use this

The YAML equivalent of an INI inventory, preferred in larger projects since it nests more naturally with group_vars and host_vars.

Set group variables inline in INI inventory

Intermediate
[webservers:vars] http_port=8080 env=production

↓ Click command to explain

When to use this

Attach variables to every host in a group directly inside the INI inventory file, without a separate group_vars file.

Gotcha

This works but does not scale well. Most teams move group variables into a dedicated group_vars/<groupname>.yml file once there are more than a couple of them.

Nest groups with children

Intermediate
[production:children] webservers databases

↓ Click command to explain

When to use this

Build a parent group made up of other groups, so a single pattern like production can target every host in both webservers and databases.

Print the full resolved inventory as JSON

Beginner
ansible-inventory --list

↓ Click command to explain

--list Outputs the entire inventory, all groups and hosts, as JSON

When to use this

See exactly how Ansible has parsed and merged your inventory sources into one structure, including all variables.

Visualize inventory as a group tree

Beginner
ansible-inventory --graph

↓ Click command to explain

--graph Prints groups and their child groups and hosts as an indented tree

When to use this

Get a quick, human-readable view of how groups nest inside each other, much easier to scan than the raw JSON output.

Print resolved variables for one host

Beginner
ansible-inventory --host web1.example.com

↓ Click command to explain

--host Prints every variable resolved for a single named host

When to use this

Check exactly which variables a specific host will see once group_vars, host_vars, and inventory variables are all merged together.

Print the resolved inventory as YAML

Intermediate
ansible-inventory --list --yaml

↓ Click command to explain

--yaml Formats the --list output as YAML instead of the default JSON

When to use this

Read the full resolved inventory in a more readable format than raw JSON, useful for a quick manual review.

Target the implicit all group

Beginner
ansible all --list-hosts

↓ Click command to explain

When to use this

all is a built-in group containing every host in the inventory, useful for fleet-wide checks like connectivity or fact gathering.

Target hosts that belong to no group

Intermediate
ansible ungrouped --list-hosts

↓ Click command to explain

When to use this

ungrouped is a built-in group for any host defined in inventory but not assigned to a named group, useful for catching inventory hygiene mistakes.

Target a group excluding another group

Advanced
ansible webservers:!staging --list-hosts

↓ Click command to explain

When to use this

Target every webserver except the ones also in the staging group, useful for production-only fleet-wide operations.

Gotcha

The exclude operator only removes hosts that are also matched by an earlier part of the pattern. It does not work as a standalone negative pattern on its own.

Target hosts in both of two groups

Advanced
ansible webservers:&staging --list-hosts

↓ Click command to explain

When to use this

Target only the hosts that appear in both webservers and staging, a true intersection of two groups.

Target hosts by wildcard hostname

Intermediate
ansible 'web*' --list-hosts

↓ Click command to explain

When to use this

Target every host whose name matches a shell-style wildcard, useful when hosts are not all in one clean group.

Target hosts by regular expression

Advanced
ansible '~web[0-9]+\.example\.com' --list-hosts

↓ Click command to explain

When to use this

Target hosts by a full regular expression when a wildcard is not precise enough, marked with a leading tilde.

Use a dynamic inventory plugin

Advanced
ansible-inventory -i inventory/aws_ec2.yml --graph

↓ Click command to explain

When to use this

Pull inventory live from a cloud provider's API using an inventory plugin config file instead of maintaining a static host list by hand.

Gotcha

A dynamic inventory source is still just a file, but its extension and a plugin: key inside it tell Ansible to run a plugin instead of parsing it as static INI or YAML.

Group variables in a dedicated file

Intermediate
# group_vars/webservers.yml http_port: 8080 max_clients: 200

↓ Click command to explain

When to use this

The standard way to attach variables to a group at scale: a YAML file named after the group under a group_vars directory next to the inventory.

Host variables in a dedicated file

Intermediate
# host_vars/web1.example.com.yml ansible_host: 10.0.1.5 ansible_user: deploy

↓ Click command to explain

When to use this

The standard way to attach variables to a single host at scale: a YAML file named after the host under a host_vars directory next to the inventory.

Override a variable at the command line

Intermediate
ansible-playbook site.yml -e 'env=staging'

↓ Click command to explain

-e Sets an extra variable, the highest-precedence variable source in Ansible

When to use this

Override any variable defined anywhere else, since extra vars sit at the very top of Ansible's variable precedence order.

Gotcha

The simplified precedence order, lowest to highest: role defaults, inventory group_vars and host_vars, play vars, role vars, task vars, then extra vars (-e) last and highest. When two definitions of the same variable conflict, the higher one always wins.

Pass structured JSON as extra vars

Advanced
ansible-playbook site.yml -e '{"key":"value"}'

↓ Click command to explain

When to use this

Pass a nested structure of variables directly on the command line using inline JSON instead of a flat key=value pair.

Inspect a host's fully resolved variables

Intermediate
ansible web1.example.com -m debug -a 'var=hostvars[inventory_hostname]'

↓ Click command to explain

When to use this

Print every variable Ansible has resolved for a specific host at run time, combining inventory, group_vars, host_vars, and facts.

Copy a file with the copy module

Beginner
ansible all -m copy -a 'src=app.conf dest=/etc/app.conf mode=0644'

↓ Click command to explain

When to use this

Push a static file from the control node to every matched host, setting its permissions in the same task.

Render a Jinja2 template with the template module

Beginner
ansible all -m template -a 'src=app.conf.j2 dest=/etc/app.conf'

↓ Click command to explain

When to use this

Render a config file from a Jinja2 template, substituting variables per host, instead of copying an identical static file everywhere.

Gotcha

The source path is always relative to a templates/ directory next to the playbook or role, not an arbitrary filesystem path.

Create a directory with the file module

Beginner
ansible all -m file -a 'path=/opt/app state=directory mode=0755'

↓ Click command to explain

When to use this

Ensure a directory exists with specific permissions before deploying application files into it.

Remove a file with the file module

Beginner Destructive
ansible all -m file -a 'path=/tmp/old.log state=absent'

↓ Click command to explain

When to use this

Remove a file or directory, including recursively for a directory, cleanly and idempotently.

Gotcha

state=absent on a directory removes it and everything inside it, with no confirmation prompt. Double-check the path before running this against a real fleet.

Create a symlink with the file module

Intermediate
ansible all -m file -a 'src=/opt/app/current dest=/opt/app/releases/1 state=link'

↓ Click command to explain

When to use this

Point a stable path at a versioned release directory, the classic pattern for atomic, symlink-based deployments.

Restart a service with the service module

Beginner
ansible all -m service -a 'name=nginx state=restarted'

↓ Click command to explain

When to use this

Restart a service using whichever init system the host actually runs, without hardcoding systemctl or service commands.

Enable and start a service with systemd

Beginner
ansible all -m systemd -a 'name=nginx enabled=yes state=started'

↓ Click command to explain

When to use this

Ensure a service is both running now and set to start on boot, specifically on hosts using systemd.

Reload systemd unit files

Intermediate
ansible all -m systemd -a 'daemon_reload=yes'

↓ Click command to explain

When to use this

Reload systemd's configuration after deploying a new or changed .service unit file, so systemd picks up the change.

Gotcha

Forgetting daemon_reload after editing a unit file is one of the most common causes of 'I changed the service file but nothing happened' confusion.

Install a package with the generic package module

Beginner
ansible all -m package -a 'name=curl state=present'

↓ Click command to explain

When to use this

Install a package across a mixed fleet of Debian and RHEL-family hosts without branching logic, since package delegates to the right backend automatically.

Refresh the apt package cache

Beginner
ansible all -m apt -a 'update_cache=yes'

↓ Click command to explain

When to use this

Refresh apt's package index before installing packages, the equivalent of apt-get update.

Install a package with apt

Beginner
ansible all -m apt -a 'name=nginx state=present'

↓ Click command to explain

When to use this

Install a package on Debian or Ubuntu hosts specifically, with access to apt-only parameters like update_cache.

Upgrade every package with apt

Intermediate Destructive
ansible all -m apt -a 'upgrade=dist'

↓ Click command to explain

When to use this

Apply every available package upgrade across a fleet, equivalent to apt-get dist-upgrade.

Gotcha

A full dist upgrade can restart services or even require a reboot depending on what was updated. Run this with --check first and schedule it deliberately, never as a surprise.

Install a package with yum

Beginner
ansible all -m yum -a 'name=httpd state=latest'

↓ Click command to explain

When to use this

Install or update a package on RHEL, CentOS, or Fedora hosts using yum (dnf is used automatically where yum is just an alias).

Create a user account with the user module

Beginner
ansible all -m user -a 'name=deploy shell=/bin/bash groups=sudo append=yes'

↓ Click command to explain

When to use this

Create a deploy or service account with a specific shell and group membership as part of server bootstrapping.

Gotcha

Always pass append=yes when adding to groups, otherwise groups= replaces the user's entire group list instead of adding to it.

Remove a user account with the user module

Intermediate Destructive
ansible all -m user -a 'name=olduser state=absent remove=yes'

↓ Click command to explain

When to use this

Fully deprovision a user account, including their home directory, when decommissioning access.

Gotcha

state=absent alone leaves the home directory behind. Add remove=yes to actually delete the home directory and mail spool along with the account.

Create a group with the group module

Beginner
ansible all -m group -a 'name=deployers state=present'

↓ Click command to explain

When to use this

Ensure a group exists before assigning users to it, useful when provisioning permissions ahead of the accounts that need them.

Clone a Git repository with the git module

Intermediate
ansible all -m git -a 'repo=https://github.com/example/app.git dest=/opt/app version=main'

↓ Click command to explain

When to use this

Deploy application code by cloning or updating a Git repository directly to a target directory on the remote host.

Add a cron job with the cron module

Beginner
ansible all -m cron -a 'name="daily backup" minute=0 hour=2 job="/opt/scripts/backup.sh"'

↓ Click command to explain

When to use this

Manage a crontab entry idempotently, identified by its name comment so re-running the task never creates duplicate entries.

Gotcha

The name parameter is not just a label. Ansible uses it as the marker comment to find and update the entry on future runs, so keep it stable once set.

Remove a cron job with the cron module

Intermediate Destructive
ansible all -m cron -a 'name="old job" state=absent'

↓ Click command to explain

When to use this

Remove a previously managed cron entry cleanly by its name, without disturbing any other jobs in the same crontab.

Run a command with the command module

Beginner
ansible all -m command -a 'systemctl status nginx'

↓ Click command to explain

When to use this

Run a single command directly, without shell interpretation, which is safer and the recommended default over shell.

Gotcha

command does not process shell operators like |, >, or &&. If the command needs any of those, use shell instead.

Run a piped command with the shell module

Beginner
ansible all -m shell -a 'ps aux | grep nginx'

↓ Click command to explain

When to use this

Run a command that specifically needs shell features like pipes, which the command module cannot process.

Run a raw command with no Python dependency

Advanced
ansible all -m raw -a 'which python3'

↓ Click command to explain

When to use this

Run a bare SSH command on a host that has no Python interpreter yet, most commonly used to bootstrap Python itself on a fresh image.

Gotcha

raw is the one module that does not require Python on the target at all, since it skips Ansible's usual module machinery entirely and just runs the command over SSH.

Print a variable's value with debug

Beginner
ansible all -m debug -a 'var=ansible_facts'

↓ Click command to explain

When to use this

Print the current value of a variable or fact during troubleshooting, without modifying anything.

Print a custom message with debug

Beginner
ansible all -m debug -a "msg='deployment starting'"

↓ Click command to explain

When to use this

Print a plain custom message during a run, often used to mark a checkpoint or explain what a following task is about to do.

Set a runtime variable with set_fact

Intermediate
ansible all -m set_fact -a 'app_version=1.2.3'

↓ Click command to explain

When to use this

Compute or set a variable during a run that later tasks in the same play can reference, without it needing to exist in inventory or group_vars.

Gotcha

A fact set with set_fact only persists for the current play by default. Add cacheable=yes with fact caching enabled if it needs to survive into a later play or run.

Ensure a line exists in a file

Beginner
ansible all -m lineinfile -a "path=/etc/hosts line='127.0.0.1 app.local'"

↓ Click command to explain

When to use this

Add or update a single line in an existing config file without touching the rest of its contents.

Replace a matching line with lineinfile

Intermediate
ansible all -m lineinfile -a "path=/etc/ssh/sshd_config regexp='^PermitRootLogin' line='PermitRootLogin no'"

↓ Click command to explain

When to use this

Replace whatever an existing setting currently is, rather than blindly appending, by matching it with a regular expression first.

Gotcha

Without regexp, lineinfile only checks for an exact match of line and will append a duplicate rather than replacing a differently-formatted existing line.

Manage a multi-line block in a file

Intermediate
ansible all -m blockinfile -a "path=/etc/motd block='Managed by Ansible'"

↓ Click command to explain

When to use this

Insert or update a multi-line block of text wrapped in marker comments, so re-running the task updates the block instead of duplicating it.

Download a file with get_url

Beginner
ansible all -m get_url -a 'url=https://example.com/app.tar.gz dest=/tmp/app.tar.gz mode=0644'

↓ Click command to explain

When to use this

Download a file directly on the remote host from an HTTP, HTTPS, or FTP URL, skipping the control node entirely.

Extract an archive with unarchive

Intermediate
ansible all -m unarchive -a 'src=/tmp/app.tar.gz dest=/opt/app remote_src=yes'

↓ Click command to explain

When to use this

Extract a tar or zip archive that already exists on the remote host, commonly chained right after get_url.

Gotcha

remote_src=yes is essential here. Without it, unarchive assumes src is a path on the control node and tries to copy it over first.

Wait for a port to become open

Intermediate
ansible all -m wait_for -a 'port=8080 delay=5 timeout=60'

↓ Click command to explain

When to use this

Pause a playbook until an application has actually started listening on its port, before running a health check task against it.

Wait for a file to disappear

Advanced
ansible all -m wait_for -a 'path=/tmp/deploy.lock state=absent'

↓ Click command to explain

When to use this

Pause until a lock file created by a deploy script is removed, coordinating Ansible with an external process.

Call an HTTP endpoint with uri

Intermediate
ansible all -m uri -a 'url=http://localhost/health return_content=yes'

↓ Click command to explain

When to use this

Hit a health check or API endpoint and inspect the response, useful right after a deploy to confirm the app came up healthy.

Check if a file exists with stat

Beginner
ansible all -m stat -a 'path=/etc/app.conf'

↓ Click command to explain

When to use this

Check whether a file exists and inspect its permissions or checksum, typically registered into a variable and used in a later when condition.

Fail fast with a pre-flight assertion

Advanced
ansible all -m assert -a "that='ansible_memtotal_mb > 512'"

↓ Click command to explain

When to use this

Fail a play early and loudly if a required condition is not met, such as a minimum amount of memory before installing a memory-hungry service.

Fetch a file from a remote host to the control node

Intermediate
ansible all -m fetch -a 'src=/var/log/app.log dest=/tmp/logs/ flat=yes'

↓ Click command to explain

When to use this

Pull a file from every matched host back to the control node, the reverse direction of copy, commonly used to collect logs.

Gotcha

Without flat=yes, fetch nests each file under a per-host directory on the control node so files from different hosts never collide.

Sync a directory tree with rsync

Advanced
ansible all -m synchronize -a 'src=/local/dir/ dest=/remote/dir/'

↓ Click command to explain

When to use this

Sync a whole directory tree efficiently using rsync under the hood, much faster than copy for large numbers of files.

Gotcha

synchronize requires rsync to be installed on both the control node and the target host, unlike copy which has no such dependency.

Add an SSH public key with authorized_key

Advanced
ansible all -m authorized_key -a "user=deploy key={{ lookup('file', '~/.ssh/id_rsa.pub') }}"

↓ Click command to explain

When to use this

Add a public key to a user's authorized_keys file idempotently, commonly used when bootstrapping a new deploy account.

Reboot a host and wait for it to come back

Advanced Destructive
ansible all -m reboot -a 'reboot_timeout=300' --become

↓ Click command to explain

When to use this

Reboot a host as part of a kernel or system update, and block until it has actually come back online and is reachable again.

Gotcha

Always run this with --limit against a small batch first. Rebooting an entire fleet in one pass with high forks can take down a service simultaneously across every instance.

Gather the list of installed packages

Intermediate
ansible all -m package_facts

↓ Click command to explain

When to use this

Discover exactly which packages and versions are installed on a host, useful for an inventory audit or a when condition based on package presence.

Create a new encrypted file

Beginner
ansible-vault create secrets.yml

↓ Click command to explain

When to use this

Create a brand-new file that is encrypted from the moment it is saved, opening your default editor to write its contents.

Gotcha

The editor used is whatever your EDITOR environment variable points to. If nothing happens when you run this, EDITOR is probably unset.

Edit an existing encrypted file

Beginner
ansible-vault edit secrets.yml

↓ Click command to explain

When to use this

Decrypt a vault file into your editor, let you make changes, and re-encrypt it on save, without ever leaving plaintext on disk.

View an encrypted file without editing

Beginner
ansible-vault view secrets.yml

↓ Click command to explain

When to use this

Read the contents of a vault-encrypted file to your terminal without opening an editor or risking an accidental save.

Encrypt an existing plaintext file

Beginner
ansible-vault encrypt vars/prod.yml

↓ Click command to explain

When to use this

Encrypt a file that already exists as plaintext, turning it into a vault-protected file in place.

Encrypt multiple files at once

Intermediate
ansible-vault encrypt vars/prod.yml vars/staging.yml

↓ Click command to explain

When to use this

Encrypt several related variable files in a single command, all with the same vault password.

Permanently decrypt a file

Intermediate Destructive
ansible-vault decrypt secrets.yml

↓ Click command to explain

When to use this

Remove vault encryption from a file entirely, leaving it as plain, readable text on disk.

Gotcha

This leaves real secrets sitting in plaintext on disk. Only do this briefly and intentionally, and never commit the decrypted file to version control.

Change a vault file's password

Intermediate
ansible-vault rekey secrets.yml

↓ Click command to explain

When to use this

Rotate the password protecting a vault file without needing to fully decrypt and re-encrypt it manually.

Gotcha

Rekeying only changes the password for that one file. Every other vault file encrypted with the old password still needs rekeying separately, or a shared vault ID setup.

Encrypt a single value inline

Intermediate
ansible-vault encrypt_string 'S3cr3tP@ss' --name 'db_password'

↓ Click command to explain

--name Names the resulting encrypted variable, formatting the output ready to paste into a YAML file

When to use this

Encrypt just one secret value, like a single password, so it can be pasted directly into an otherwise plaintext variables file.

Encrypt a value piped from stdin

Advanced
echo -n 'S3cr3tP@ss' | ansible-vault encrypt_string --stdin-name 'db_password'

↓ Click command to explain

--stdin-name Reads the value to encrypt from stdin instead of a command-line argument

When to use this

Encrypt a secret without it ever appearing in your shell history, which passing it directly as a command-line argument would risk.

Gotcha

echo -n is important here. A trailing newline from a plain echo becomes part of the encrypted value and can silently break comparisons or authentication.

Decrypt using a password file

Intermediate
ansible-playbook site.yml --vault-password-file ~/.vault_pass

↓ Click command to explain

--vault-password-file Reads the vault password from a file, or from a script's stdout if the file is executable

When to use this

Supply the vault password non-interactively, most commonly needed for CI/CD pipelines running playbooks unattended.

Prompt interactively for the vault password

Beginner
ansible-playbook site.yml --ask-vault-pass

↓ Click command to explain

--ask-vault-pass Prompts interactively for the vault password at run time

When to use this

Run a playbook that touches vault-encrypted files interactively, typing the password once at the start of the run.

Encrypt a file under a named vault ID

Advanced
ansible-vault encrypt secrets.yml --vault-id prod@prompt

↓ Click command to explain

--vault-id Encrypts under a named vault identity, prompting interactively for that identity's password

When to use this

Encrypt a file under a labeled identity like prod, so different environments can use entirely separate vault passwords.

Run a playbook using a named vault ID

Advanced
ansible-playbook site.yml --vault-id prod@~/.vault_pass_prod

↓ Click command to explain

When to use this

Decrypt files that were encrypted under a specific named vault identity, pairing the identity with its password file.

Run a playbook with multiple vault IDs at once

Advanced
ansible-playbook site.yml --vault-id dev@~/.vault_dev --vault-id prod@~/.vault_prod

↓ Click command to explain

When to use this

Decrypt files encrypted under different vault identities in a single run, such as a playbook that references both dev and prod secrets.

Gotcha

Ansible tries each supplied vault-id against each encrypted file until one works, so the order does not need to match which file uses which identity.

Create a new file under a named vault ID

Advanced
ansible-vault create --vault-id prod@prompt secrets_prod.yml

↓ Click command to explain

When to use this

Create a brand-new secrets file that is encrypted from the start under a specific named vault identity.

Scaffold a new role

Beginner
ansible-galaxy init my_role

↓ Click command to explain

When to use this

Generate the standard role directory structure (tasks, handlers, defaults, templates, and more) instead of building it by hand.

Gotcha

The generated defaults/main.yml is where role variables meant to be easily overridden should live, since role defaults have the lowest variable precedence of any source.

Install a role from Galaxy

Beginner
ansible-galaxy install geerlingguy.nginx

↓ Click command to explain

When to use this

Install a community-maintained role directly from Ansible Galaxy instead of writing one from scratch.

Install every role listed in requirements.yml

Beginner
ansible-galaxy install -r requirements.yml

↓ Click command to explain

-r Reads a list of roles (and their versions and sources) from a requirements file

When to use this

Install every external role a project depends on in one command, keeping dependencies version-pinned and reproducible.

List installed roles

Beginner
ansible-galaxy list

↓ Click command to explain

When to use this

See every role currently installed locally along with its version, useful before upgrading or auditing dependencies.

Remove an installed role

Beginner Destructive
ansible-galaxy remove geerlingguy.nginx

↓ Click command to explain

When to use this

Remove a role that is no longer needed from the local roles directory.

Search Galaxy for a role

Beginner
ansible-galaxy search nginx

↓ Click command to explain

When to use this

Search the public Galaxy registry for roles matching a keyword before deciding whether to write one yourself.

Show details about a Galaxy role

Intermediate
ansible-galaxy info geerlingguy.nginx

↓ Click command to explain

When to use this

Check a role's available versions, dependencies, and last update date before pinning it in requirements.yml.

Install a collection

Beginner
ansible-galaxy collection install community.general

↓ Click command to explain

When to use this

Install a collection, a bundle of modules, plugins, and roles from a single namespace, most commonly community.general for modules not in ansible-core.

Gotcha

Collections and roles are installed separately with different subcommands. A missing module usually means the collection needs installing, not just a role.

Install collections from a requirements file

Intermediate
ansible-galaxy collection install -r requirements.yml

↓ Click command to explain

When to use this

Install every collection dependency a project needs in one command, version-pinned and reproducible across machines.

List installed collections

Beginner
ansible-galaxy collection list

↓ Click command to explain

When to use this

See every collection currently installed and its version, useful for confirming a required collection is actually present.

Verify an installed collection against its checksums

Advanced
ansible-galaxy collection verify community.general

↓ Click command to explain

When to use this

Confirm an installed collection has not been modified locally, comparing its files against the checksums published to Galaxy.

Build a collection into a distributable archive

Advanced
ansible-galaxy collection build

↓ Click command to explain

When to use this

Package a collection you maintain into a tarball ready to publish or install elsewhere, run from inside the collection's root directory.

Publish a collection to Galaxy

Advanced
ansible-galaxy collection publish my_namespace-my_collection-1.0.0.tar.gz

↓ Click command to explain

When to use this

Publish a built collection tarball to Ansible Galaxy or a private Automation Hub so others can install it.

Install a specific version of a role

Intermediate
ansible-galaxy install geerlingguy.nginx,3.1.0

↓ Click command to explain

When to use this

Pin a role to an exact version rather than always installing latest, keeping a project's dependencies reproducible.

Install a role into a specific directory

Intermediate
ansible-galaxy install geerlingguy.nginx -p ./roles

↓ Click command to explain

-p Sets the directory roles are installed into, instead of the default roles path

When to use this

Install a role directly into a project-local roles directory so it can be committed and versioned alongside the playbook.

Scaffold a new collection

Advanced
ansible-galaxy collection init my_namespace.my_collection

↓ Click command to explain

When to use this

Generate the standard collection directory structure when starting a new collection to hold custom modules, plugins, or roles.

Trigger a handler with notify

Intermediate
tasks: - name: Update nginx config template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf notify: Restart nginx handlers: - name: Restart nginx service: name: nginx state: restarted

↓ Click command to explain

When to use this

Restart a service only when the config that feeds it actually changed, instead of restarting it unconditionally on every run.

Gotcha

A handler is matched to notify by its name, not by which task called it. Renaming a handler without updating every notify that references it silently breaks the trigger.

Handlers run in the order they are defined

Intermediate
handlers: - name: Restart nginx service: name: nginx state: restarted - name: Reload firewall service: name: firewalld state: reloaded

↓ Click command to explain

When to use this

Handlers always run in the order they are listed under handlers, regardless of the order in which tasks notified them.

Run notified handlers even if a later task fails

Advanced
ansible-playbook site.yml --force-handlers

↓ Click command to explain

--force-handlers Runs any handlers that were already notified even if a later task in the play fails

When to use this

Ensure a service still restarts to pick up a config change even if an unrelated later task in the same play fails.

Gotcha

By default, handlers only run at the very end of a successful play. A failure anywhere in the play normally skips every pending handler entirely.

Run a task only when a condition is true

Beginner
- name: Install nginx on Debian family apt: name: nginx state: present when: ansible_facts['os_family'] == "Debian"

↓ Click command to explain

When to use this

Run a task only on hosts matching a condition, most commonly branching behavior based on OS family or a variable.

Gotcha

when is evaluated as a raw Jinja2 expression, so it should not be wrapped in {{ }} the way a value elsewhere in a task would be.

Combine multiple conditions with when

Intermediate
- name: Restart app only in production service: name: myapp state: restarted when: - env == "production" - deploy_enabled | bool

↓ Click command to explain

When to use this

A list of conditions under when is implicitly ANDed together, all must be true for the task to run.

Loop a task over a simple list

Beginner
- name: Create multiple users user: name: "{{ item }}" state: present loop: - alice - bob - carol

↓ Click command to explain

When to use this

Run the same task once per item in a list, referencing the current item with {{ item }}, instead of writing one task per user.

Loop with the older with_items syntax

Intermediate
- name: Install packages apt: name: "{{ item }}" with_items: - nginx - curl - git

↓ Click command to explain

When to use this

The older looping syntax, still common in existing playbooks and still fully supported.

Gotcha

with_items still works but loop is the modern recommended syntax, and it composes more predictably with filters and lookups than the older with_ family.

Loop over a list of dictionaries

Advanced
- name: Create users with groups user: name: "{{ item.name }}" groups: "{{ item.groups }}" loop: - { name: 'alice', groups: 'sudo' } - { name: 'bob', groups: 'developers' }

↓ Click command to explain

When to use this

Loop over structured data instead of plain strings, accessing each item's fields with dot notation like item.name.

Fall back to a default value with the default filter

Beginner
ansible localhost -m debug -a "msg={{ myvar | default('fallback') }}"

↓ Click command to explain

When to use this

Provide a fallback value for a variable that might be undefined, avoiding an undefined variable error.

Transform text case with upper and lower

Beginner
ansible localhost -m debug -a "msg={{ 'hello world' | upper }}"

↓ Click command to explain

When to use this

Transform string case inside a template or task, useful for normalizing values like environment names before comparing them.

Join a list into a delimited string

Intermediate
ansible localhost -m debug -a "msg={{ ['a','b','c'] | join(',') }}"

↓ Click command to explain

When to use this

Turn a list variable into a single delimited string, commonly used when rendering a config value that expects a comma or space separated list.

Pretty-print a variable as YAML

Intermediate
ansible localhost -m debug -a "msg={{ myvar | to_nice_yaml }}"

↓ Click command to explain

When to use this

Render a complex variable as readable, indented YAML inside a template or debug message, instead of a single-line dump.

Replace text with a regular expression

Advanced
ansible localhost -m debug -a "msg={{ 'foo123' | regex_replace('[0-9]+', '') }}"

↓ Click command to explain

When to use this

Strip or replace parts of a string using a regular expression, useful for cleaning up a value pulled from facts before using it elsewhere.

Pick one of two values with ternary

Advanced
ansible localhost -m debug -a "msg={{ (env == 'prod') | ternary('production','staging') }}"

↓ Click command to explain

When to use this

Pick between two values based on a boolean condition in a single expression, a compact alternative to a multi-line when/set_fact pair.

Filter a list of dictionaries with selectattr

Advanced
ansible localhost -m debug -a "msg={{ users | selectattr('active') | list }}"

↓ Click command to explain

When to use this

Filter a list of dictionaries down to only the items matching an attribute condition, such as only active users.

Validate a rendered template before saving it

Advanced
ansible all -m template -a "src=sudoers.j2 dest=/etc/sudoers validate='visudo -cf %s'"

↓ Click command to explain

validate= Runs a validation command against a temp copy of the rendered file before overwriting the real destination

When to use this

Validate a rendered config file with its own tool, like visudo, before it ever overwrites the live file, so a broken render never lands on disk.

Gotcha

The %s in the validate command is replaced with the path to the temporary rendered file, not the real destination, which is exactly what makes this safe.

Run with connection-level verbosity

Intermediate
ansible-playbook site.yml -vvv

↓ Click command to explain

-vvv Shows connection details including the actual SSH command being executed

When to use this

Debug a connection failure by seeing the exact SSH command Ansible is running under the hood, including which key and user it picked.

Run with maximum verbosity

Advanced
ansible-playbook site.yml -vvvv

↓ Click command to explain

-vvvv Adds connection plugin debugging on top of -vvv, the most detailed verbosity level

When to use this

Debug the deepest connection-layer problems, such as an SSH multiplexing or ControlPath issue that -vvv does not fully expose.

Debug a connectivity failure

Intermediate
ansible all -m ping -vvv

↓ Click command to explain

When to use this

Combine the simplest possible module with high verbosity to isolate whether a failure is about connectivity at all, before suspecting anything else.

Debug a privilege escalation failure

Intermediate
ansible all -m command -a 'whoami' --become -vvv

↓ Click command to explain

When to use this

Isolate whether a task failure is actually a sudo or become configuration problem, separate from the task's own logic.

Confirm which Python interpreter Ansible is using

Intermediate
ansible all -m setup -a 'filter=ansible_python_interpreter'

↓ Click command to explain

When to use this

Diagnose a module failure caused by Ansible picking an unexpected Python interpreter on a host with multiple Python versions installed.

Fall back to the paramiko connection plugin

Advanced
ansible all -m ping -c paramiko

↓ Click command to explain

-c Selects a specific connection plugin instead of the default openssh-based one

When to use this

Work around an issue specific to the system OpenSSH client by switching to Ansible's Python-based paramiko connection plugin.

Pass extra SSH arguments for a difficult connection

Advanced
ansible all -m ping --ssh-common-args='-o StrictHostKeyChecking=no'

↓ Click command to explain

--ssh-common-args Passes extra arguments through to every SSH invocation Ansible makes

When to use this

Work around a host key checking failure or pass a jump host argument when connecting through a bastion.

Gotcha

Disabling StrictHostKeyChecking removes a real security check. Fine for short-lived lab or CI environments, but avoid it as a permanent fix for real infrastructure.

Increase the connection timeout

Intermediate
ansible all -m ping --timeout=30

↓ Click command to explain

--timeout Sets the connection timeout in seconds, default is 10

When to use this

Give slow or high-latency hosts more time to respond before Ansible gives up on the connection.

Re-run only the hosts that failed last time

Advanced
ansible-playbook site.yml --limit @site.retry

↓ Click command to explain

--limit @file.retry Limits the run to only the hosts listed in an auto-generated .retry file from the previous failed run

When to use this

Re-run a playbook against only the hosts that failed last time, instead of the entire fleet, after fixing the underlying issue.

Gotcha

Retry files are only generated when a run actually fails, and only if retry_files_enabled is not disabled in ansible.cfg.

Use a non-sudo privilege escalation method

Advanced
ansible all -m ping --become --become-method=su

↓ Click command to explain

--become-method Selects which privilege escalation method to use, default is sudo

When to use this

Work around a host where sudo is not configured but su is available, or another become plugin like doas or pbrun is required.

Show only non-default configuration values

Intermediate
ansible-config dump --only-changed

↓ Click command to explain

--only-changed Shows only settings that differ from their built-in default value

When to use this

Cut through hundreds of default settings and see only the handful your project has actually overridden.

Get module documentation as JSON

Intermediate
ansible-doc --json apt

↓ Click command to explain

--json Outputs module documentation as machine-readable JSON instead of formatted text

When to use this

Get a module's parameter list as structured JSON, useful for scripting or building tooling around Ansible's own module metadata.

List modules by plugin type

Beginner
ansible-doc -l -t module

↓ Click command to explain

-t Restricts the listing to a specific plugin type, such as module, lookup, or filter

When to use this

List only modules and exclude other plugin types like lookups or filters when browsing what is available.

Test a limit pattern before running for real

Beginner
ansible-playbook site.yml --limit 'web*' --list-hosts

↓ Click command to explain

When to use this

Confirm a wildcard or complex limit pattern resolves to exactly the hosts you expect before actually running the playbook against them.

Step through from a specific task

Advanced
ansible-playbook site.yml --step --start-at-task='Restart service'

↓ Click command to explain

When to use this

Combine resuming from a specific task with interactive step confirmation, useful for carefully walking through the exact section of a playbook that failed.

Dump gathered facts to files for offline inspection

Intermediate
ansible all -m setup --tree /tmp/facts

↓ Click command to explain

--tree Writes each host's output to its own file under the given directory instead of printing to the terminal

When to use this

Capture full facts for every host into separate files for offline diffing or review, instead of scrolling through terminal output for a large fleet.

Reference Tools

The most commonly used Ansible modules, each with a one-line description and the minimal syntax to use it inside a task. Search by module name or what it does.

copy

Copies a file from the control node to remote hosts.

copy: {src: app.conf, dest: /etc/app.conf, mode: '0644'}

template

Renders a Jinja2 template file and copies the result to remote hosts.

template: {src: app.conf.j2, dest: /etc/app.conf}

file

Manages file and directory properties: create, delete, permissions, symlinks.

file: {path: /opt/app, state: directory, mode: '0755'}

service

Starts, stops, or restarts a system service using the host's native service manager.

service: {name: nginx, state: restarted, enabled: true}

systemd

Manages services under systemd specifically, including reloading unit files after a change.

systemd: {name: nginx, state: started, enabled: true, daemon_reload: true}

package

Installs or removes a package using whichever package manager the target OS actually has.

package: {name: curl, state: present}

apt

Installs or removes packages on Debian and Ubuntu systems using apt.

apt: {name: nginx, state: present, update_cache: true}

yum

Installs or removes packages on RHEL, CentOS, and Fedora systems using yum or dnf.

yum: {name: httpd, state: latest}

user

Creates, modifies, or removes a user account on the remote host.

user: {name: deploy, shell: /bin/bash, groups: sudo, append: true}

group

Creates or removes a group on the remote host.

group: {name: deployers, state: present}

git

Clones or updates a Git repository on the remote host.

git: {repo: 'https://github.com/example/app.git', dest: /opt/app, version: main}

cron

Manages a cron job entry in a user's crontab.

cron: {name: 'daily backup', minute: '0', hour: '2', job: /opt/scripts/backup.sh}

command

Runs a command directly without a shell, so pipes, redirects, and env vars are not interpreted.

command: /usr/bin/systemctl status nginx

shell

Runs a command through /bin/sh, so shell features like pipes and redirects actually work.

shell: ps aux | grep nginx

debug

Prints a message or variable value during a playbook run, mainly for troubleshooting.

debug: {msg: 'Deployment starting'}

set_fact

Sets a variable at runtime that is available to later tasks in the same play.

set_fact: {app_version: '1.2.3'}

lineinfile

Ensures a specific line exists in a file, adding or replacing it as needed.

lineinfile: {path: /etc/hosts, line: '127.0.0.1 app.local'}

blockinfile

Inserts or updates a marked, multi-line block of text in a file, safely re-runnable.

blockinfile: {path: /etc/motd, block: 'Managed by Ansible'}

get_url

Downloads a file from HTTP, HTTPS, or FTP to the remote host.

get_url: {url: 'https://example.com/app.tar.gz', dest: /tmp/app.tar.gz, mode: '0644'}

unarchive

Extracts a tar or zip archive on the remote host, optionally unpacking one already fetched with get_url.

unarchive: {src: /tmp/app.tar.gz, dest: /opt/app, remote_src: true}

wait_for

Pauses playbook execution until a port, file, or string condition is met.

wait_for: {port: 8080, delay: 5, timeout: 60}

uri

Sends an HTTP request and can validate the response, useful for health checks and API calls.

uri: {url: 'http://localhost/health', return_content: true}

stat

Retrieves file or file system status information, commonly used with a when condition.

stat: {path: /etc/app.conf}

assert

Fails the play with a clear message if a condition is not true, useful for pre-flight checks.

assert: {that: inventory_hostname is defined}

Frequently Asked Questions

Ansible is an agentless automation tool that configures servers over plain SSH, running small Python programs called modules on the remote host and then removing them, rather than requiring a permanently installed agent like some competing tools. A playbook describes the desired end state of a server in YAML, such as a package being installed or a service running, and Ansible figures out whether any change is actually needed before making it. This agentless design is a big part of why Ansible spread so quickly through operations teams: there is nothing to install or maintain on the managed hosts beyond Python and SSH, both of which almost every Linux server already has.

The reason ansible-playbook shows up in daily operations work so often is that server configuration drifts constantly. A manually patched package, a config file edited directly during an incident, or a cron job added by hand during a one-off fix all quietly pull a server away from its intended state. Running the same playbook repeatedly and safely, a property called idempotency, is what lets a team re-apply the source of truth on a schedule or after every incident and trust that only the actual drift gets corrected, not everything getting torn down and rebuilt from scratch.

The single most important mental model for working with Ansible is that almost every module describes a desired state, not a sequence of steps. The apt module's state: present does not mean run apt-get install, it means ensure this package ends up installed, checking first whether it already is. This is exactly why running a playbook twice in a row against an already-configured server reports no changes the second time: every module already checked the current state and found nothing left to do. Commands like ansible-playbook --check and --diff exist specifically to preview that comparison before committing to it.

Three mistakes account for most of the frustration engineers hit when learning Ansible. The first is reaching for the shell or command module for something a dedicated module already handles, which throws away idempotency and the clean check-mode diff a real module provides. The second is forgetting that handlers only fire once, at the end of a play, and only if the notifying task actually reported a change, which trips people up when they expect a handler to run immediately or every time. The third is not understanding variable precedence, so an override in group_vars gets silently beaten by a leftover -e flag from a previous command in shell history, and the wrong value wins with no error message pointing at why.