Excluding Hosts in Ansible Playbooks: How to Skip Specific Servers with Patterns


1 views

When managing infrastructure with Ansible, we often need to apply playbooks to most hosts while excluding specific server groups. While command-line patterns like all,!ntpservers work perfectly, implementing the same exclusion logic within playbooks requires different approaches.

Here are three effective ways to exclude hosts in playbooks:

1. Using the hosts: directive with patterns

- name: Apply to all hosts except database servers
  hosts: all:!dbservers
  tasks:
    - name: Install monitoring client
      ansible.builtin.package:
        name: monitoring-agent
        state: present

2. Leveraging Group Variables

- name: Conditional execution based on group membership
  hosts: all
  tasks:
    - name: Install client on non-server hosts
      ansible.builtin.package:
        name: client-software
        state: present
      when: "'servers' not in group_names"

3. Combining Multiple Patterns

- name: Complex exclusion pattern
  hosts: "{{ groups['web_servers'] + groups['app_servers'] }}"
  tasks:
    - name: Configure load balancer settings
      ansible.builtin.template:
        src: templates/lb_config.j2
        dest: /etc/lb_config.conf

For more complex scenarios, consider these approaches:

Dynamic Inventory Filtering

- name: Filter hosts using JMESPath
  hosts: "{{ hostvars | dict2items | json_query(query) | map(attribute='value') | list }}"
  vars:
    query: "[?value.group_names.contains(@, 'production') && !value.group_names.contains(@, 'database')]"
  tasks:
    - name: Deploy application
      ansible.builtin.copy:
        src: /opt/apps/main.jar
        dest: /opt/apps/

Using Custom Filters

- name: Apply custom filter plugin
  hosts: "{{ all_servers | exclude('backup_servers') }}"
  tasks:
    - name: Run security update
      ansible.builtin.apt:
        name: "*"
        state: latest
        update_cache: yes

When implementing host exclusions, watch for these issues:

  • Pattern evaluation occurs during inventory parsing, not during play execution
  • Exclusions don't affect included or imported playbooks
  • Dynamic groups might require special handling

For maximum reliability, combine pattern matching with explicit conditionals in tasks when appropriate.

While host patterns are evaluated early, complex exclusions can impact performance:

  • Simple patterns (like all:!group) are fastest
  • Nested conditionals add minimal overhead
  • Custom filters and JMESPath queries should be used judiciously

When managing infrastructure with Ansible, you'll often need to target specific subsets of hosts while excluding others. While command-line patterns like all,!ntpservers work well for ad-hoc commands, implementing this in playbooks requires a different approach.

The most straightforward way is to use exclusion patterns directly in your playbook's hosts directive:


- name: Install client on all hosts except servers
  hosts: all:!servers
  tasks:
    - name: Install client package
      ansible.builtin.package:
        name: my-client-pkg
        state: present

For more complex scenarios, leverage Ansible groups in your inventory:


[web]
web1.example.com
web2.example.com

[db]
db1.example.com

[all_except_db:children]
web

Then reference this group in your playbook:


- name: Configure all hosts except database servers
  hosts: all_except_db
  tasks:
    - name: Apply common configuration
      template:
        src: templates/common.conf.j2
        dest: /etc/common.conf

For runtime decisions, use Ansible's magic variables:


- name: Conditional execution based on host facts
  hosts: all
  tasks:
    - name: Skip servers
      ansible.builtin.command: /usr/bin/install-client
      when: "'server' not in group_names"
  • Document your exclusion logic in playbook comments
  • Use descriptive group names (e.g., non_prod instead of all:!prod)
  • Test exclusion patterns with --list-hosts flag first
  • Consider using tags for more granular control

Here's a complete example for patching non-critical systems:


- name: Apply security patches to non-critical hosts
  hosts: all:!critical:!database
  serial: 20%
  tasks:
    - name: Update all packages
      ansible.builtin.yum:
        name: '*'
        state: latest
        update_cache: yes
      when: ansible_os_family == 'RedHat'