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:
- 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:
- name: Using External Vars
hosts: servers
vars_files:
- vars/app.yaml
tasks:
- name: Print the loaded variable
ansible.builtin.debug:
msg: "{{ app_name }}"app_name: "MyApplication"
app_port: 8080The 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.
- 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 }}"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_filesfor larger sets of values, or anything meant to be reused across more than one playbook. - Never define the same variable in both
vars:and avars_filesentry 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.