How to Configure ansible.cfg for Ansible Projects on Linux

Tested on Rocky Linux 10.2 (Red Quartz)
Package ansible-core 2.16.16
Applies to RHEL, Rocky Linux, AlmaLinux, Oracle Linux, CentOS Stream, Fedora
Privilege sudo or root
Scope Configure project-level ansible.cfg defaults for inventory path, remote user, privilege escalation, roles path, and verification of active settings.

ansible.cfg controls how Ansible behaves on your control node: which inventory to read, which user to SSH as, whether become is on by default, and where to find roles and collections. Without it, you repeat the same flags on every command—-i, -u, --private-key, --become—and small mistakes turn into connection or inventory errors.

This guide explains what the file is, where Ansible looks for it, how to build a project-level config on Rocky Linux 10, and how to verify the active settings with ansible --version and ansible-config. You need Ansible installed on the control node first; managed hosts stay agentless.


What is ansible.cfg?

ansible.cfg is Ansible’s main configuration file. Commands such as ansible, ansible-playbook, ansible-galaxy, and ansible-config read it when they start.

The file uses INI-style sections—names in square brackets—with key = value lines underneath:

ini
[defaults]
inventory = inventory/hosts
remote_user = ansible

[privilege_escalation]
become = True

You normally create one ansible.cfg in the root of your Ansible project, beside inventory and playbooks, so settings travel with the repo. After install, Rocky Linux may only give you a commented template at /etc/ansible/ansible.cfg; a project file overrides that when you work from your project directory.


Why Do You Need ansible.cfg?

Without a project config, every ad hoc command tends to grow a handful of flags—see ad hoc commands for module syntax once defaults are set:

bash
ansible -i inventory/hosts rocky2 -u ansible --private-key ~/.ssh/id_ed25519 -m ansible.builtin.ping --become

That is hard to read, easy to mistype, and painful to repeat in playbooks, CI jobs, and lab notes.

With ansible.cfg in the project root, the same intent shrinks to:

bash
ansible rocky2 -m ansible.builtin.ping

Defaults for inventory path, remote user, SSH key, and privilege escalation live in one file. Your project becomes predictable—you cd into the directory, run Ansible, and get the same behavior on your laptop, a teammate’s VM, or a fresh lab rebuild. Pair config with how to run Ansible playbooks once playbooks enter the workflow.


ansible.cfg Search Order

Ansible loads one config file per run. It searches in this order and stops at the first file found:

Priority Location
1 ANSIBLE_CONFIG environment variable (path to a file)
2 ansible.cfg in the current working directory
3 ~/.ansible.cfg in the user’s home directory
4 /etc/ansible/ansible.cfg

Ansible does not merge /etc/ansible/ansible.cfg with your project file. If ./ansible.cfg exists where you run the command, the system file is ignored for that run.

Why the current directory matters: Ansible resolves ./ansible.cfg from where you run the command, not where the playbook file lives. If you keep playbooks in playbooks/site.yml but run ansible-playbook from /tmp, Ansible will not see ~/ansible-project/ansible.cfg unless you cd there first or set ANSIBLE_CONFIG.

Why project-level config is best for learning: One directory holds ansible.cfg, inventory, roles, and playbooks. You archive or clone one tree and the defaults come with it.

Security note: Ansible refuses to auto-load ./ansible.cfg from a world-writable directory (another user could drop a malicious config). Keep projects under your home directory with normal permissions. On WSL, avoid leaving the repo on /mnt/c/... without fixing mount options—or set ANSIBLE_CONFIG to a safe file path.

See the official configuration settings for the full search rules and ansible-config behavior.


Create a Project-Level ansible.cfg File

Create the project root if you have not already:

bash
mkdir -p ~/ansible-project/inventory

Change into that directory before you create or edit the config:

bash
cd ~/ansible-project

Create ansible.cfg with only the keys you need—do not copy the full thousand-line template from ansible-config init into your repo:

bash
touch ansible.cfg

A typical learning layout (details in the project directory structure guide):

text
ansible-project/
├── ansible.cfg
├── inventory/
├── playbooks/
├── roles/
└── collections/

Keep paths in ansible.cfg relative to the file (inventory/hosts, roles, not absolute paths tied to one machine) so the project stays portable.


ansible.cfg Sections Explained

Ansible groups settings into INI sections. For most Linux labs you only need three:

Section Purpose
[defaults] General behavior: inventory, remote_user, roles_path, collections_path, logging, output
[privilege_escalation] become / sudo defaults
[ssh_connection] SSH plugin behavior, including pipelining

Sections such as [persistent_connection] or [paramiko_connection] matter for network gear or special connection plugins—you can ignore them until a playbook requires them.

The subsections below show what to put in each block and why.


Configure Inventory Path in ansible.cfg

Under [defaults], point Ansible at your host list:

ini
[defaults]
inventory = inventory/hosts

You can also use a directory (inventory/) when you split groups across multiple files. Ansible loads all inventory sources in that directory.

inventory = inventory/hosts drops -i inventory/hosts from every command. Inventory syntax (groups, variables, children) belongs in the inventory files guide—here you only wire the default path.

Confirm Ansible sees the configured inventory after you save the file:

bash
cd ~/ansible-project
ansible-inventory --list --yaml | head -15

Sample output:

output
all:
  children:
    lab:
      hosts:
        rocky2:
          ansible_host: 192.168.56.109

If that lists your hosts, the inventory setting is working.


Configure Remote User and SSH Key

Tell Ansible which account to use for SSH when a host does not set ansible_user:

ini
[defaults]
remote_user = ansible
private_key_file = ~/.ssh/id_ed25519

remote_user is the login user—not root. private_key_file points at the key Ansible uses for SSH (match the key you deployed in lab setup).

Inventory can still override the default. If a host or group sets ansible_user, that value wins over remote_user for that host. Per the precedence rules, command-line -u and environment variables such as ANSIBLE_REMOTE_USER override ansible.cfg too.

If Ansible connects as the wrong user, check inventory for ansible_user before you blame ansible.cfg:

bash
ansible-config dump | grep DEFAULT_REMOTE_USER

Sample output:

output
DEFAULT_REMOTE_USER(/home/ansible/ansible-project/ansible.cfg) = ansible

An override from the environment shows (env: ANSIBLE_REMOTE_USER) instead of your project file path.


Configure Privilege Escalation with become

Module tasks that manage packages, services, or system files usually need root. Set defaults under [privilege_escalation]:

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

With become = True, tasks that declare become: true (or rely on the default) use sudo without --become on every CLI invocation. become_ask_pass = False fits labs where the automation user has passwordless sudo in /etc/sudoers.d/.

If sudo requires a password on your hosts, do not put passwords in ansible.cfg. Run playbooks with --ask-become-pass or set become_ask_pass = True and let Ansible prompt.


Configure Roles Path and Collections Path

Point Ansible at project-local content:

ini
[defaults]
roles_path = roles
collections_path = ~/.ansible/collections:/usr/share/ansible/collections

roles_path = roles means a role named httpd lives at roles/httpd/—the layout covered in the roles directory structure tutorial.

collections_path is a colon-separated list. User-installed collections from ansible-galaxy collection install usually land under ~/.ansible/collections; system RPMs may use /usr/share/ansible/collections.

ansible.cfg key Status
collections_path Current setting name—use this in new project files
collections_paths Deprecated alias (plural); Ansible may warn and map it to collections_path

If you copy an old config or blog snippet with collections_paths, rename it to collections_path. ansible-config dump --only-changed shows the internal name COLLECTIONS_PATHS even when your file uses the singular key—that is normal.

Verify collections Ansible can see:

bash
ansible-galaxy collection list

Sample output:

output
# /usr/share/ansible/collections/ansible_collections
Collection    Version
------------- -------
ansible.posix 2.1.0

An empty list means only built-in ansible.builtin modules are available until you install collections.


Configure Host Key Checking

SSH host key checking is a security control: Ansible refuses to connect when the remote host key does not match ~/.ssh/known_hosts, which helps detect man-in-the-middle attacks and unexpected IP reuse. In a lab where you rebuild VMs often, strict checking produces extra prompts.

ini
[defaults]
host_key_checking = False

host_key_checking = False tells Ansible to accept unknown or changed host keys automatically—convenient for disposable lab VMs, but unsafe on production networks because a wrong IP or compromised route could connect you to the wrong machine without warning. Use it only on isolated lab networks you control.

For production and shared infrastructure, keep host_key_checking = True (the default) and populate known_hosts deliberately—ssh-keyscan, configuration management, or your SSH tooling—not by disabling verification. If keys rotate often, automate known-host updates instead of turning checking off globally.


Configure Python Interpreter Discovery

Managed Linux hosts need Python 3. Ansible warns if it cannot pick an interpreter cleanly.

ini
[defaults]
interpreter_python = auto_silent

auto_silent lets Ansible discover Python 3 on each host without noisy warnings on every run. Avoid hard-coding /usr/bin/python3 unless you must support hosts with multiple Python versions and you know exactly which binary to use.


Verify Which ansible.cfg File Is Active

When settings seem ignored, check which file Ansible loaded:

bash
cd ~/ansible-project
ansible --version

Sample output:

output
ansible [core 2.16.16]
  config file = /home/ansible/ansible-project/ansible.cfg
  configured module search path = ['/home/ansible/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.12/site-packages/ansible
  ansible collection location = /home/ansible/.ansible/collections:/usr/share/ansible/collections

The config file line is the first thing to read. If it shows /etc/ansible/ansible.cfg while you expected your project file, you are not in the project directory—or ANSIBLE_CONFIG points elsewhere.

Run the same check from the wrong directory to see the fallback:

bash
cd ~
ansible --version | grep 'config file'

Sample output:

output
config file = /etc/ansible/ansible.cfg

That is why cd ~/ansible-project matters before every Ansible command.


View Active Configuration with ansible-config

ansible-config shows what Ansible actually uses after merges with defaults.

Print the loaded config file content:

bash
cd ~/ansible-project
ansible-config view

Sample output:

output
[defaults]
inventory = inventory/hosts
remote_user = ansible
host_key_checking = False
roles_path = roles
collections_path = ~/.ansible/collections:/usr/share/ansible/collections
interpreter_python = auto_silent

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

[ssh_connection]
pipelining = True

List every active value and where it came from:

bash
ansible-config dump | grep DEFAULT_REMOTE_USER

Sample output:

output
DEFAULT_REMOTE_USER(/home/ansible/ansible-project/ansible.cfg) = ansible

Show only values you changed from built-in defaults:

bash
ansible-config dump --only-changed

Sample output:

output
COLLECTIONS_PATHS(/home/ansible/ansible-project/ansible.cfg) = ['/home/ansible/.ansible/collections', '/usr/share/ansible/collections']
CONFIG_FILE() = /home/ansible/ansible-project/ansible.cfg
DEFAULT_BECOME(/home/ansible/ansible-project/ansible.cfg) = True
DEFAULT_BECOME_ASK_PASS(/home/ansible/ansible-project/ansible.cfg) = False
DEFAULT_BECOME_METHOD(/home/ansible/ansible-project/ansible.cfg) = sudo
DEFAULT_BECOME_USER(/home/ansible/ansible-project/ansible.cfg) = root
DEFAULT_HOST_LIST(/home/ansible/ansible-project/ansible.cfg) = ['/home/ansible/ansible-project/inventory/hosts']
DEFAULT_REMOTE_USER(/home/ansible/ansible-project/ansible.cfg) = ansible
DEFAULT_ROLES_PATH(/home/ansible/ansible-project/ansible.cfg) = ['/home/ansible/ansible-project/roles']
HOST_KEY_CHECKING(/home/ansible/ansible-project/ansible.cfg) = False
INTERPRETER_PYTHON(/home/ansible/ansible-project/ansible.cfg) = auto_silent
Command Purpose
ansible-config view Shows the loaded config file content
ansible-config dump Shows all active values and their source
ansible-config dump --only-changed Shows only non-default settings

Test ansible.cfg with Ansible Commands

Work from ~/ansible-project and confirm defaults work without extra flags.

Ping a managed host without -i or -u:

bash
ansible rocky2 -m ansible.builtin.ping

Sample output:

output
rocky2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

A successful pong means inventory path, remote_user, SSH, and Python discovery line up with your ansible.cfg.

Confirm become from [privilege_escalation] without --become on the CLI:

bash
ansible rocky2 -m ansible.builtin.command -a "whoami" -b

Sample output:

output
rocky2 | CHANGED | rc=0 >>
root

root on the managed host means privilege escalation worked with your project defaults.

Later, when you add playbooks and roles, run a playbook without passing -i on the command line—the project config should supply the inventory path automatically.


Common ansible.cfg Options

Option Section Purpose
inventory [defaults] Default inventory file or directory
remote_user [defaults] Default SSH login user
private_key_file [defaults] Path to SSH private key
host_key_checking [defaults] Whether to verify SSH host keys
roles_path [defaults] Directories to search for roles
collections_path [defaults] Directories to search for collections
interpreter_python [defaults] Python interpreter discovery on targets
become [privilege_escalation] Enable privilege escalation by default
become_method [privilege_escalation] Usually sudo on Linux
become_user [privilege_escalation] User after escalation—typically root
pipelining [ssh_connection] Fewer SSH round trips when sudo allows it

For every key your build supports, run ansible-config list or read the configuration settings reference.


Common ansible.cfg Mistakes and Fixes

Problem Likely cause
ansible.cfg not detected Command run from wrong directory; no ./ansible.cfg in cwd
Wrong inventory used Bad inventory= path; overridden with -i on CLI
Wrong user used ansible_user in inventory overrides remote_user
Permission denied (SSH) Wrong private_key_file or key not on managed host
Sudo password error become = True but passwordless sudo not configured
Role not found Typo in roles_path or role not under roles/
Collection not found Collection not installed; wrong collections_path
Config ignored entirely World-writable project dir; ANSIBLE_CONFIG set elsewhere
Deprecation on collections_paths Old key name—use collections_path

This file matches the GoLinuxCloud lab: dedicated ansible user, passwordless sudo on managed nodes, project-relative paths, and relaxed host key checking for disposable VMs.

bash
cd ~/ansible-project
cat > ansible.cfg << 'EOF'
[defaults]
inventory = inventory/hosts
remote_user = ansible
private_key_file = ~/.ssh/id_ed25519
host_key_checking = False
roles_path = roles
collections_path = ~/.ansible/collections:/usr/share/ansible/collections
interpreter_python = auto_silent

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

[ssh_connection]
pipelining = True
EOF

Pair it with a minimal inventory/hosts (adjust IP and hostname for your lab):

ini
[lab]
rocky2

[lab:vars]
ansible_host=192.168.56.109

Verify:

bash
ansible --version | grep 'config file'

Sample output:

output
config file = /home/ansible/ansible-project/ansible.cfg

This is the same style of configuration used in the upcoming inventory, playbook, roles, and collections examples in the course.


References


Summary

ansible.cfg defines default Ansible behavior on your control node—inventory path, SSH user, become, and lookup paths for roles and collections. Put one file in your project root, run commands from that directory, and verify with ansible --version and ansible-config dump --only-changed. Next, expand the project directory structure and inventory, then wire group_vars and host_vars patterns before you write playbooks.


Frequently Asked Questions

1. Where should I put ansible.cfg?

In the project root—the directory you cd into before running ansible or ansible-playbook. Ansible reads ./ansible.cfg before ~/.ansible.cfg and /etc/ansible/ansible.cfg.

2. Which ansible.cfg file does Ansible use?

The first match: ANSIBLE_CONFIG, then ./ansible.cfg in the current working directory, then ~/.ansible.cfg, then /etc/ansible/ansible.cfg. Ansible does not merge multiple files.

3. Why is my ansible.cfg ignored?

Usually you ran the command from the wrong directory, set ANSIBLE_CONFIG elsewhere, or the project directory is world-writable. Run ansible --version and check the config file line.

4. Can inventory variables override ansible.cfg?

Yes. ansible_user on a host or group overrides remote_user from ansible.cfg. Command-line flags and environment variables can override both.

5. Should I edit /etc/ansible/ansible.cfg?

For learning and team projects, use ./ansible.cfg in the repo. Leave /etc/ansible/ansible.cfg as the system fallback unless you manage host-wide defaults as an admin.

6. What comes after configuring ansible.cfg?

Flesh out inventory and playbooks from your project directory.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)