

Ansible variables are what make playbooks reusable. Without them, every environment needs its own hardcoded playbook. With them, one playbook covers dev, staging, and production because the differences live in variables, not in the tasks.
At a glance
Ansible variables define values that differ between hosts, environments, or runs. Current version: ansible-core 2.20.5 (April 2025). Ansible evaluates 22 precedence layers to resolve variable values; extra vars (-e) always win. Variables use Jinja2{{ variable_name }}syntax in playbooks and templates.
What are Ansible variables
Ansible variables are dynamic components that allow for reusability in playbooks and roles, enhancing the efficiency of configurations.
Variables make playbooks work across environments without duplication: dev, staging, and production can each get the right values from a single set of tasks.
Why use variables?
- Dynamic configurations: Variables allow you to define values that can change based on the environment or context, such as different server IPs, usernames, or package versions
- Simplified management: By using variables, you can manage complex configurations more easily, as changes need to be made in only one place
- Enhanced readability: Meaningful variable names make playbooks more understandable and maintainable
Variable naming rules
Ansible enforces specific rules for variable names to ensure consistency and prevent conflicts:
- Start with a letter or underscore: Variable names must begin with a letter (a-z, A-Z) or an underscore (_)
- Allowed characters: Subsequent characters can include letters, numbers (0-9), and underscores
- Avoid reserved words: Do not use reserved words from Python or Ansible's playbook keywords as variable names
Examples of valid variable names:
web_server_ip: "192.168.1.10"
app_port: 8080
max_retries: 3
_internal_flag: true
database_name: "prod_db"Examples of invalid variable names:
# Invalid: these will cause errors
1st_server: bad # starts with a number
my-server: bad # contains a hyphen
class: bad # Python reserved word
ansible_host: bad # reserved by AnsibleAdhering to these naming conventions is a good start, but you also need to give your variables meaningful names so anyone reading your Ansible playbooks will understand what they are for.
Where can you use Ansible variables?
Ansible provides multiple ways to define and use variables, each tailored to specific use cases. Below is a detailed explanation of the various methods, with examples to illustrate their practical applications.
1. Defining Variables in Playbooks
Defining variables directly in a playbook is the simplest and most accessible method. This approach is useful when you want to tightly couple variables with a specific playbook or perform quick tests. Variables declared in this manner are scoped to the playbook and cannot be reused elsewhere.
Playbook example:
---
- name: Deploy application
hosts: webservers
vars:
app_name: "my_app"
app_port: 8080
tasks:
- name: Start application service
ansible.builtin.service:
name: "{{ app_name }}"
state: started
- name: Open firewall port
ansible.posix.firewalld:
port: "{{ app_port }}/tcp"
state: enabled
permanent: trueExplanation:
- The vars section defines ‘app_name’ and ‘app_port’
- The variables are accessed using Jinja2 syntax
({{ variable_name }})in tasks.
This method keeps the playbook self-contained but can become unwieldy if the number of variables increases.
2. Defining Variables in Inventory Files
Inventory files allow you to associate variables with specific hosts or groups of hosts. This method is ideal for defining system-specific or environment-specific values without altering playbooks.
Example inventory file (hosts):
[webservers]
web1 ansible_host=192.168.1.10 app_port=8080 db_name=prod_db
web2 ansible_host=192.168.1.11 app_port=8080 db_name=prod_db
[databases]
db1 ansible_host=192.168.1.20 db_port=5432 db_name=prod_db
db2 ansible_host=192.168.1.21 db_port=5432 db_name=prod_dbExplanation:
- Host-specific variables like ‘app_por’t and ‘db_name’ are defined alongside each host
- Groupings such as [webservers] and [databases] help categorize hosts by role
- These variables can be directly accessed in playbooks targeting these hosts or groups
This method ensures configuration flexibility across different environments or roles.
3. Defining Variables in Separate Variable Files
Storing variables in separate files is a best practice for larger projects. This keeps the playbooks clean and allows variables to be reused across multiple playbooks.
Variable file (vars/main.yml):
app_name: "my_application"
app_port: 8080
db_host: "db.example.com"
db_port: 5432
log_level: "info"Playbook example:
---
- name: Deploy with external variables
hosts: webservers
vars_files:
- vars/main.yml
tasks:
- name: Configure application
ansible.builtin.template:
src: templates/app.conf.j2
dest: /etc/{{ app_name }}/app.conf
- name: Start application
ansible.builtin.service:
name: "{{ app_name }}"
state: startedExplanation:
- The vars_files directive includes external variable files, making the playbook easier to read and maintain
- Variable files can follow specific naming conventions for better organization, such as vars/dev.yml for development or vars/prod.yml for production
This approach supports modularity and scalability in managing configurations.
4. Group and Host Variables
Group and host variables are stored in designated directories (group_vars and host_vars) and automatically applied to their respective groups or hosts. This structure is highly scalable and ensures consistent configurations across environments.
Group variables (group_vars/webservers.yml):
http_port: 80
max_connections: 200
deploy_user: "deploy"
app_dir: "/opt/myapp"Host variables (host_vars/web1.yml):
http_port: 8080
server_role: "primary"
backup_enabled: trueExplanation:
- Variables in group_vars apply to all hosts in the group, such as webservers
- Variables in host_vars override group variables for a specific host, such as web1
This approach makes managing variables for complex inventories simpler and enforces a clear separation of concerns.
5. Defining Variables at Runtime
The -e or --extra-vars option lets you define variables dynamically during playbook execution. This is especially useful for ad-hoc configurations or testing.
Example command:
ansible-playbook deploy.yml -i inventory.ini -e "env=prod app_version=2.1.0"Explanation:
- Variables passed at runtime override other variable definitions in the hierarchy
This method is convenient for temporarily changing settings without editing files. It is, however, less suitable for configurations that need to be reused consistently.
6. JSON and YAML Files for Runtime Variables
When working with a large number of variables, JSON or YAML files provide a structured way to pass variables at runtime.
JSON example (vars.json):
{
"app_name": "my_application",
"app_port": 8080,
"db_host": "db.example.com",
"env": "production"
}YAML example (vars.yml):
app_name: "my_application"
app_port: 8080
db_host: "db.example.com"
env: "production"Passing the file:
ansible-playbook deploy.yml -i inventory.ini -e "@vars.json"or
ansible-playbook deploy.yml -i inventory.ini -e "@vars.yml"Explanation:
The structured format improves readability and minimizes errors when passing multiple variables. This method is excellent for managing complex variable sets with special characters.
Types of Ansible variables
Ansible supports a wide range of variable types to handle diverse data structures and use cases. Here's an in-depth look at the variable types, along with detailed examples to clarify their applications.
1. Simple Variables
Simple variables store a single value, such as a string, number, or boolean. These are ideal for straightforward configurations where each variable represents a single property or setting.
Examples:
app_name: "my_application"
max_retries: 3
debug_mode: false
timeout_seconds: 30
admin_email: "ops@example.com"Use case:
- Simple variables work well for settings like application names (‘app_name’), numeric parameters (‘max_retries’), or flags (‘debug_mode’)
- These variables are easy to define and reference, making them suitable for straightforward configurations
2. Complex Variables
These variables allow for more sophisticated configurations by supporting lists, dictionaries, and nested data structures. These are essential for managing interconnected or hierarchical data.
a. Lists
Lists are used to define an ordered collection of items. Each item in the list can represent a related piece of information, such as versions, IP addresses, or tasks.
Example:
supported_versions:
- "1.0"
- "1.5"
- "2.0"
web_servers:
- web1.example.com
- web2.example.com
- web3.example.comUse case:
- Lists are perfect for scenarios where you need to iterate over multiple values, such as supported application versions or a list of servers
b. Dictionaries
Dictionaries, also called maps or hashes, define key-value pairs. They are ideal for grouping related configurations into a single structure.
Example:
database_config:
host: "db.example.com"
port: 5432
name: "prod_db"
user: "app_user"
pool_size: 10Use case:
- Dictionaries work well for organizing structured data like database configurations (database_config), where each key-value pair represents a specific setting
- You can easily reference individual elements, such as database_config.host which uses the dot notation
c. Nested variables
These variables combine lists and dictionaries to model more complex relationships, such as defining multiple servers with unique attributes.
Example:
servers:
- name: "web1"
ip: "192.168.1.10"
role: "primary"
- name: "web2"
ip: "192.168.1.11"
role: "secondary"Use case:
- Nested variables are invaluable for scenarios involving groups of objects, such as multiple servers, where each object has its own properties (name, ip)
Referencing nested variables: Nested variables can be accessed using dot notation or bracket notation.
Example in a task:
- name: Display server info
ansible.builtin.debug:
msg: "Server {{ servers[0].name }} at {{ servers[0].ip }} ({{ servers[0].role }})"Explanation:
servers[0].nameretrieves the name of the first server in the list (web1)servers[0].ipretrieves the ip of the first server (192.168.1.10)
This allows you to dynamically access and use specific elements of nested data structures.
Special variables in Ansible
Ansible's special variables are predefined and offer insights into the system data, inventory, or execution context of a playbook or role. These variables are categorized into magic variables, connection variables, and facts, each serving a specific purpose.
It’s crucial to note that these variable names are reserved by Ansible and cannot be redefined. Below, we explore these categories in detail.
Magic variables
Ansible automatically creates magic variables to reflect its internal state. These variables cannot be altered by users but can be accessed directly to retrieve useful information about the playbook’s execution and environment.
Using inventory_hostname
This magic variable represents the name of the current host in the inventory.
Playbook example:
---
- name: Display host information
hosts: all
tasks:
- name: Show current host name
ansible.builtin.debug:
msg: "Running on {{ inventory_hostname }}"Output:
ok: [web1] => {
"msg": "Running on web1"
}
ok: [web2] => {
"msg": "Running on web2"
}Other essential magic variables
- hostvars: Provides information about other hosts in the inventory, including their associated variables
- Example:
hostvars['web1'].ansible_hostretrieves the 'ansible_host' variable for the web1 host
- Example:
- group_names: Contains a list of group names to which the current host belongs
- Example:
group_nameshelps identify the roles or purposes assigned to the current host
- Example:
- groups: Group names to the list of hosts in each group
- Example:
groups['webservers']returns all hosts in the webservers group
- Example:
These variables are indispensable for dynamic inventory management and playbook flexibility. You can reference the list in the documentation here.
Connection variables
Connection variables control how Ansible connects to remote hosts during playbook execution. They define the connection type, user, and other related settings.
Using ansible_connection
The ansible_connection variable indicates the connection type (e.g., SSH, local, or winrm).
Playbook example:
---
- name: Run local diagnostics
hosts: localhost
connection: local
tasks:
- name: Show connection type
ansible.builtin.debug:
msg: "Connection type: {{ ansible_connection }}"Explanation:
- The connection: local directive specifies that the playbook should run locally rather than connecting to a remote host
- The
ansible_connectionvariable dynamically identifies the connection type (local in this case)
Sample output:
ok: [localhost] => {
"msg": "Connection type: local"
}Ansible facts
Ansible facts are a collection of data automatically gathered about remote systems during playbook execution.
They provide detailed information about the system, such as operating system details, network interfaces, disk configurations, and more. Facts are stored in the ansible_facts variable, which can be used in tasks, conditionals, and templates.
Key features of Ansible facts
- Automatic collection: Facts are gathered at the start of each play by default
- Access and scope: Facts are available in the
ansible_factsdictionary and can also be accessed as top-level variables with theansible_ prefix - Customization: Users can add custom facts or disable fact gathering if not needed
Viewing facts
To see all available facts for a host, add this task to a playbook:
- name: Print all facts
ansible.builtin.debug:
var: ansible_factsAlternatively, you can gather raw fact data from the command line:
ansible all -i inventory.ini -m ansible.builtin.setupUsing facts in playbooks
Facts allow you to dynamically configure tasks based on system attributes. For example, you can retrieve the hostname and default IPv4 address of a system:
Playbook example:
---
- name: Display system information
hosts: all
tasks:
- name: Show hostname and IP address
ansible.builtin.debug:
msg: "Host: {{ ansible_facts['nodename'] }}, IP: {{ ansible_facts['default_ipv4']['address'] }}"Explanation:
ansible_facts['nodename']: Retrieves the system hostnameansible_facts['default_ipv4']['address']: Retrieves the default IPv4 address
Sample output:
ok: [web1] => {
"msg": "Host: web1.example.com, IP: 192.168.1.10"
}Common use cases for facts
- Dynamic Configurations
- Use facts to configure tasks dynamically, such as installing packages based on the OS family:
- name: Install web server
ansible.builtin.package:
name: "{{ 'apache2' if ansible_facts['os_family'] == 'Debian' else 'httpd' }}"
state: present- Conditionals and filters: Use facts to apply conditional logic, such as running tasks only on systems with specific attributes
- Custom facts: Custom facts allow you to extend Ansible's default capabilities by defining your own facts specific to your needs. These can be either static facts (defined in files) or dynamic facts (generated by scripts). Custom facts are stored in the ansible_local namespace to avoid conflicts with system facts.
Example: Adding a static fact
Create a file for the fact:
- On the remote host, create a directory /etc/ansible/facts.d
- Add a file named custom.fact with content in INI format:
[application]
version=2.1.0
environment=production
deployed_by=ansibleAccess the fact in a playbook:
- name: Show custom application facts
ansible.builtin.debug:
msg: "App version: {{ ansible_local['custom']['application']['version'] }}"Example: Adding a dynamic fact
Create a script for the fact:
- Place an executable script (e.g., generate fact.sh) in /etc/ansible/facts.d
- The script should output JSON:
#!/bin/bash
echo '{"app_status": "running", "last_deploy": "2025-04-01", "version": "2.1.0"}'Access the fact in a playbook:
- name: Show dynamic application facts
ansible.builtin.debug:
msg: "App status: {{ ansible_local['generate']['app_status'] }}"Best practices with facts
- Caching Facts: Use fact caching to improve performance in large environments or repetitive tasks.
- Disabling Facts: Turn off fact gathering for better scalability if you don’t need system details as in the playbook below:
---
- name: Deploy without fact gathering
hosts: all
gather_facts: false
tasks:
- name: Run deployment script
ansible.builtin.shell: /opt/deploy.sh
args:
chdir: /opt/myappVariable precedence
Because variables can be defined in various locations, Ansible uses a precedence hierarchy to decide which value takes effect. Below is the entire list as defined in the documentation, with the least precedence at the top (the last listed variables override all other variables):
- command line values (for example, -u my_user, these are not variables)
- role defaults
- inventory file or script group vars
- inventory group_vars/all
- playbook group_vars/all
- inventory group_vars/*
- playbook group_vars/*
- inventory file or script host vars
- inventory host_vars/*
- playbook host_vars/*
- host facts / cached set_facts
- play vars
- play vars_prompt
- play vars_files
- role vars
- block vars (only for tasks in block)
- task vars (only for the task)
- include_vars
- set_facts / registered vars
- role (and include_role) params
- include params
- extra vars (for example, -e "user=my_user")(always win precedence)
Working with variables at scale
Ansible variables enable scalable, flexible, and reusable automation. By mastering their usage and following best practices, you can enhance your Ansible projects' efficiency and maintainability.
Key takeaways include:
- Flexibility: Variables adapt your playbooks to different contexts with minimal changes.
- Organization: Properly organizing and centralizing variables reduces redundancy and simplifies management.
- Efficiency: Using consistent naming conventions and precedence rules keeps large playbooks maintainable.
By applying the best practices outlined here, you can keep Ansible projects maintainable and easy to hand off.
If you're interested in learning more about Ansible, I recommend these two blog posts:
- The Essential Ansible Tutorial: A Step by Step Guide
- Mastering Ansible Playbooks: Examples and Step by Step Guide
Running Ansible with env0
Integrating Ansible with env0 revolutionizes infrastructure management by combining Ansible’s powerful automation capabilities with env0’s advanced orchestration and collaboration features. This integration simplifies workflows, reduces manual effort, and enhances governance.
Key advantages of env0 integration
1. Automated execution: Instead of manually running Ansible commands through the CLI, env0 allows you to directly define and manage environments. This reduces errors and improves consistency, reducing errors and improving consistency.
2. Template management: Use env0 to create and manage environments based on Ansible templates. These templates can specify the Ansible version, SSH keys, and other configurations, ensuring that your deployments adhere to organizational standards.
3. Enhanced GitHub integration: Link your env0 environments to your GitHub repository. By specifying the folder where your Ansible playbooks reside, env0 ensures that your scripts and configurations are always accessible and up-to-date.
4. Simplified variable handling: With env0, defining and managing environment variables like ANSIBLE_CLI_inventory becomes straightforward. This enables Ansible to dynamically locate and utilize the correct inventory files for deployments.
5. Automated execution: Once an environment is initiated, env0 handles the entire process: cloning the repository, setting up the working directory, loading variables, and executing the playbooks. This removes manual steps from deployment cycles.
6. Governance and collaboration: env0’s built-in RBAC and OPA policy support ensures that deployments remain secure and compliant. Team members can collaborate efficiently with clear access controls and activity tracking.
7. Comprehensive logs and insights: Review deployment logs in env0 to verify configurations and monitor playbook execution. This transparency aids troubleshooting and ensures accountability.
8. Multi-framework support: Combine Ansible with other IaC tools like Terraform, OpenTofu, Pulumi, or CloudFormation within env0. This flexibility allows teams to use the best tools for specific tasks while maintaining a cohesive workflow.
By integrating Ansible with env0, teams can achieve greater efficiency, improve collaboration, and maintain strict governance over their infrastructure.
Frequently asked questions
Q. How do I specify a variable in Ansible?
Variables can be specified using the vars keyword in a playbook, inventory files, or through external variable files. They can also be passed at runtime using the -e flag.
Q. What is {{ item }} in Ansible?
{{ item }} is a placeholder used in loops to reference the current item being iterated over.
Q. What is the difference between vars_files and include_vars?
- vars_files: Used to include external variable files in a playbook
- include_vars: A task that dynamically includes variable files during playbook execution
Q. How do I use environment variables in Ansible?
Environment variables can be accessed using the ansible_env dictionary. Learn more in the documentation.
Q. How do I pass variables to an Ansible playbook?
Variables can be passed using the -e flag, through inventory files, or by including them in variable files referenced in the playbook.
Q. What is the order of precedence for Ansible variables?
The order of precedence determines which variable value is used when multiple variables with the same name exist. Extra variables (-e) have the highest precedence.
Q. What is the order of execution in Ansible?
Ansible executes tasks in the order they are listed in the playbook, applying variables and configurations as it progresses.
Q. What is the Ansible naming convention for variables?
Variables should start with a letter or underscore, and subsequent characters can include letters, numbers, and underscores. Avoid using reserved words or special characters.
Q. How do I set the env variable using Ansible?
Use the environment keyword in tasks to set environment variables for that task.
Q. How do I assign variables in Ansible?
Assign variables using the vars keyword, in inventory files, or through set_fact during playbook execution.
Q. How do I use environment variables in an Ansible playbook?
Access environment variables using the ansible_env dictionary or pass them explicitly when running the playbook.
Q. How do I pass variables to Ansible playbook?
Variables can be passed using the -e option, included in inventory files, or defined in external files and referenced in the playbook.

.webp)

![Using Open Policy Agent (OPA) with Terraform: Tutorial and Examples [2026]](https://cdn.prod.website-files.com/63eb9bf7fa9e2724829607c1/69d6a3bde2ffe415812d9782_post_th.png)