Introduction
ansible.cfg controls Ansible's behavior — inventory location, SSH settings, privilege escalation, performance tuning, and more. Place it in your project directory for per-project configuration.
Configuration Precedence
Ansible checks these locations (first match wins):
ANSIBLE_CONFIGenvironment variable./ansible.cfg(current directory)~/.ansible.cfg(home directory)/etc/ansible/ansible.cfg(global)
Best practice: Put ansible.cfg in your project root.
Essential Configuration
# ansible.cfg
[defaults]
# Inventory file
inventory = inventory/hosts.yml
# Disable host key checking (for lab/dev)
host_key_checking = False
# SSH user
remote_user = deploy
# Roles path
roles_path = roles:~/.ansible/roles
# Collections path
collections_path = collections
# Retry files (disable)
retry_files_enabled = False
# Output formatting
stdout_callback = yaml
# or: json, debug, dense, minimal
# Fact caching
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible-facts
fact_caching_timeout = 86400
# Display skipped hosts
display_skipped_hosts = False
# Vault password file
vault_password_file = .vault-pass
[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = False
[ssh_connection]
# SSH pipelining (major performance boost)
pipelining = True
# SSH arguments
ssh_args = -C -o ControlMaster=auto -o ControlPersist=60s
# SCP vs SFTP
transfer_method = smart
# Connection timeout
timeout = 30
Performance Tuning
[defaults]
# Parallel execution (default: 5)
forks = 20
# Smart fact gathering (cache facts)
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible-facts
fact_caching_timeout = 3600
# Callback plugins for speed
stdout_callback = yaml
callback_whitelist = timer, profile_tasks
# Disable cowsay
nocows = True
[ssh_connection]
# SSH pipelining (reduces SSH operations)
pipelining = True
# Persistent connections
ssh_args = -C -o ControlMaster=auto -o ControlPersist=600s
# Max connections
control_path_dir = /tmp/.ansible-cp
Impact of Settings
| Setting | Impact |
|---|---|
forks = 20 | Run on 20 hosts simultaneously (vs 5 default) |
pipelining = True | ~2x speed (fewer SSH round-trips) |
gathering = smart | Only gather facts once (cached) |
ControlPersist=600s | Keep SSH connections open 10 min |
Security Settings
[defaults]
# Don't log sensitive data
no_log = False
display_args_to_stdout = False
# Vault
vault_password_file = .vault-pass
# Host key checking (enable in production!)
host_key_checking = True
[privilege_escalation]
become = False # Don't default to root
become_ask_pass = True # Ask for sudo password
Inventory Configuration
[defaults]
inventory = inventory/
[inventory]
# Enable inventory plugins
enable_plugins = host_list, yaml, ini, auto, aws_ec2, azure_rm
# Cache dynamic inventory
cache = True
cache_plugin = jsonfile
cache_connection = /tmp/ansible-inventory-cache
cache_timeout = 3600
Logging
[defaults]
# Log all output
log_path = /var/log/ansible.log
# Task profiling
callback_whitelist = timer, profile_tasks, profile_roles
# Verbose by default
# verbosity = 1
Per-Environment Config
project/
├── ansible.cfg # Default config
├── ansible-dev.cfg # Dev overrides
├── inventory/
│ ├── production/
│ └── staging/
└── playbooks/
# Use specific config
ANSIBLE_CONFIG=ansible-dev.cfg ansible-playbook site.yml
Common Patterns
Development Config
[defaults]
inventory = inventory/dev.yml
host_key_checking = False
retry_files_enabled = False
stdout_callback = yaml
forks = 10
[privilege_escalation]
become = True
become_ask_pass = False
Production Config
[defaults]
inventory = inventory/production/
host_key_checking = True
retry_files_enabled = True
log_path = /var/log/ansible/production.log
vault_password_file = .vault-pass
forks = 30
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible-facts
callback_whitelist = timer, profile_tasks
[privilege_escalation]
become = True
become_method = sudo
become_ask_pass = False
[ssh_connection]
pipelining = True
ssh_args = -C -o ControlMaster=auto -o ControlPersist=600s
CI/CD Config
[defaults]
inventory = inventory/
host_key_checking = False
retry_files_enabled = False
stdout_callback = yaml
forks = 50
gathering = smart
[ssh_connection]
pipelining = True
ssh_args = -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no
Environment Variables
Override any setting with ANSIBLE_ prefix:
export ANSIBLE_FORKS=50
export ANSIBLE_HOST_KEY_CHECKING=False
export ANSIBLE_STDOUT_CALLBACK=yaml
export ANSIBLE_VAULT_PASSWORD_FILE=.vault-pass
export ANSIBLE_CONFIG=/path/to/ansible.cfg
View Current Config
# Show all config
ansible-config dump
# Show only changed values
ansible-config dump --only-changed
# Show config file location
ansible-config view
# List all settings
ansible-config list
Troubleshooting
Config Not Being Read
# Check which config is active
ansible-config dump --only-changed | head -5
# First line shows config file path
# Check precedence
ansible --version
# Shows "config file = /path/to/ansible.cfg"
Pipelining Fails
Pipelining requires requiretty to be disabled in sudoers:
# Fix via playbook
- name: Disable requiretty
ansible.builtin.lineinfile:
path: /etc/sudoers
regexp: "^Defaults.*requiretty"
line: "Defaults !requiretty"
validate: "visudo -cf %s"
Related Articles
Conclusion
ansible.cfg is the control center for Ansible behavior. Put it in your project root. The three biggest performance wins: forks = 20+, pipelining = True, and gathering = smart. Use stdout_callback = yaml for readable output. Enable host_key_checking in production. Keep per-project configs for different environments. Use ansible-config dump --only-changed to verify your settings are applied.