Inventories and ad-hoc commands

Describe your machines in an inventory, connect over SSH without installing an agent, and run one-off tasks from the command line.

The inventory

Ansible is agentless: it copies a small Python program over SSH, runs it on the target, and removes it. The inventory is the list of hosts it may talk to, grouped, and optionally annotated with variables.

# inventory.ini
[web]
web1.example.com
web2.example.com ansible_user=deploy

[database]
db1.example.com

[prod:children]        # a group of groups
web
database

[prod:vars]
ansible_python_interpreter=/usr/bin/python3
ansible_ssh_private_key_file=~/.ssh/prod_ed25519
PatternMatches
allEvery host in the inventory
webThe group named web
web:db1.example.comUnion of a group and a single host
prod:!databaseEverything in prod except the database group
web:&prodHosts that are in both web and prod
web[0]The first host of the group

Ad-hoc commands

Anything you would run once on one machine, you can run on a hundred. The shape is always ansible <pattern> -m <module> -a <arguments>.

# is the connection and the remote Python working?
ansible all -i inventory.ini -m ping

# run a command and print its output
ansible web -i inventory.ini -m shell -a 'df -h /'

# discover facts about the machines
ansible prod -i inventory.ini -m setup -a 'filter=ansible_distribution*'

# copy a file, then confirm it landed
ansible web -i inventory.ini -m copy -a 'src=./app.conf dest=/etc/app.conf mode=0644'

# install a package (needs privilege escalation)
ansible web -i inventory.ini -b -m apt -a 'name=nginx state=present'
  • -i selects the inventory; without it Ansible falls back to /etc/ansible/hosts.
  • -m names the module (ping, copy, apt, service) and -a passes its arguments.
  • -b enables become (sudo) for that run; --become-user names the target user.
  • -f 20 raises the number of parallel forks above the default of five.
  • --limit web2 narrows any pattern further, which is how you test a change safely.

Configuration and safety

# ansible.cfg — keep it beside your playbooks
[defaults]
inventory = ./inventory.ini
host_key_checking = True
forks = 20
retry_files_enabled = False

[privilege_escalation]
become = True
become_method = sudo
become_ask_pass = False

Ansible reads ansible.cfg from the current directory first, then ~/.ansible.cfg, then /etc/ansible/ansible.cfg. Keeping one beside the playbooks makes a repository behave the same on every machine.

⚠️
Ad-hoc commands are not idempotent by default: shell and command run every time they are invoked. Use modules with a state argument for anything that must be safe to repeat, and keep shell for genuinely one-off work.

FAQ

Do I need to install anything on the managed hosts?
No agent. You need SSH access and a Python interpreter. If a host has no Python at all, a small subset of modules can still run through raw.
How do I try a command without changing anything?
Add --check for a dry run and --diff beside it to see which lines would change. Both require the module you use to support check mode.

Playbooks and modules Roles, Vault and idempotency

Last refreshed 2026-09-18.