What's Actually Happening
Ansible playbook runs without loading host-specific variables from inventory. Variables defined in host_vars or inventory file are not available during playbook execution, causing tasks to fail or use wrong values.
The Error You'll See
Variable undefined:
```bash $ ansible-playbook site.yml -l server1
TASK [Configure app] *************** fatal: [server1]: FAILED! => { "msg": "The task includes an undefined variable 'app_port'." } ```
Wrong variable value:
```bash $ ansible server1 -m debug -a "var=app_port"
server1 | SUCCESS => { "app_port": 8080 # Expected 9000 from inventory } ```
Inventory not loaded:
```bash $ ansible-inventory --list
{ "_meta": { "hostvars": {} # Empty, host variables not found } } ```
Why This Happens
- 1.Wrong inventory path - Ansible not finding inventory file
- 2.Invalid inventory format - YAML syntax errors
- 3.host_vars directory wrong - Not in correct location
- 4.Variable precedence override - Other sources overriding
- 5.Group vs host variables - Variables in wrong scope
- 6.Inventory file not parsed - Format not recognized
Step 1: Check Inventory Path
```bash # Check current inventory path ansible-config dump | grep inventory
# Default locations: # - ansible.cfg: inventory = /path/to/inventory # - Environment: ANSIBLE_INVENTORY # - CLI: -i /path/to/inventory
# Check ansible.cfg grep inventory ansible.cfg
# Common settings: [defaults] inventory = ./inventory # Or: inventory = ./hosts.ini,./inventory/
# Test inventory loading ansible-inventory --list -i ./inventory
# Verify hosts found ansible all -i ./inventory --list-hosts
# Check if using correct inventory ansible-playbook site.yml -i ./inventory -v ```
Step 2: Verify Inventory File Format
```bash # Check inventory file exists ls -la inventory/ ls -la hosts.ini
# Validate YAML inventory syntax python -c "import yaml; yaml.safe_load(open('inventory.yaml'))"
# Or use ansible-inventory ansible-inventory -i inventory.yaml --list
# Valid YAML inventory format: --- all: hosts: server1: app_port: 9000 app_user: deploy server2: app_port: 8080 app_user: admin children: web: hosts: server1: server2: vars: nginx_port: 80
# INI format (hosts.ini): [web] server1 app_port=9000 app_user=deploy server2 app_port=8080 app_user=admin
[web:vars] nginx_port=80
# Validate INI format ansible-inventory -i hosts.ini --list ```
Step 3: Check host_vars Directory
```bash # host_vars should be adjacent to inventory file # Structure: # inventory/ # hosts.ini # group_vars/ # web.yml # host_vars/ # server1.yml # server2.yml
ls -la inventory/host_vars/ ls -la inventory/host_vars/server1.yml
# Check file content cat inventory/host_vars/server1.yml
--- app_port: 9000 app_user: deploy
# Verify ansible sees host_vars ansible-inventory --list -i inventory/hosts.ini
# Should show: { "_meta": { "hostvars": { "server1": { "app_port": 9000 } } } }
# If host_vars not adjacent to inventory, specify: ansible-playbook site.yml -i inventory/hosts.ini --extra-vars "@host_vars/server1.yml" ```
Step 4: Test Variable Loading
```bash # Debug variable value ansible server1 -m debug -a "var=app_port" -i inventory/
# Debug all host variables ansible server1 -m debug -a "var=hostvars[inventory_hostname]" -i inventory/
# Check inventory hierarchy ansible-inventory --graph -i inventory/
# Use verbose mode to see variable sources ansible-playbook site.yml -i inventory/ -vvv | grep app_port
# Show variable precedence ansible server1 -m debug -a "var=ansible_vars_sources" -i inventory/ ```
Step 5: Check Variable Precedence
```bash # Ansible variable precedence (lowest to highest): # 1. command line values (lowest) # 2. role defaults # 3. inventory file or script group vars # 4. inventory group_vars/all # 5. playbook group_vars/all # 6. inventory group_vars/* # 7. playbook group_vars/* # 8. inventory file or script host vars # 9. inventory host_vars/* # 10. playbook host_vars/* # 11. host facts / cached set_facts # 12. play vars # 13. play vars_prompt # 14. play vars_files # 15. role vars # 16. block vars # 17. task vars # 18. include_vars # 19. set_facts / registered vars # 20. role (and include_role) params # 21. include_params # 22. extra vars (highest)
# Check if variable being overridden ansible server1 -m debug -a "var=app_port" -vvv
# Check playbook variables grep app_port playbook.yml
# Check role defaults grep -r app_port roles/*/defaults/
# Check extra vars ansible-playbook site.yml -e "app_port=8080" # This overrides inventory host_vars! ```
Step 6: Fix Inventory Directory Structure
```bash # Correct structure for inventory directory:
mkdir -p inventory/group_vars inventory/host_vars
# Create inventory file cat << 'EOF' > inventory/hosts.ini [web] server1 server2
[db] server3 EOF
# Create host_vars cat << 'EOF' > inventory/host_vars/server1.yml --- app_port: 9000 app_user: deploy EOF
cat << 'EOF' > inventory/host_vars/server2.yml --- app_port: 8080 app_user: admin EOF
# Create group_vars cat << 'EOF' > inventory/group_vars/web.yml --- nginx_port: 80 nginx_user: www-data EOF
# Verify structure tree inventory/ # inventory/ # ├── hosts.ini # ├── group_vars/ # │ └── web.yml # └── host_vars/ # │ ├── server1.yml # │ └── server2.yml
# Test loading ansible-inventory -i inventory/hosts.ini --list ```
Step 7: Use Dynamic Inventory Correctly
```bash # For dynamic inventory plugins
# Check plugin configuration ansible-config dump | grep inventory_plugins
# Example AWS inventory plugin: # inventory/aws_ec2.yml plugin: aws_ec2 regions: - us-east-1 hostvars: app_port: 9000
# Verify plugin loaded ansible-inventory -i inventory/aws_ec2.yml --list
# For custom inventory scripts: ansible-inventory -i inventory/my_script.py --list
# Check script is executable chmod +x inventory/my_script.py
# Test script output python inventory/my_script.py # Should output valid JSON
# Multiple inventory sources: ansible-playbook site.yml -i inventory/hosts.ini -i inventory/aws_ec2.yml ```
Step 8: Verify Host Name Matching
```bash # host_vars file name must match host name exactly
# Check host names in inventory ansible-inventory --list -i inventory/hosts.ini | jq '.all.hosts'
# Host names: # server1, server2
# host_vars files must be: # inventory/host_vars/server1.yml # inventory/host_vars/server2.yml
# NOT: # inventory/host_vars/server1.local.yml # Wrong if host is 'server1' # inventory/host_vars/192.168.1.1.yml # Wrong if host is 'server1'
# Check if host_vars file matches host ls inventory/host_vars/
# If host name includes domain, include in filename: # Host: server1.example.com # File: inventory/host_vars/server1.example.com.yml
# Or use aliases in INI inventory: [web] server1 ansible_host=192.168.1.1 # host_vars/server1.yml will load ```
Step 9: Force Variable Reload
```bash # Clear ansible cache rm -rf ~/.ansible/cache/ rm -rf .ansible/
# Reload facts ansible server1 -m setup -i inventory/
# Use --flush-cache ansible-playbook site.yml -i inventory/ --flush-cache
# Force re-read inventory ansible-inventory -i inventory/hosts.ini --list --refresh
# Check ansible inventory manager ansible-config dump | grep inventory_cache
# Disable caching in ansible.cfg: [defaults] fact_caching = memory inventory_cache = false ```
Step 10: Debug Variable Sources
```bash # Create playbook to debug variable precedence cat << 'EOF' > debug_vars.yml --- - hosts: all tasks: - name: Debug app_port value debug: var: app_port
- name: Debug all host vars
- debug:
- var: hostvars[inventory_hostname]
- name: Debug inventory hostname
- debug:
- var: inventory_hostname
- name: Debug group names
- debug:
- var: group_names
- name: Show variable source
- shell: echo "app_port from host_vars"
- when: app_port == 9000
- EOF
ansible-playbook debug_vars.yml -i inventory/ -vv
# Use ansible-doc for inventory plugin help ansible-doc -t inventory aws_ec2 ansible-doc -t inventory yaml ```
Ansible Inventory Variables Checklist
| Check | Command | Expected |
|---|---|---|
| Inventory path | ansible-config dump | Correct path |
| Host vars file | ls host_vars/ | Matches host name |
| Variable value | debug var= | Expected value |
| Inventory graph | --graph | Shows host_vars |
| YAML syntax | python yaml.load | No errors |
| Precedence | debug -vvv | No override |
Verify the Fix
```bash # After fixing inventory variable loading
# 1. Verify host_vars loaded ansible-inventory --list -i inventory/ | jq '.["_meta"]["hostvars"]["server1"]' // Should show host variables
# 2. Check variable value ansible server1 -m debug -a "var=app_port" -i inventory/ // Should show expected value (9000)
# 3. Run playbook successfully ansible-playbook site.yml -i inventory/ -l server1 // Tasks should use correct variable values
# 4. Verify no override ansible-playbook site.yml -vvv | grep app_port // Should show value from host_vars
# 5. Check inventory graph ansible-inventory --graph -i inventory/ // Shows host variables attached to hosts
# 6. Test with multiple hosts ansible all -m debug -a "var=app_port" -i inventory/ // Each host shows its own port ```
Related Issues
- [Fix Ansible Variable Undefined](/articles/fix-ansible-variable-undefined)
- [Fix Ansible Inventory Not Found](/articles/fix-ansible-inventory-not-found)
- [Fix Ansible Group Variables Not Applied](/articles/fix-ansible-group-variables-not-applied)