Ansible cfg Precedence — Configuration File Load Order

Introduction

Ansible loads configuration from multiple locations with a strict precedence order. Understanding this hierarchy prevents unexpected behavior when ansible.cfg settings seem to be ignored. This guide covers the full load order, common pitfalls, and how to debug configuration issues.

Precedence Order (Highest to Lowest)

1. ANSIBLE_CONFIG environment variable (highest priority)
2. ./ansible.cfg (current directory)
3. ~/.ansible.cfg (home directory)
4. /etc/ansible/ansible.cfg (system-wide, lowest priority)

First match wins — Ansible uses the FIRST config file found in this order and ignores all others. It does NOT merge settings from multiple files.

Detailed Load Order

# Ansible checks in this exact sequence:
# 1. $ANSIBLE_CONFIG (if set and file exists)
export ANSIBLE_CONFIG=/opt/projects/webservers/ansible.cfg

# 2. Current working directory (if world-writable check passes)
./ansible.cfg

# 3. User home directory
~/.ansible.cfg

# 4. System default
/etc/ansible/ansible.cfg

Security: World-Writable Directory Check

# ❌ Ansible REJECTS ./ansible.cfg if the directory is world-writable
chmod 777 /tmp/project/
cd /tmp/project/
ansible --version
# [WARNING]: Ansible is being run in a world writable directory,
# ignoring it as an ansible.cfg source.

# ✅ Fix: restrict directory permissions
chmod 755 /tmp/project/

Environment Variables Override Everything

# Individual settings override ANY config file
export ANSIBLE_FORKS=50
export ANSIBLE_TIMEOUT=60
export ANSIBLE_REMOTE_USER=deploy
export ANSIBLE_PRIVATE_KEY_FILE=~/.ssh/deploy_key

# Check which config is active
ansible --version
# config file = /path/to/active/ansible.cfg

Common ansible.cfg Sections

# ansible.cfg
[defaults]
inventory = ./inventory/hosts
remote_user = deploy
forks = 20
timeout = 30
host_key_checking = False
retry_files_enabled = False
stdout_callback = yaml
interpreter_python = auto_silent
collections_paths = ./collections:~/.ansible/collections

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

[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o PreferSharedKey=yes
control_path_dir = ~/.ansible/cp

[inventory]
enable_plugins = host_list, script, auto, yaml, ini, toml

[galaxy]
server_list = automation_hub, galaxy

[galaxy_server.automation_hub]
url = https://cloud.redhat.com/api/automation-hub/
auth_url = https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token
token = your_token_here

[galaxy_server.galaxy]
url = https://galaxy.ansible.com/

Per-Project Configuration Pattern

my-project/
├── ansible.cfg          ← Project-specific config (picked up automatically)
├── inventory/
│   ├── production
│   └── staging
├── group_vars/
├── roles/
└── playbooks/
# my-project/ansible.cfg
[defaults]
inventory = ./inventory/production
roles_path = ./roles:~/.ansible/roles
collections_paths = ./collections
vault_password_file = ./.vault_pass
log_path = ./ansible.log

Debug: Which Config Is Active?

# Show active config file
ansible --version | grep "config file"
# config file = /home/deploy/projects/webservers/ansible.cfg

# Show ALL config settings and their source
ansible-config dump
# DEFAULT_BECOME(/home/deploy/projects/webservers/ansible.cfg) = True
# DEFAULT_FORKS(/home/deploy/projects/webservers/ansible.cfg) = 20
# DEFAULT_REMOTE_USER(env: ANSIBLE_REMOTE_USER) = deploy

# Show only non-default settings
ansible-config dump --only-changed

# Show config for specific file
ansible-config view -c /path/to/ansible.cfg

# List all available settings
ansible-config list

Common Pitfalls

ProblemCauseFix
Settings ignoredWrong directory or world-writableCheck ansible --version for active config path
Different behavior in CI vs localCI uses different working directorySet ANSIBLE_CONFIG explicitly
Home config pollutes projects~/.ansible.cfg applies globallyUse per-project ./ansible.cfg instead
Vault password not foundRelative path in wrong contextUse absolute path or ANSIBLE_VAULT_PASSWORD_FILE
SSH timeout in containersSystem /etc/ansible/ansible.cfg not presentInclude ansible.cfg in container image

CI/CD Configuration

# GitHub Actions example
- name: Run playbook
  env:
    ANSIBLE_CONFIG: ./deploy/ansible.cfg
    ANSIBLE_FORCE_COLOR: "true"
    ANSIBLE_FORKS: "20"
  run: ansible-playbook -i inventory/production site.yml

Precedence for Individual Settings

Within a single ansible.cfg, settings also have internal precedence:

1. Command-line flags (e.g., -u deploy, --forks 50)
2. Playbook keywords (e.g., become: true in play)
3. Environment variables (ANSIBLE_*)
4. Config file setting ([defaults] section)
5. Ansible built-in defaults
# Command-line ALWAYS wins
ansible-playbook site.yml -u admin --forks 50
# This uses user=admin, forks=50 regardless of ansible.cfg

Best Practices

  1. One ansible.cfg per project — keeps configuration self-contained
  2. Commit ansible.cfg to Git — team shares same settings
  3. Never use ~/.ansible.cfg for project settings — causes "works on my machine" issues
  4. Use ANSIBLE_CONFIG in CI/CD — explicit is better than implicit
  5. Check ansible --version first when debugging — confirms which file is loaded
  6. Avoid system-wide /etc/ansible/ansible.cfg for project overrides

Conclusion

Ansible's config precedence is ANSIBLE_CONFIG → ./ansible.cfg → ~/.ansible.cfg → /etc/ansible/ansible.cfg. First found wins — no merging. Use ansible-config dump --only-changed to see what's active and where it comes from. Keep project config in ./ansible.cfg committed to Git, and use ANSIBLE_CONFIG for CI/CD overrides.