Configuration #

In the Ansible ecosystem, the flexibility and portability of automation projects are largely determined by how you manage their configuration. Without centralized, standardized configuration, you’d be forced to write long, repetitive command-line flags every time you run a playbook. To solve this, Ansible provides a default behavior management mechanism through a configuration file called ansible.cfg. This file lets you define the inventory storage directory, SSH authentication method, parallel execution limits (forks), privilege escalation, and even network performance optimizations. This article dissects the role of ansible.cfg, its search hierarchy rules, escalation prevention security features, and high-level performance parameters.

The Role of ansible.cfg #

The ansible.cfg file is the control center for the Ansible engine’s behavior on the control node machine. Explicitly configuring this helps you ensure every infrastructure development team member (platform engineers) uses the same parameters when running the same automation. This minimizes execution variation caused by differences in operating systems or personal configurations on each developer’s local machine.

Theoretically, this configuration file separates declarative playbook logic from tactical execution methods. You can define the safest and most efficient default behavior for your project environment, then share it to the Git repository along with all other automation code.


Configuration File Search Hierarchy #

When you run an Ansible command (like ansible or ansible-playbook), the Ansible execution engine searches for the ansible.cfg file in several locations in sequence. Ansible uses a priority system where a configuration file found at a higher-priority location overrides configuration from locations below it.

Here are the 4 search locations for the ansible.cfg file, from highest to lowest priority:

ansible.cfg configuration search order (Priority 1 to 4):
  1. ANSIBLE_CONFIG (Environment Variable)      ← Ignores all physical files
  2. ./ansible.cfg (Active project directory)   ← Best practice for project portability
  3. ~/.ansible.cfg (User home directory)       ← Personal configuration for a specific user
  4. /etc/ansible/ansible.cfg (System global)   ← Post-installation system default configuration

The search decision flow is visualized in the following decision diagram:

flowchart TD
    A["Start Ansible Command"] --> B{"Is the ANSIBLE_CONFIG variable set?"}
    B -- "Yes (Priority 1)" --> C["Use the file from ANSIBLE_CONFIG"]
    B -- "No" --> D{"Does ./ansible.cfg exist in the active directory?"}
    D -- "Yes (Priority 2)" --> E{"Is the directory world-writable?"}
    E -- "Yes (Potential Vulnerability)" --> F["Ignore ./ansible.cfg (Use fallback)"]
    E -- "No" --> G["Use local ./ansible.cfg"]
    D -- "No" --> H{"Does ~/.ansible.cfg exist in the user home?"}
    H -- "Yes (Priority 3)" --> I["Use personal ~/.ansible.cfg"]
    H -- "No" --> J{"Does /etc/ansible/ansible.cfg exist?"}
    J -- "Yes (Priority 4)" --> K["Use global /etc/ansible/ansible.cfg"]
    J -- "No" --> L["Use Engine Default Configuration (Default Fallback)"]
    F --> H

Project Directory Security Feature #

One of Ansible’s unique security handling features is automatically ignoring the local ./ansible.cfg configuration file when the directory containing it is world-writable (writable by anyone on the system, for example if the folder permission is set to 777 or 775 with an overly permissive group).

Why Does This Rule Exist? #

On multi-user Linux systems, if the project directory is world-writable, a malicious other user can drop a custom ansible.cfg file into that directory. That configuration file could contain dangerous privilege escalation parameters or load malicious plugin libraries. When you run the ansible command with sudo privileges, you’d accidentally execute that other user’s malicious code.

If Ansible detects an unsafe project folder, it shows a warning and immediately jumps to the next priority (~/.ansible.cfg or /etc/ansible/ansible.cfg):

[WARNING]: Avoid running Ansible from a directory writable by other users.
Hosting it in a world-writable directory is a security risk.

The Solution: You must make sure your project directory permissions are configured strictly (only the owner can modify files):

# Restrict directory permissions so only the owner has write access
chmod 755 ~/ansible-project
chmod 644 ~/ansible-project/ansible.cfg

Breaking Down the defaults Parameters #

The [defaults] block contains global parameters that control the basic behavior of all Ansible modules and playbooks. Here are the details of the important options we often use:

1. inventory #

This option tells Ansible where your default server inventory file is located.

[defaults]
# Use a single INI or YAML format file
inventory = hosts.ini

# Use a directory (Production Best Practice)
inventory = inventory/

If you point this parameter to a directory, Ansible processes all files inside it (whether INI, YAML, or dynamic inventory scripts) and merges them into one unified inventory.

2. remote_user #

The default operating system user on managed nodes that Ansible uses when initiating SSH connections.

[defaults]
remote_user = ansible-deploy

You can override this value at the playbook level, inventory group level, or use the -u argument when running the CLI.

3. private_key_file #

Defines the absolute or relative path to your SSH private key file for passwordless authentication.

[defaults]
private_key_file = ~/.ssh/id_ed25519

4. forks #

Sets the maximum number of parallel connections Ansible can open to managed nodes simultaneously.

[defaults]
forks = 15

By default, the forks value is set very low, at 5. If you have 100 target servers, Ansible processes the tasks incrementally (5 servers at a time), which can slow down deployment. Raising this value to 15 or 20 speeds up execution on large-scale infrastructure. However, you must be careful because too high a value can strain the CPU and memory capacity of the control node machine.

5. host_key_checking #

Controls whether Ansible must verify the target server’s host key fingerprint in the local known_hosts file before connecting via SSH.

[defaults]
host_key_checking = True
# ANTI-PATTERN: Permanently disabling host key checking in all environments
host_key_checking = False

# CORRECT: Always enable it in production environments to avoid Man-in-the-Middle (MitM) attacks
host_key_checking = True

Disabling this check is only allowed in local development lab environments (like vagrant or containers) to smooth out initial setup. In production, disabling this feature opens a security hole for connection hijacking attacks.

6. stdout_callback #

Changes the visual output display format of Ansible in the terminal when you run automation commands.

[defaults]
stdout_callback = yaml

Ansible’s default output (the default callback) tends to be very verbose and messy. Switching to the yaml format makes task execution reports, change statuses (changed), and error details display with a much cleaner, more readable indented structure.


Breaking Down the privilege_escalation Parameters #

Most infrastructure automation requires administrative (root) privileges. The [privilege_escalation] section controls how Ansible raises its privileges on target managed nodes.

[privilege_escalation]
# Enable privilege escalation by default for all tasks
become = True

# The escalation method used (sudo is the Linux gold standard)
become_method = sudo

# The escalation target user (almost always root)
become_user = root

# Determine whether Ansible should ask for the sudo password interactively
become_ask_pass = False

If the target managed node has been configured with a NOPASSWD rule in the /etc/sudoers file (as we discussed in the installation article), you must set become_ask_pass = False. Conversely, if server security policy requires a sudo password at all times, set this parameter to True, and use the --ask-become-pass option (or -K) when running the playbook to enter the password securely.


Performance Optimization in ssh_connection #

The [ssh_connection] section is the most important area for anyone who wants to tune Ansible playbook execution speed. This is where you optimize the built-in SSH connection.

1. pipelining #

This is the most important optimization feature in Ansible. By default, for every task, Ansible copies the automation module as a physical file to the target’s temporary directory, executes it, then deletes it. This requires several SSH connection transfer cycles.

[ssh_connection]
pipelining = True

Enabling pipelining cuts those cycles by sending the Python script directly through the standard input (stdin) of the active SSH connection without copying a physical file to the target disk. The performance comparison with and without pipelining is illustrated below:

flowchart TD
    subgraph "Without Pipelining (Standard Method)"
        direction TB
        A1["Control Node"] -->|"1. SSH Handshake"| B1["Managed Node"]
        A1 -->|"2. SFTP Copy Module (Temp File)"| B1
        A1 -->|"3. SSH Exec Interpreter (Run File)"| B1
        A1 -->|"4. SSH Exec Cleanup (Delete Temp File)"| B1
        B1 -->|"5. Send JSON Output"| A1
    end
    subgraph "With Pipelining (Optimized)"
        direction TB
        A2["Control Node"] -->|"1. SSH Handshake"| B2["Managed Node"]
        A2 -->|"2. Stream Python Script via stdin & Exec"| B2
        B2 -->|"3. Send JSON Output"| A2
    end

Enabling this feature can speed up playbook execution by up to 2 to 3 times.

[!WARNING] The pipelining = True feature is incompatible if your target server system enables the requiretty rule in the /etc/sudoers file. The requiretty rule rejects non-interactive command execution without a physical terminal. To use pipelining, you must disable requiretty in your target server’s sudoers configuration.

2. ssh_args #

A parameter for passing additional options directly to your control node machine’s SSH client.

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=accept-new
  • ControlMaster=auto: Enables SSH connection multiplexing. SSH reuses the already-open connection socket for subsequent commands to the same server, avoiding time-consuming SSH handshakes.
  • ControlPersist=60s: Keeps the background SSH socket connection open for 60 seconds after the last task finishes. If a new task runs within that window, the connection instantly reconnects.
  • StrictHostKeyChecking=accept-new: Automatically adds new host key fingerprints to your known_hosts file, but still blocks connections if the target host key changes (preventing MitM sabotage).

Overriding via Environment Variables #

Every configuration in the ansible.cfg file can be overridden using environment variables. Ansible environment variable naming follows a consistent rule: prefixed with ANSIBLE_, followed by the category and parameter names in capital letters.

The table below maps popular ansible.cfg configuration parameters to their environment variable counterparts:

CFG SectionParameterEnvironment VariableExample Value
[defaults]inventoryANSIBLE_INVENTORYinventory/staging/
[defaults]forksANSIBLE_FORKS50
[defaults]remote_userANSIBLE_REMOTE_USERroot
[defaults]host_key_checkingANSIBLE_HOST_KEY_CHECKINGFalse
[defaults]stdout_callbackANSIBLE_STDOUT_CALLBACKjson
[privilege_escalation]becomeANSIBLE_BECOMETrue
[ssh_connection]pipeliningANSIBLE_SSH_PIPELININGTrue

Using these environment variables is very useful in CI/CD automation pipelines. You don’t need to modify physical configuration files in the Git repository — just define environment variables on your CI/CD runner.


Ready-to-Use Configuration File Examples #

Here are two ansible.cfg file examples ready for you to copy and use according to your working environment’s characteristics.

1. ansible.cfg Template for Development Environments #

This template is designed for high execution speed and workflow tolerance in local lab/sandbox environments.

# File: ansible.cfg (Development/Lab Sandbox)
[defaults]
# Use the project's local inventory automatically
inventory = ./inventory/hosts.ini

# Default login details for the sandbox machine
remote_user = vagrant
private_key_file = .vagrant/machines/default/virtualbox/private_key

# Standard lab parallelism
forks = 5

# SSH key fingerprint tolerance (Suited for machines that are frequently rebuilt)
host_key_checking = False

# Disable retry file creation that clutters the project folder
retry_files_enabled = False

# Clean log display format
stdout_callback = yaml

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

[ssh_connection]
# Enable connection optimization
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=15s

2. ansible.cfg Template for Production Environments #

This template is designed with extra-strict security standards, complete log auditing, and enterprise-scale performance optimization.

# File: ansible.cfg (Production Environment Only)
[defaults]
# Structured production inventory folder path
inventory = ./inventory/production/

# Dedicated deployer user
remote_user = ansible-deployer
private_key_file = /opt/ansible/.ssh/deployer_private_key.ed25519

# High parallel connections for large-scale servers
forks = 25

# SECURITY: Always enable host key validation to prevent hijacking
host_key_checking = True

# Automation activity log storage location for compliance audit purposes
log_path = /var/log/ansible/production-execution.log

# Disable retry files
retry_files_enabled = False

# Use the structured YAML format
stdout_callback = yaml

[privilege_escalation]
become = True
become_method = sudo
become_user = root
# Don't store the become password here, use Ansible Vault if ask_pass is disabled
become_ask_pass = False

[ssh_connection]
# Pipelining must be enabled for large-scale server performance
pipelining = True

# Extra security: Use accept-new and strict SSH keys
ssh_args = -o ControlMaster=auto -o ControlPersist=120s -o StrictHostKeyChecking=yes -o ConnectTimeout=10

Summary #

  • Project Control Center — The ansible.cfg file defines the default parameters for automation execution, ensuring execution consistency among development team members.
  • Precedence Hierarchy — Configuration lookup has 4 priority levels; determination via Environment Variable (ANSIBLE_CONFIG) and the local project file (./ansible.cfg) sit at the highest levels.
  • Security Protection Feature — Ansible ignores the local ./ansible.cfg file if your project directory is set to world-writable (loose permissions like 777) to prevent privilege hijacking.
  • Forks Tuning — Adjusting the forks value (e.g. 15-25) increases task parallelism speed on large-scale infrastructure, at the cost of control node resource usage.
  • Production Host Key Checking — Always enable host_key_checking = True in production to protect systems from Man-in-the-Middle threats.
  • SSH Pipelining — Enabling pipelining = True transmits execution scripts directly to the target server’s Python standard input without physical file copies, cutting deployment duration by up to 3 times.
  • Multiplexing Optimization — The ControlMaster and ControlPersist configuration keeps SSH connection sockets alive in the background for reuse, eliminating repeated SSH handshake negotiation latency.

← Previous: Installation Next: Directory Structure →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact