Mastering Ansible: Complete Guide to ansible.cfg, Inventory, and Playbooks
This article walks through Ansible’s three core components—ansible.cfg settings, inventory definitions (including INI, YAML, and dynamic scripts), and Playbook structure—showing practical examples, performance tweaks, variable hierarchies, conditionals, loops, handlers, tags, blocks, roles, and common pitfalls to help you manage hundreds of servers efficiently.
1. ansible.cfg – the brain of Ansible
Ansible’s behavior is driven almost entirely by configuration files. It searches for configuration in the following order:
./ansible.cfg in the current directory
~/.ansible.cfg in the user’s home directory
/etc/ansible/ansible.cfg (global)
Placing an ansible.cfg in a project directory overrides the global file, allowing independent settings per project.
Commonly changed parameters are under the [defaults] section:
[defaults]
inventory=./hosts
remote_user=ansible
forks=20
timeout=30
host_key_checking=False
log_path=/var/log/ansible.log
retry_files_enabled=False
ansible_managed=This file is managed by Ansible.
roles_path=./roles
library=./libraryKey tips:
Specify multiple inventory files with commas, e.g., inventory=./prod_hosts,./dev_hosts Set forks according to the number of managed hosts (10 for up to 10 hosts, 20‑50 for up to 100, 50‑100 for a few hundred).
Increase timeout when the network is unreliable.
Enable log_path in production and ensure the running user can write to the log directory.
Disable host_key_checking for non‑interactive runs, but keep it enabled in high‑security environments and pre‑populate ~/.ssh/known_hosts.
Turn off retry_files_enabled unless you need automatic retry files.
2. Inventory – the host roster
Inventory tells Ansible which machines to manage, how they are grouped, and what host‑specific variables they have.
2.1 INI format
[webservers]
192.168.1.10
192.168.1.11
web01.example.com
[dbservers]
192.168.1.20
192.168.1.21
[production:children]
webservers
dbserversGroup definitions ( [group]), nested groups ( [group:children]), and host variables can be added directly after a host line, e.g.:
[webservers]
web01 ansible_host=192.168.1.10 ansible_port=2222 ansible_user=deploy
web02 ansible_host=192.168.1.11
[dbservers]
db01 ansible_host=192.168.1.20 ansible_user=rootCommon host variables: ansible_host – real IP or DNS name ansible_port – SSH port (default 22) ansible_user – SSH login user ansible_ssh_private_key_file – private key path ansible_python_interpreter – Python interpreter on the target
2.2 Group variables
Define variables for an entire group with [group:vars]:
[webservers:vars]
ansible_user=deploy
ansible_port=2222
http_port=80
app_env=productionHosts inherit these values unless they override them individually.
2.3 Host range syntax
Use patterns to generate multiple hosts without writing each line:
[webservers]
web[01:10].example.com
[dbservers]
db[01:05:2].example.com # expands to db01, db03, db052.4 YAML inventory
For complex inventories, YAML improves readability:
all:
children:
webservers:
hosts:
web01:
ansible_host: 192.168.1.10
ansible_port: 2222
web02:
ansible_host: 192.168.1.11
dbservers:
hosts:
db01:
ansible_host: 192.168.1.20
vars:
db_port: 3306
ansible_user: deploy2.5 Dynamic inventory
When hosts are created and destroyed automatically (e.g., cloud), a script that outputs JSON can be used. Example Python script:
#!/usr/bin/env python3
import json
instances = get_cloud_instances()
inventory = {
"webservers": {
"hosts": ["10.0.1.1", "10.0.1.2"],
"vars": {"http_port": 80}
},
"dbservers": {"hosts": ["10.0.2.1"]},
"_meta": {"hostvars": {
"10.0.1.1": {"ansible_user": "deploy"},
"10.0.1.2": {"ansible_user": "deploy"},
"10.0.2.1": {"ansible_user": "root"}
}}
}
print(json.dumps(inventory, indent=2))Official scripts such as ec2.py (AWS) or alicloud.py (Alibaba Cloud) can be referenced directly:
$ ansible -i /path/to/ec2.py all --list-hosts
[defaults]
inventory=/path/to/ec2.pyFrom Ansible 2.10+, the preferred way is to use an inventory plugin, e.g. aws_ec2.yaml :
plugin: aws_ec2
regions:
- us-east-1
- us-west-2
filters:
tag:Environment: production
keyed_groups:
- key: tags.Role
prefix: role3. Playbook – the heart of automation
A Playbook is a YAML file that describes a series of plays. Each play defines target hosts, privilege escalation, variables, and a list of tasks.
3.1 Minimal Playbook
---
- name: Install and start Nginx
hosts: webservers
become: yes
tasks:
- name: Install nginx
yum:
name: nginx
state: present
- name: Start nginx
service:
name: nginx
state: started
enabled: yesKey concepts:
Play – a set of tasks applied to a group of hosts.
Task – a single action, usually a module call.
Module – the executable unit (e.g., yum, service, copy).
3.2 Full play structure
A complete play includes vars , pre_tasks , roles , tasks , post_tasks , and handlers :
---
- name: Deploy Web Service
hosts: webservers
become: yes
gather_facts: yes
vars:
http_port: 80
server_name: example.com
vars_files:
- vars/common.yml
pre_tasks:
- name: Show start message
debug:
msg: "Starting deployment..."
roles:
- common
- nginx
tasks:
- name: Deploy configuration
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx
handlers:
- name: restart nginx
service:
name: nginx
state: restarted3.3 Variable precedence
Variables are resolved from lowest to highest priority:
Defined directly in the Playbook.
External variable files ( vars_files).
Inventory variables (group or host).
Command‑line extra vars (e.g., -e "app_port=9090").
3.4 Conditionals (when)
Run tasks only when a condition is true, for example:
- name: Install Apache on Debian
apt:
name: apache2
state: present
when: ansible_os_family == "Debian"
- name: Install Apache on RedHat
yum:
name: httpd
state: present
when: ansible_os_family == "RedHat"3.5 Loops
Iterate over simple lists or dictionaries:
- name: Install packages
yum:
name: "{{ item }}"
state: present
loop:
- nginx
- php{{ php_version }}
- php{{ php_version }}-fpm
- php{{ php_version }}-mysql
- git
- curl3.6 Handlers
Handlers run only when a task reports changed and are triggered by notify :
- name: Deploy nginx config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx
- name: restart nginx
service:
name: nginx
state: restartedHandlers execute at the end of the play; they run once even if notified multiple times.
3.7 Tags
Tag tasks to run a subset of the playbook:
- name: Install nginx
yum:
name: nginx
state: present
tags: [install, nginx]Run with --tags nginx , skip with --skip-tags config , or list all tags with --list-tags .
3.8 Blocks
Group tasks and define error handling:
- name: Configure Web Service
block:
- name: Install nginx
yum:
name: nginx
state: present
- name: Start nginx
service:
name: nginx
state: started
rescue:
- name: Report failure
debug:
msg: "nginx installation failed"
always:
- name: Clean temporary files
file:
path: /tmp/nginx_install_temp
state: absent3.9 Roles
Roles provide a reusable directory structure. A typical role layout includes tasks/main.yml , handlers/main.yml , templates/ , files/ , vars/main.yml , defaults/main.yml , and meta/main.yml . Use them in a playbook with the roles list.
3.10 Full example – deploying Nginx + PHP
---
- name: Deploy Nginx + PHP
hosts: webservers
become: yes
gather_facts: yes
vars:
http_port: 80
php_version: "8.2"
app_user: www-data
pre_tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
when: ansible_os_family == "Debian"
tasks:
- name: Install packages
package:
name:
- nginx
- "php{{ php_version }}"
- "php{{ php_version }}-fpm"
- "php{{ php_version }}-mysql"
- git
- curl
state: present
- name: Create application directories
file:
path: "{{ item }}"
state: directory
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0755"
loop:
- /var/www/app
- /var/www/app/public
- /var/www/app/storage
- name: Deploy code
git:
repo: "https://github.com/myorg/myapp.git"
dest: /var/www/app
version: main
force: yes
notify: reload php-fpm
- name: Deploy nginx config
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/app
owner: root
group: root
mode: "0644"
notify: restart nginx
- name: Enable site
file:
src: /etc/nginx/sites-available/app
dest: /etc/nginx/sites-enabled/app
state: link
notify: restart nginx
- name: Ensure nginx is running
service:
name: nginx
state: started
enabled: yes
handlers:
- name: restart nginx
service:
name: nginx
state: restarted
- name: reload php-fpm
service:
name: "php{{ php_version }}-fpm"
state: reloaded
post_tasks:
- name: Verify nginx
uri:
url: "http://localhost:{{ http_port }}"
status_code: 200
register: result
retries: 3
delay: 5
until: result.status == 2003.11 Debugging tricks
Useful command‑line options: --syntax-check – validate YAML syntax. --list-hosts – show which hosts will be targeted. --list-tasks – list all tasks. --check – run in dry‑run mode (not supported by shell / command). --step – pause before each task. -vvv – increase verbosity.
3.12 Common pitfalls
YAML indentation must use spaces, not tabs.
Every task should have a name for readability.
Maintain idempotence – running a playbook repeatedly should produce the same result.
Never store plain‑text secrets; use ansible-vault to encrypt them.
Prefix variable names to avoid collisions (e.g., nginx_port).
Handler names must be unique across the entire play.
Use the .j2 suffix for Jinja2 templates.
In summary, the article covers the three core Ansible configuration files, provides practical tips for ansible.cfg , demonstrates how to manage inventories of any size, and walks through building robust, maintainable Playbooks with variables, conditionals, loops, handlers, tags, blocks, and roles.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
AI Agent Super App
AI agent applications, installation, large-model testing, computer fundamentals, IT operations and maintenance exchange, network technology exchange, Linux learning
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
