How to Use Ansible Playbook –limit with Multiple Hosts Without Creating Groups


2 views

When working with Ansible playbooks, many engineers encounter a common limitation: the --limit or -l flag doesn't behave as expected when specifying multiple hosts directly in the command line. The standard approach:

ansible-playbook -i inventory.ini -l host1.com host2.com playbook.yml

Results in the frustrating error:

ERROR! the playbook: host2.com could not be found

The fundamental issue lies in how command-line arguments are parsed. Ansible interprets everything after -l as part of the limit pattern until it encounters another flag. To properly specify multiple hosts, you need to:

  1. Quote the entire host pattern
  2. Use proper pattern separators

Solution 1: Comma-separated list

ansible-playbook -i inventory.ini -l "host1.com,host2.com" playbook.yml

Solution 2: Colon-separated list

ansible-playbook -i inventory.ini -l "host1.com:host2.com" playbook.yml

Solution 3: Using wildcard patterns

ansible-playbook -i inventory.ini -l "*.com" playbook.yml

For more complex scenarios, you can combine pattern operators:

# All hosts in datacenter1 except db servers
ansible-playbook -i inventory.ini -l "datacenter1.*:!datacenter1.db*" playbook.yml

For cases where pattern matching isn't sufficient, consider these approaches:

Using temporary inventory files:

echo "host1.com\nhost2.com" > /tmp/hosts && \
ansible-playbook -i inventory.ini -l @/tmp/hosts playbook.yml && \
rm /tmp/hosts

JSON input via stdin:

echo '["host1.com","host2.com"]' | ansible-playbook -i inventory.ini -l @/dev/stdin playbook.yml
  • Forgetting to quote patterns with spaces
  • Mixing different pattern separators inconsistently
  • Not escaping special characters in hostnames
  • Assuming host patterns work the same way across all Ansible versions

Here's how we might deploy to multiple web servers while skipping maintenance hosts:

ansible-playbook -i production.ini \
-l "web*.example.com:!web[09].example.com" \
--extra-vars "deploy_version=1.4.2" \
deploy.yml

This command would target all web servers except web0 and web9, passing along the deployment version as a variable.

Pattern matching behavior has evolved across Ansible versions:

  • 2.4+: Added support for @ prefix for inventory files
  • 2.9+: Improved pattern parsing for IPv6 addresses
  • Recent versions: Better error messages for invalid patterns

When working with Ansible in constrained environments, we often encounter situations where modifying inventory files isn't an option. The --limit (or -l) flag becomes crucial for targeting specific hosts, but its syntax for multiple hosts isn't immediately obvious from the documentation.

The proper syntax for limiting to multiple hosts inline requires using commas to separate hostnames:

ansible-playbook -i inventory.ini -l "server1.example.com,server2.example.com" playbook.yml

Alternatively, you can use patterns:

ansible-playbook -i inventory.ini -l "server[1:2].example.com" playbook.yml

The original error occurs because Ansible interprets space-separated arguments after -l as different parameters. Some key observations:

  • Always quote the host list when using commas
  • The @file syntax works but requires maintaining external files
  • Pattern matching can often replace explicit listing

For complex environments, you can combine patterns with logical operators:

ansible-playbook -i inventory.ini -l "server*.example.com:!server-backup.example.com" playbook.yml

This would target all servers except the backup server.

While --limit is convenient, be aware that:

  • Ansible still parses the entire inventory
  • Connection overhead remains for all matched hosts
  • For very large inventories, explicit host listing may be faster than patterns

Here's a complete command targeting specific web servers for a rolling update:

ansible-playbook -i production.ini \
-l "web-[01:05].prod.example.com" \
--extra-vars "maintenance_mode=true" \
rolling-update.yml