ansible.cfg and basic settings
Where ansible.cfg lives and the search order. host_key_checking, inventory, remote_user, become.
Without a config, every Ansible command has to be loaded with flags: where
the inventory is, which user to connect as, which key, whether sudo is
needed. ansible.cfg moves these flags into one file next to the project.
In this lesson we'll look at where Ansible searches for this file, what
usually goes in it, and how to quickly check which config actually got
picked up.
Why you need ansible.cfg
Here's what the command looks like when everything is passed as flags:
ansible all -i ./inventory.ini -u deploy --private-key ~/.ssh/id_rsa -b -m ansible.builtin.ping
This is inconvenient, and a line like this breaks whenever the environment
changes: a different key path, a different user. ansible.cfg keeps the
repeated parameters in a file that lives in git with the project. The
command shrinks to ansible all -m ansible.builtin.ping.
Config search order
Ansible searches for the config in this order and takes the first one it finds:
- The environment variable
ANSIBLE_CONFIG(path to a file). ansible.cfgin the current working directory.~/.ansible.cfgin the home directory./etc/ansible/ansible.cfg.
Files don't merge. Ansible uses the first one it finds and ignores the rest entirely. If
~/.ansible.cfg has forks = 20, and the project's own ansible.cfg has no
forks, the project gets the default value (5), not 20.
That gives a practical rule. Run ansible-playbook from /srv/project/,
where ansible.cfg lives, and it takes effect, even if there's a different
one in the home directory. Run it from a neighboring folder, and the
project's settings no longer apply.
A folder writable by everyone. If the current folder has write
permission for everyone (chmod 777, mode drwxrwxrwx), Ansible
deliberately skips ./ansible.cfg and prints a warning:
[WARNING]: Ansible is being run in a world writable directory (/work), ignoring it as an ansible.cfg source.
This protects against a swapped config in shared folders. The search
continues: ~/.ansible.cfg, then /etc/ansible/ansible.cfg. This comes up
most often on WSL, when a project lives on the Windows drive at
/mnt/c/...: there, every file shows up as writable by everyone.
File format
Plain ini format. The main parameters live in the [defaults] section,
privilege escalation in [privilege_escalation]:
[defaults]
inventory = ./inventory.ini
remote_user = deploy
private_key_file = ~/.ssh/id_rsa
host_key_checking = False
forks = 10
[privilege_escalation]
become = True
become_method = sudo
become_user = root
Spaces around = are allowed. A relative path (./inventory.ini) is
resolved from the folder where ansible.cfg itself lives, not from where the
command was run.
A comment on its own line starts with # or ;. At the end of a line,
only ; counts as a comment. A line forks = 10 # a lot breaks the run:
Ansible reads the value as 10 # a lot and refuses to work, with an
invalid value error.
Each parameter has to be in its own section. become = True, written in
[defaults], is silently skipped by Ansible: it only looks for this
parameter in [privilege_escalation].
The full list of parameters with descriptions comes from
ansible-config init --disabled: it prints a config template with every
value commented out.
host_key_checking = False
This parameter shows up in nearly every tutorial config, so let's look at what it does.
On the first connection to an unknown host, SSH asks:
The authenticity of host '10.0.0.5' can't be established.
Are you sure you want to continue connecting (yes/no)?
From a terminal you can type yes by hand. In cron or CI there's no one to
answer, and the run on that host doesn't get any further.
host_key_checking = False turns off host key checking, and the connection
goes through without asking.
There's a cost to this. In a dev environment or in the cloud, where IPs
change constantly, it's more convenient this way: known_hosts goes stale
faster than anyone manages to update it. But the protection against a
swapped server (MITM) is gone too. If a different machine with a different
key ends up between you and the host, Ansible won't notice.
In production it's better to pre-populate ~/.ssh/known_hosts with
ssh-keyscan when a host is created, and leave the check on.
The same parameter can be set with an environment variable:
ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook site.yml
An environment variable wins over the value from ansible.cfg. This is
handy in CI: the project has one config, and the pipeline's behavior changes
through a variable. Command-line flags, in turn, win over environment
variables.
remote_user and keys
[defaults]
remote_user = deploy
private_key_file = ~/.ssh/deploy_ed25519
remote_user sets the user Ansible connects to the managed host as.
private_key_file sets the SSH key.
By default Ansible connects with the regular ssh client, so without
private_key_file its usual rules apply: it tries the standard keys
(~/.ssh/id_rsa, ~/.ssh/id_ecdsa, ~/.ssh/id_ed25519 and others) and keys
from the SSH agent. If a key was added with ssh-add, Ansible picks it up
without an explicit private_key_file.
become: privilege escalation
Most tasks on a server need root privileges. Privilege escalation in
Ansible is called become and is configured in the [privilege_escalation]
section:
[privilege_escalation]
become = True
become_method = sudo
become_user = root
become = True turns on privilege escalation for all tasks by default.
become_method picks the mechanism. ansible-core ships with sudo (the
default), su and runas (for Windows). The rest, such as doas and
pbrun, come from the community.general collection. become_user sets
which user tasks run as after escalation, root by default.
The value from the config is overridden at the play or task level:
- name: prepare app server
hosts: webservers
become: true # the play escalates privileges even if cfg has it off
tasks:
- name: create log directory
ansible.builtin.file:
path: /var/log/myapp
state: directory
- name: check git as the login user
ansible.builtin.command: git --version
become: false # this task runs without sudo
A minimal working ansible.cfg
This config is enough for most tutorial and small projects:
[defaults]
inventory = ./inventory.ini
remote_user = deploy
host_key_checking = False
forks = 10
[privilege_escalation]
become = True
become_method = sudo
become_user = root
Put it at the root of the project next to inventory.ini. Run commands from
that folder, and ansible-playbook picks up the file on its own.
How to check which config was picked up
The path to the config is printed by ansible --version:
ansible --version
ansible [core 2.19.14]
config file = /srv/project/ansible.cfg
...
If it says None, no config was found in any of the four places.
Which parameters differ from the defaults and where each one came from is
shown by ansible-config dump --only-changed. The source is printed in
parentheses:
ansible-config dump --only-changed
CONFIG_FILE() = /srv/project/ansible.cfg
DEFAULT_BECOME(/srv/project/ansible.cfg) = True
DEFAULT_FORKS(env: ANSIBLE_FORKS) = 3
DEFAULT_HOST_LIST(/srv/project/ansible.cfg) = ['/srv/project/inventory.ini']
...
Here forks came from an environment variable and overrode the value from
the file. ansible-config view prints the file itself, exactly as written.
Common mistakes
- The command was run outside the project folder. The project's config
isn't read, there's no inventory, and Ansible prints
Could not match supplied host pattern. The playbook still finishes without an error, it just doesn't touch any host. - The project folder is writable by everyone (
chmod 777, a Windows drive in WSL)../ansible.cfgis skipped with a world writable directory warning. - A parameter is in the wrong section:
becomein[defaults]is silently ignored. - A trailing
#comment on a line with a value breaks parsing of that value. - Expecting
~/.ansible.cfgand the project's config to merge. Only one file works, the first one found. host_key_checking = Falsegets carried from dev into production along with the rest of the config.
What you'll need for the tasks
- The config search order, and that files don't merge.
- The commands
ansible --versionandansible-config dump --only-changed. - The sections
[defaults](theinventoryparameter) and[privilege_escalation](thebecomeparameter). - How Ansible behaves in a folder that's writable by everyone.
In the next lesson we'll look at how to run Ansible on hosts without Python.
Done reading?
Mark the lesson as read to move your track progress forward.
Lesson marked as done.
Go through it with an AI agent
The agent explains anything unclear in plain words, gives you 2 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.
Tell the agent: use MCP izi, give me lesson ansible-basics-03-config-basics
Progress saved in this browser
Clear your history or open this on your phone, and the marks will be gone. Leave your email and they'll move to your account. No password needed.
Done. moved to your account. You can see your progress on any device. My progress