Skip to content

Play Level Variables


The previous chapter borrowed vars: just to have something to print. This chapter covers it properly, along with its natural next step: pulling variables out of the playbook entirely and into their own file.

vars: — Defining Variables On A Play

A play-level vars: key holds a simple mapping of names to values, available to every task in that play:

playbook.yaml
- name: Basic Vars
  hosts: servers
  vars:
    app_name: "MyApp"
    app_port: 8080
  tasks:
    - name: Print the app name and port
      ansible.builtin.debug:
        msg: "{{ app_name }} runs on port {{ app_port }}"
ok: [ubuntu] => {
    "msg": "MyApp runs on port 8080"
}

Nothing new mechanically from the last chapter — just more than one variable, and both referenced in the same string.

vars_files: — Loading Variables From A Separate File

Instead of writing values directly into the play, vars_files loads them from a separate YAML file:

playbook.yaml
- name: Using External Vars
  hosts: servers
  vars_files:
    - vars/app.yaml
  tasks:
    - name: Print the loaded variable
      ansible.builtin.debug:
        msg: "{{ app_name }}"
vars/app.yaml
app_name: "MyApplication"
app_port: 8080

The file itself is just a flat mapping of variable names to values — no vars: wrapper needed inside it, since the whole file’s contents are the variables.

Why Split Variables Into Their Own File?

A few genuinely practical reasons: it separates what to do (the playbook’s tasks) from what values to use (the config-like data in the vars file), makes it easy to reuse the same set of values across more than one playbook, and gives anyone editing configuration a much smaller, more obvious place to look than scrolling through task logic to find a hardcoded value.

A Gotcha: vars_files Beats Plain vars

Here’s a genuinely counterintuitive one, worth knowing now rather than discovering by accident. If the same variable is defined both directly in a play’s vars: and in a file loaded via vars_files, the vars_files value wins — not the one sitting right there in the play itself, which is what most people would guess first.

playbook.yaml
- name: Precedence Demo
  hosts: servers
  vars_files:
    - vars/app.yaml
  vars:
    app_name: "FromPlayVars"
  tasks:
    - name: Print app_name
      ansible.builtin.debug:
        msg: "{{ app_name }}"
vars/app.yaml
app_name: "FromVarsFile"
ok: [ubuntu] => {
    "msg": "FromVarsFile"
}

This is documented, deliberate Ansible behavior, not a bug — but it’s exactly the kind of thing you don’t want to be surprised by mid-debugging. We’ll come back to the fuller picture of variable precedence in a later chapter; for now, the practical fix is simpler than memorizing rankings: don’t define the same variable in both places within one play. Pick one source per variable, and this ambiguity never comes up at all.

Best Practices

  • Use vars: for small, playbook-specific values that don’t need to exist anywhere else.
  • Use vars_files for larger sets of values, or anything meant to be reused across more than one playbook.
  • Never define the same variable in both vars: and a vars_files entry in the same play — the override rule above is real, but avoiding the collision entirely is simpler than relying on remembering which side wins.
  • Keep vars files organized in a dedicated directory (vars/, as shown above) so they’re easy to find independently of the playbooks that use them.
Last updated on