Sign in
← Ansible
12 min · easy · Lesson 3 of 12

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:

  1. The environment variable ANSIBLE_CONFIG (path to a file).
  2. ansible.cfg in the current working directory.
  3. ~/.ansible.cfg in the home directory.
  4. /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.

Search order - the first one found wins, the rest are ignored 1 $ANSIBLE_CONFIG environment variable, path to a file not set 2 ./ansible.cfg current working directory found - in use 3 ~/.ansible.cfg home directory 4 /etc/ansible/ansible.cfg system default not checked not checked Files don't merge - the first one found is used in full, the rest are ignored.
The search goes top to bottom and stops at the first file found.

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

What you'll need for the tasks

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.

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.

Get access for $49

Tell the agent: use MCP izi, give me lesson ansible-basics-03-config-basics