Ansible Configuration File Precedence
Ansible reads its configuration from the first of four locations it finds, and gets this wrong for you silently if you assume the wrong one. Rather than describe the order, this writes conflicting values into three of them and reads back which one took effect - because `ansible-config dump` prints the source of every setting in brackets next to its value.
Control Node and Managed Nodes Guide 4 of 45 Beginner
- OSUbuntu 26.04 LTS
- ansible-core2.20.1
- Python3.14.4
- TimeAbout 14 min
- Reviewed23 August 2026
Written against the versions above. The search order is `ANSIBLE_CONFIG` (environment), then `./ansible.cfg` in the current directory, then `~/.ansible.cfg`, then `/etc/ansible/ansible.cfg`. **The first one found wins completely** - settings are not merged across files. Individual `ANSIBLE_*` environment variables beat all of them.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| ANS-CTL01 | 192.168.0.36 | Ubuntu 26.04 LTS | Ansible Control Node | 2 Core | 3 GB | 50 GB |
Before you start
- A control node with Ansible installed.
- The session writes and removes
~/.ansible.cfgand/tmp/other.cfg, and restores the original~/ansible.cfgat the end.
-
Which file is in use
Two commands answer it. The version banner:
config file = /home/sysadmin/ansible.cfgand the dump of everything you have changed from the defaults:
ansible-config dump --only-changed--only-changedis the useful one. A full dump is hundreds of settings; this shows just the handful you are responsible for, which is usually the whole explanation of why your Ansible behaves differently from someone else's.bash Example session ansible --version | head -3ansible [core 2.20.1] config file = /home/sysadmin/ansible.cfg configured module search path = ['/home/sysadmin/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']cat ~/ansible.cfg[defaults]inventory = ./inventory.inihost_key_checking = Falseremote_user = sysadminstdout_callback = yamlansible-config dump --only-changedCONFIG_FILE() = /home/sysadmin/ansible.cfgDEFAULT_HOST_LIST(/home/sysadmin/ansible.cfg) = ['/home/sysadmin/inventory.ini']DEFAULT_REMOTE_USER(/home/sysadmin/ansible.cfg) = sysadminDEFAULT_STDOUT_CALLBACK(/home/sysadmin/ansible.cfg) = yamlHOST_KEY_CHECKING(/home/sysadmin/ansible.cfg) = False GALAXY_SERVERS:Expected resultThe config path, its contents, and the settings it changed.
Success conditionYou can answer 'which config is this using' immediately.
-
The four places it looks
ls: cannot access '/etc/ansible/ansible.cfg': No such file or directoryls: cannot access '/home/sysadmin/.ansible.cfg': No such file or directoryNeither of the lower-priority locations exists here, which is why the project file is being used. On many systems
/etc/ansible/ansible.cfgdoes exist from the package, and is the reason a fresh checkout behaves differently on someone else's machine.ansible-config init --disabledprints a fully commented template of every available setting - a good way to discover options, and far too long to keep as your actual config.bash Example session ansible-config init --disabled 2>/dev/null | head -5[defaults]# (boolean) By default, Ansible will issue a warning when received from a task action (module or action plugin).# These warnings can be silenced by adjusting this setting to False.;action_warnings=Truels -l /etc/ansible/ansible.cfg 2>&1ls: cannot access '/etc/ansible/ansible.cfg': No such file or directory[exit 2]ls -l ~/.ansible.cfg 2>&1ls: cannot access '/home/sysadmin/.ansible.cfg': No such file or directory[exit 2]Expected resultA commented template, and two locations that are empty here.
Success conditionYou know all four candidates, not just the one you use.
-
Prove the order
Two files, two different values for the same setting -
forks = 11in~/.ansible.cfgandforks = 22in./ansible.cfg:DEFAULT_FORKS(/home/sysadmin/ansible.cfg) = 22The value comes with its source in brackets. No guessing: the project file won, and
~/.ansible.cfgwas not consulted at all.That parenthetical is the single most useful thing about
ansible-config dump, and it settles arguments. When a setting is not doing what you expect, dump it and read where it came from.bash Example session printf '[defaults]\nforks = 11\n' > ~/.ansible.cfg && echo "wrote ~/.ansible.cfg with forks=11"wrote ~/.ansible.cfg with forks=11printf '[defaults]\ninventory = ./inventory.ini\nhost_key_checking = False\nremote_user = sysadmin\nstdout_callback = yaml\nforks = 22\n' > ~/ansible.cfg && echo "wrote ./ansible.cfg with forks=22"wrote ./ansible.cfg with forks=22ansible-config dump | grep -E "^DEFAULT_FORKS"DEFAULT_FORKS(/home/sysadmin/ansible.cfg) = 22ansible --version | grep "config file" config file = /home/sysadmin/ansible.cfgExpected result
DEFAULT_FORKS(/home/sysadmin/ansible.cfg) = 22.Success conditionYou can prove which file supplied a setting.
-
The environment beats them all
DEFAULT_FORKS(/tmp/other.cfg) = 33config file = /tmp/other.cfgANSIBLE_CONFIGreplaces the whole search - the project file is ignored entirely. This is how CI pipelines pin a known configuration, and how you test one without touching your own.And a single setting can be overridden on its own:
DEFAULT_FORKS(env: ANSIBLE_FORKS) = 44env: ANSIBLE_FORKS- the source again. Nearly every setting has anANSIBLE_*variable, which beats every file. The full order, highest first:ANSIBLE_environment variableANSIBLE_CONFIGpointing at a file./ansible.cfg~/.ansible.cfg/etc/ansible/ansible.cfg
And the trap: files do not merge. If
./ansible.cfgsets one thing, nothing is inherited from the others - a project config with onlyinventoryin it silently discards every setting you had in~/.ansible.cfg.bash Example session printf '[defaults]\ninventory = /home/sysadmin/inventory.ini\nforks = 33\n' > /tmp/other.cfg && ANSIBLE_CONFIG=/tmp/other.cfg ansible-config dump | grep -E "^DEFAULT_FORKS"DEFAULT_FORKS(/tmp/other.cfg) = 33ANSIBLE_CONFIG=/tmp/other.cfg ansible --version | grep "config file" config file = /tmp/other.cfgANSIBLE_FORKS=44 ansible-config dump | grep -E "^DEFAULT_FORKS"DEFAULT_FORKS(env: ANSIBLE_FORKS) = 44Expected resultThe source shown as a different file, then as an environment variable.
Success conditionYou can override configuration for one run without editing anything.
-
The settings worth knowing
inventory = ./inventory.ini host_key_checking = False remote_user = sysadmin stdout_callback = yamlFour lines that make a lab usable:
inventory- stop typing-ion every commandhost_key_checking- off here for convenience; leave it on anywhere real, because it is what stops you executing against an impostorremote_user- who to log in asstdout_callback = yaml- readable multi-line output instead of dense JSON, and worth setting on the first day
Two more you will want soon:
forks(how many hosts at once, default 5) andpipelining(fewer SSH round trips per task).The files written during this guide are removed and the original restored, which the final
--only-changedconfirms.bash Example session ansible-config list 2>/dev/null | grep -cE "^[A-Z_]+:"214ansible-config dump | grep -E "^(DEFAULT_HOST_LIST|HOST_KEY_CHECKING|DEFAULT_REMOTE_USER|DEFAULT_STDOUT_CALLBACK|DEFAULT_FORKS|DEFAULT_TIMEOUT) " [exit 1]rm -f ~/.ansible.cfg /tmp/other.cfgprintf '[defaults]\ninventory = ./inventory.ini\nhost_key_checking = False\nremote_user = sysadmin\nstdout_callback = yaml\n' > ~/ansible.cfg && ansible-config dump --only-changedCONFIG_FILE() = /home/sysadmin/ansible.cfgDEFAULT_HOST_LIST(/home/sysadmin/ansible.cfg) = ['/home/sysadmin/inventory.ini']DEFAULT_REMOTE_USER(/home/sysadmin/ansible.cfg) = sysadminDEFAULT_STDOUT_CALLBACK(/home/sysadmin/ansible.cfg) = yamlHOST_KEY_CHECKING(/home/sysadmin/ansible.cfg) = False GALAXY_SERVERS:Expected resultA count of available settings, the ones in force, and a clean restore.
Success conditionYou have a config you can explain line by line.
Troubleshooting
A setting in
~/.ansible.cfgis being ignored.Why: A
./ansible.cfgexists in the working directory and wins outright.Fix:
ansible-config dump | grep <SETTING>shows the source in brackets.Settings disappeared after adding a project config.
Why: Configuration files do not merge - the first one found is the only one read.
Fix:Copy everything you need into the project file.
Ansible behaves differently in CI.
Why: A different config file, or
ANSIBLE_*variables in the environment.Fix:Run
ansible-config dump --only-changedinside the pipeline.ansible.cfgin a world-writable directory is ignored.Why: Ansible refuses to load a config from a world-writable current directory, for security.
Fix:Fix the directory permissions, or use
ANSIBLE_CONFIGwith an absolute path.