October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
VGSources
Blog

How to Use Variables in Ansible Playbooks: A Practical Guide

A practical guide to defining, referencing, overriding, and debugging variables in Ansible playbooks, with inventory layouts and precedence guidance.
WorldAnsible Playbooks: A Practical Guide Length10 min Posted Quest giverVGSources Team
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Ansible variables let one playbook deploy different ports, packages, credentials, hosts, and environments without duplicating tasks. Define values in playbooks, vars_files, inventory group_vars and host_vars, or create them during a run with set_fact and register. Reference them with Jinja expressions such as {{ app_port }}. When definitions conflict, Ansible applies variable precedence; extra variables passed with --extra-vars (or -e) are the highest-priority override.

What an Ansible variable is

A variable is a named value that Ansible evaluates while it parses a play, selects hosts, runs tasks, or renders a template. The value can be a string, number, Boolean, list, or mapping. Keeping data separate from task logic means the same task sequence can serve development, staging, and production with different inventory or runtime values.

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use a variable in a task or template by placing its name inside a Jinja expression:

- name: Install the selected package
  ansible.builtin.package:
    name: '{{ app_package }}'
    state: present

- name: Listen on the configured port
  ansible.builtin.template:
    src: app.conf.j2
    dest: /etc/myapp/app.conf

In app.conf.j2, the corresponding expression could be port {{ app_port }}. Keep the data definition in one of the variable sources below, and keep the task focused on the action.

Choose the right place to define a value

Ansible reads variables from several locations. The best location depends on who owns the value, how widely it is shared, and whether it must change during execution.

Source Scope Use it for
Play vars The play where it is declared Small, play-specific settings that should travel with the playbook
vars_files The play that loads the file Configuration kept in a separate YAML file, such as environment settings
Inventory group_vars Every host in a group Shared defaults for a role, environment, or machine class
Inventory host_vars One inventory host Per-host exceptions, addresses, ports, or hardware-specific values
set_fact A host during the run A value calculated or discovered by an earlier task
register A host during the run The result returned by a task or module
Extra variables (-e or --extra-vars) The command invocation An intentional run-time override, such as a one-off release version

As a rule, put stable shared defaults in group_vars, host exceptions in host_vars, and temporary calculated values in set_fact or register. Use extra variables when the person launching the run must explicitly force a value.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Define variables directly in a play

A play can contain a vars mapping. This is convenient when the values are tightly coupled to that play and are unlikely to be reused elsewhere.

- name: Configure the game service
  hosts: game_servers
  vars:
    app_package: game-server
    app_port: 27015
    log_level: info
    feature_flags:
      telemetry: false
      replays: true
  tasks:
    - name: Install the service package
      ansible.builtin.package:
        name: '{{ app_package }}'
        state: present

    - name: Render service configuration
      ansible.builtin.template:
        src: game-server.conf.j2
        dest: /etc/game-server.conf

Scalar values such as info and numbers can be used directly. A mapping such as feature_flags groups related settings and is referenced with a dotted expression, for example {{ feature_flags.replays }}. Keep variable names descriptive and consistent; a misspelled name is a different variable, not an alias.

If the same variable name is written more than once in a play’s vars section, the later definition wins according to the official ansible-core 2.15 documentation. Avoid relying on that behavior: keep one authoritative definition so a future edit cannot silently change the result.

Load values from a vars file

Move configuration out of the playbook when it is larger, environment-specific, or maintained by a different person. Declare the file with vars_files:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
- name: Deploy the service
  hosts: game_servers
  vars_files:
    - vars/common.yml
    - vars/staging.yml
  tasks:
    - name: Render configuration
      ansible.builtin.template:
        src: game-server.conf.j2
        dest: /etc/game-server.conf

A file loaded this way is ordinary YAML:

app_package: game-server
app_port: 27015
log_level: info

Keep the file path under version control with the playbook when it is non-secret configuration. Sensitive values should be handled with the secret-management approach used by your organization rather than copied into command history or committed as plain text. The important variable behavior is that a value loaded from a file still participates in Ansible’s precedence rules; another source with higher precedence can replace it.

Use group_vars and host_vars in inventory

Inventory variables are the natural choice when the same playbook targets several environments or machine groups. A typical layout is:

inventory/
  hosts.yml
  group_vars/
    all.yml
    game_servers.yml
  host_vars/
    server-a.yml
    server-b.yml

group_vars/all.yml can hold a value shared by every inventory host. group_vars/game_servers.yml can hold defaults for the game_servers group:

app_package: game-server
app_port: 27015
log_level: info

Use host_vars/server-a.yml for a single host’s exception:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
app_port: 27016

The official precedence material places host_vars above group_vars, so the host-specific port takes precedence for server-a while other members keep the group value. This arrangement avoids copying an entire group configuration merely to change one host.

If multiple groups define the same variable, the latest page for using variables states that the last group loaded wins. Because load order can make a conflict difficult to see, give related groups distinct variable names or define a deliberate host-level exception instead of depending on overlapping group definitions.

Create values while a play is running

Calculate a value with set_fact

set_fact creates a host variable from values already available to the play. It is useful when a path, label, or decision depends on facts gathered earlier:

- name: Select the configuration directory
  ansible.builtin.set_fact:
    config_dir: '/etc/{{ app_package }}'

- name: Create the selected directory
  ansible.builtin.file:
    path: '{{ config_dir }}'
    state: directory
    mode: '0755'

The value is available to later tasks for that host in the current run. The precedence model means a stale fact can hide a corrected value from group_vars or another file-based source. If a variable appears unchanged after you edit inventory, inspect tasks that set it and any fact-caching configuration before changing the inventory again.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Capture a task result with register

register stores the result returned by a task so a later task can inspect it:

- name: Check whether the service is listening
  ansible.builtin.command: ss -lnt
  register: listening_ports
  changed_when: false

- name: Show the command output
  ansible.builtin.debug:
    var: listening_ports.stdout

A registered value is a result object, not automatically the text you expected. Modules commonly expose fields such as standard output, return code, and change status. Read the field you need and guard follow-up logic against a failed command or an absent field. The exact precedence position of registered variables, like that of set_fact, can differ between Ansible documentation versions, so consult the precedence page for the ansible-core release you run.

Override a value for one run with extra vars

Pass an extra variable on the command line when a deployment must use a deliberate one-time value:

ansible-playbook -i inventory/hosts.yml deploy.yml -e 'app_port=28015'

The official precedence guidance puts extra variables at the top of the stack. They are therefore the appropriate mechanism when you need to override a value from play variables, inventory, or a vars file without editing those sources. Treat this power carefully: an extra variable can mask a correction in your repository and command-line values may be visible in shell history or process listings.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For several values, pass a YAML or JSON mapping in the format supported by your Ansible installation, or use repeated key-value arguments. Keep the invocation documented in your deployment system so another operator can reproduce the same input.

Reference variables in tasks, templates, and conditions

Module arguments

Quote a Jinja expression when it is embedded in a YAML string:

- name: Set the service name
  ansible.builtin.service:
    name: '{{ app_package }}'
    state: started

When the entire value is a number or Boolean, preserve its type when the module expects one. A value written as a quoted string may be interpreted differently from an unquoted YAML number or Boolean, so match the module’s documented parameter type.

Templates

Template files use the same Jinja expression syntax:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
[server]
port={{ app_port }}
log_level={{ log_level }}
replays={{ feature_flags.replays }}

Keep templates declarative. Put calculations and selection logic in variables or tasks when that makes the resulting configuration easier to test.

Conditions

Variables can control whether a task runs:

- name: Enable replay storage
  ansible.builtin.service:
    name: replay-storage
    state: started
  when: feature_flags.replays

Do not wrap a bare variable in an extra Jinja expression inside when; the condition is already evaluated as an expression. If a value may be absent, establish it in the appropriate variable source or use a documented defaulting pattern before the conditional task.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Understand variable precedence without guessing

Ansible can load the same variable name from several places. It does not merge those definitions indiscriminately; it selects one according to a precedence hierarchy. The hierarchy is version-dependent, so use the precedence page matching your ansible-core release whenever the exact order matters.

  • The official ansible-core 2.15 documentation describes a 13-step order. Its lower-to-higher list includes role defaults, inventory and playbook group variables, host variables, facts and cached set_fact values, play variables, and prompted play variables, along with command-line values.
  • Ansible 7 documentation describes a longer 22-step order that additionally distinguishes vars_files, role variables, block variables, task variables, include_vars, set_fact, registered variables, and role or include parameters.
  • Both documentation sets put extra variables supplied with -e at the top.
  • Host-specific variables rank above group variables in the documented ordering.
  • The exact placement of set_fact, register, and include parameters differs between versions and between third-party explanations. Do not copy a numeric rank without checking the release you operate.

Think of precedence as an override decision, not as a reason to define the same key everywhere. Give a variable one clear owner, then use a higher-precedence source only for an intentional exception.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A worked example with environment defaults and a host exception

The following arrangement keeps shared settings in inventory, allows one server to use a different port, and permits a release operator to override the package version for a single run.

inventory/
  hosts.yml
  group_vars/
    game_servers.yml
  host_vars/
    server-a.yml
playbooks/
  deploy.yml

group_vars/game_servers.yml:

app_package: game-server
app_version: '1.8'
app_port: 27015

host_vars/server-a.yml:

app_port: 27016

playbooks/deploy.yml:

- name: Deploy the game service
  hosts: game_servers
  tasks:
    - name: Install the requested package version
      ansible.builtin.package:
        name: '{{ app_package }}={{ app_version }}'
        state: present

    - name: Render the service configuration
      ansible.builtin.template:
        src: game-server.conf.j2
        dest: /etc/game-server.conf

    - name: Record the effective configuration
      ansible.builtin.debug:
        msg:
          - 'package={{ app_package }}'
          - 'version={{ app_version }}'
          - 'port={{ app_port }}'

With no command-line override, server-a receives port 27016 and other group members receive 27015. To test a different package version once, run:

ansible-playbook -i inventory/hosts.yml playbooks/deploy.yml -e 'app_version=1.9'

The debug task is a useful, temporary way to confirm the effective values on each host before removing diagnostic output from a production play.

Diagnose an unexpected or undefined value

The variable is undefined

  • Check the spelling and capitalization in the definition and every reference.
  • Confirm that the play targets the inventory group or host whose variables you edited.
  • Verify that a vars_files path is included by the play that needs it.
  • Move a value from a host-only file to the correct group file if several hosts require it.

The value is present but wrong

  • Search for every definition of the variable name across playbooks, vars files, group variables, host variables, and task files.
  • Check the command that launched the play for -e; extra vars override lower sources.
  • Look for an earlier set_fact task or a registered result being reused under the same name.
  • If several inventory groups define the key, resolve the conflict rather than relying on which group happens to load last.

An inventory edit has no effect

A run-time fact may have higher precedence than the file you changed, and cached facts can survive beyond the task that created them. Inspect the play for set_fact, check whether fact caching is enabled, and remove or rename the calculated variable when the file-based value should be authoritative.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A value changes between Ansible versions

Do not assume a third-party precedence chart applies to your installation. Compare the official precedence documentation for the exact ansible-core version, especially for set_fact, registered variables, includes, and role parameters. Then add an explicit host or extra-variable override only where the behavior is intentional.

Practical rules for maintainable variables

  • Use names that describe purpose, such as app_port, rather than ambiguous names such as port.
  • Keep shared defaults in group_vars and put only genuine exceptions in host_vars.
  • Use vars_files to separate sizable environment configuration from task flow.
  • Reserve set_fact for values calculated during execution; do not use it merely to duplicate inventory data.
  • Give registered results distinctive names, such as package_check, so they are not confused with the value they report.
  • Use extra vars for a deliberate, documented override rather than as a permanent configuration store.
  • When a key is defined in multiple places, document which source is expected to win and test that assumption on the target ansible-core version.
  • Before a production run, add a temporary debug task to display non-sensitive effective values and remove it after validation.

Variable checklist before running a playbook

  1. List every variable the play references and identify its intended source.
  2. Confirm the selected inventory contains the expected groups and hosts.
  3. Check that group_vars, host_vars, and every declared vars_files path is in the location Ansible loads for that inventory and play.
  4. Search for duplicate definitions and decide which one should win.
  5. Inspect the command line for accidental -e values.
  6. Review tasks that create set_fact values or register results under names used later.
  7. For a precedence-sensitive change, consult the official hierarchy for the exact ansible-core release instead of relying on a generic chart.
  8. Run a safe validation with a debug task that prints only non-secret values, then deploy once the effective configuration matches your plan.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More quests from Patch Notes

  1. How to Set Up a RedM (RDR2) Server in 2026: License Key, txAdmin and server.cfg, Step by StepBlog11min
  2. Best RedM (RDR2) Server Hosting in 2026: Comparing Four Hosts on Slots, Memory and PriceBlog10min
  3. How to Host a Mindustry Server in 2026: server-release.jar, Port 6567 and the Commands That MatterBlog7min
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.