# Understanding variable precedence

**URL:** <https://forum.ansible.com/t/understanding-variable-precedence/12706>\
**Category:** Ansible Project\
**Created:** [April 20, 2013, 11:29pm UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706 "2013-04-20T23:29:18Z")\
**Posts on this page:** 19\
**Page:** 1

<div class="post-metadata">

**Author:** ![GW1](https://avatars.discourse-cdn.com/v4/letter/g/c89c15/32.png) [@GW1](https://forum.ansible.com/u/GW1)\
**Post date:** [April 20, 2013, 11:29pm UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706/1 "2013-04-20T23:29:18Z")

</div>

Hi,

Can somebody please explain to me why the variable precedence ([http://ansible.cc/docs/playbooks2.html#understanding-variable-precedence](http://ansible.cc/docs/playbooks2.html#understanding-variable-precedence)) is the way it is?

Especially why `vars` and `vars_files` are in that order and so far apart? Shouldn't one be just an inline replacement of the other?

To me the following variable precedence would seem more natural:

1. `--extra-vars` in command line  
2. `vars` in a playbook  
3. `vars_files` in a playbook  
4. assigned variables with `register`  
5. parameters passed to tasks/handlers list `include` statements (also parameters to `roles`)  
6. gathered facts  
7. parameters passed to playlist `include` statements  
8. inventory host variables  
9. inventory group variables in inheritance order

Note that the above 4. and 6., 5. and 7. in current documentation are joined together, but for clarity it would be better to separate them (and probably change their order as I did above).

I also put `vars` in a playbook (2.) before `vars_files`, because it feels more natural to have sane defaults in external files and override them locally. Most programs behave similar to this when loading their configuration. This way way the following use case would be much more readable (because there would be no need to look in the additional `vars_files` files to see what is going on or put things in inventory variables or use parameterized roles):

# webservers.yml - assigns roles to hosts, allow specifying which repository and branch to use

---

<div class="post-metadata">

**Author:** ![Michael\_DeHaan2](https://avatars.discourse-cdn.com/v4/letter/m/da6949/32.png) [@Michael\_DeHaan2](https://forum.ansible.com/u/Michael_DeHaan2)\
**Post date:** [April 21, 2013, 2:13am UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706/2 "2013-04-21T02:13:23Z")

</div>

Different folks are going to have different preferences, but there’s an error here – vars and vars\_files have essentially the same priority, so I’ll fix the docs.

facts and registered variables will override them.

---

<div class="post-metadata">

**Author:** ![Michael\_DeHaan2](https://avatars.discourse-cdn.com/v4/letter/m/da6949/32.png) [@Michael\_DeHaan2](https://forum.ansible.com/u/Michael_DeHaan2)\
**Post date:** [April 21, 2013, 2:47am UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706/3 "2013-04-21T02:47:56Z")

</div>

Ok, I just rewrote this section and pushed the changes

Here is the new text that explains the _why_. As we have thousands of playbooks in the wild I’m not going to be changing precedence order any, so I hope this explains how to do anything you would want to do – and where to put something if you care for a default.

== start ==

You have already learned about inventory variables, ‘vars’, and ‘vars\_files’. In the event the same variable name occurs in more than one place, what happens? There are really three tiers of precedence, and within those tiers, some minor ordering rules that you probably won’t even need to remember. We’ll explain them anyway though.

Variables that are set during the execution of the play have highest priority. This includes registered variables and facts, which are discovered pieces of information about remote hosts.

Descending in priority are variables defined in the playbook. ‘vars\_files’ as defined in the playbook are next up, followed by variables as passed to ansible-playbook via –extra-vars (-e), then variables defined in the ‘vars’ section. These should all be taken to be basically the same thing – good places to define constants about what the play does to all hosts in the play.

Finally, inventory variables have the least priority. Variables about hosts override those about groups. If a variable is defined in multiple groups and one group is a child of the other, the child group variable will override the variable set in the parent.

This makes the ‘group\_vars/all’ file the best place to define a default value you wish to override in another group, or even in a playbook. For example, your organization might set a default ntp server in group\_vars/all and then override it based on a group based on a geographic region. However if you type ‘ntpserver: [asdf.example.com](http://asdf.example.com)’ in a vars section of a playbook, you know from reading the playbook that THAT specific value is definitely the one that is going to be used. You won’t be fooled by some variable from inventory sneaking up on you.

So, in short, if you want something easy to remember: facts beat playbook definitions, and playbook definitions beat inventory variables.

== end ==

---

<div class="post-metadata">

**Author:** ![Michael\_DeHaan2](https://avatars.discourse-cdn.com/v4/letter/m/da6949/32.png) [@Michael\_DeHaan2](https://forum.ansible.com/u/Michael_DeHaan2)\
**Post date:** [April 21, 2013, 7:14am UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706/4 "2013-04-21T07:14:20Z")

</div>

Here is something some folks may also not know about:

[http://ansible.cc/docs/playbooks2.html#conditional-imports](http://ansible.cc/docs/playbooks2.html#conditional-imports)

–Michael

---

<div class="post-metadata">

**Author:** ![GW1](https://avatars.discourse-cdn.com/v4/letter/g/c89c15/32.png) [@GW1](https://forum.ansible.com/u/GW1)\
**Post date:** [April 21, 2013, 11:44am UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706/5 "2013-04-21T11:44:46Z")

</div>

Hi,

I noticed that the new text does not mention parameters to included tasks/handlers list and also not for parameters to included playbooks. Also `vars_prompt` could be added.

Yes, changing the precedence is dangerous, but I really wondering if any playbook in the wild ever uses:

vars:  
foo: “this will get overriden”  
vars\_files:

- vars/file\_overriding\_foo.yml

Maybe in combination with conditional imports it would make a little sense (under one condition `foo` is overriden, otherwise not), but I doubt anyone is doing this. Or is there anyone on this list? Can you explain the use case?

Greetings,  
gw

---

<div class="post-metadata">

**Author:** ![Michael\_DeHaan](https://avatars.discourse-cdn.com/v4/letter/m/c4cdca/32.png) [@Michael\_DeHaan](https://forum.ansible.com/u/Michael_DeHaan)\
**Post date:** [April 21, 2013, 2:11pm UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706/6 "2013-04-21T14:11:53Z")

</div>

Yep, it’s useful.

The usage of vars\_files conditionally (see the last link I shared) can use a fact to determine a path to a variable file.

For instance, you could set up defaults in vars, and then have some OS specific variables loaded from something like this:

vars\_files:

- “/vars/{{ ansible\_os\_family}}.yml”

As such, you _might_ want to set the default in vars.

We’re not changing the precedence orders 🙂

---

<div class="post-metadata">

**Author:** ![PJ1](https://avatars.discourse-cdn.com/v4/letter/p/eb8c5e/32.png) [@PJ1](https://forum.ansible.com/u/PJ1)\
**Post date:** [April 22, 2013, 2:12pm UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706/7 "2013-04-22T14:12:17Z")

</div>

How does it work with precedence with hosts/groups specified on command line versus hosts and groups in playbook-of-playbooks and/or playbooks?

---

<div class="post-metadata">

**Author:** ![Michael\_DeHaan](https://avatars.discourse-cdn.com/v4/letter/m/c4cdca/32.png) [@Michael\_DeHaan](https://forum.ansible.com/u/Michael_DeHaan)\
**Post date:** [April 22, 2013, 2:35pm UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706/8 "2013-04-22T14:35:37Z")

</div>

I’m not sure what you mean by hosts/groups specified on the command line, can you elaborate?

Although, host/group variables are always lowest in terms of priority.

Playbooks including one another does not affect this.

---

<div class="post-metadata">

**Author:** ![PJ1](https://avatars.discourse-cdn.com/v4/letter/p/eb8c5e/32.png) [@PJ1](https://forum.ansible.com/u/PJ1)\
**Post date:** [April 22, 2013, 2:52pm UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706/9 "2013-04-22T14:52:40Z")

</div>

# ansible-playbook -l host01 main.yml

main.yml: (hosts: defaultA)  
 → include sub\_book1.yml (hosts: hostA)  
 → include sub\_book2.yml (hosts: all)

It seems that group defaultA will be overridden by command line option, which is fine.  
hostA in sub\_book1.yml will be skipped as it’s not match with commnad line, but required for main.yml to succeed.  
sub\_book2.yml will be skipped as host01 cannot be found in “all” or not matched with command line (most likely because of cobbler, still have to look at this).

Just a bit confused how it is intended to work.

---

<div class="post-metadata">

**Author:** ![PJ1](https://avatars.discourse-cdn.com/v4/letter/p/eb8c5e/32.png) [@PJ1](https://forum.ansible.com/u/PJ1)\
**Post date:** [April 22, 2013, 2:54pm UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706/10 "2013-04-22T14:54:39Z")

</div>

I’m lagging behind a bit (sitting on RHEL machine) so running 1.0

---

<div class="post-metadata">

**Author:** ![PJ1](https://avatars.discourse-cdn.com/v4/letter/p/eb8c5e/32.png) [@PJ1](https://forum.ansible.com/u/PJ1)\
**Post date:** [April 22, 2013, 3:08pm UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706/11 "2013-04-22T15:08:39Z")

</div>

correction (sorry, should learn to test properly first ;)):

defaultA will be skipped as host is not in the group.  
hostA will be skipped as host doesn’t match command-line (required to run playbook).  
“all” will successfully single out the host.

---

<div class="post-metadata">

**Author:** ![Michael\_DeHaan2](https://avatars.discourse-cdn.com/v4/letter/m/da6949/32.png) [@Michael\_DeHaan2](https://forum.ansible.com/u/Michael_DeHaan2)\
**Post date:** [April 22, 2013, 5:20pm UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706/12 "2013-04-22T17:20:24Z")

</div>

Ok, so seems to be working then? Good deal!

Let me know if not.

---

<div class="post-metadata">

**Author:** ![PJ1](https://avatars.discourse-cdn.com/v4/letter/p/eb8c5e/32.png) [@PJ1](https://forum.ansible.com/u/PJ1)\
**Post date:** [April 23, 2013, 12:04am UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706/13 "2013-04-23T00:04:15Z")

</div>

Well… there’s two issues still as I see it.

It would be really nice to have some sort of explanation about how it is intended to work with hosts/groups in the manual, just as with above excellent description of variable precedence… especially useful for noobs like me. 🙂

After spending an hour thinking about this with a severe split-brain at 02:00, I actually came to the conclusion (correct me if I’m wrong) that using ansible-playbook with the -l option, i.e. limit hosts/groups, will actually break the functionality of any ‘hosts:’ containing hostnames (intended as “only run on this host/group”), since it will not match what you specified on the command line.

In my case, hostA is actually a playbook checking out a git repository on a specific host, which will be skipped if I use the limit option.

Perhaps something like a “required\_hosts:” or similar would solve it?

---

<div class="post-metadata">

**Author:** ![Michael\_DeHaan2](https://avatars.discourse-cdn.com/v4/letter/m/da6949/32.png) [@Michael\_DeHaan2](https://forum.ansible.com/u/Michael_DeHaan2)\
**Post date:** [April 23, 2013, 12:10am UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706/14 "2013-04-23T00:10:16Z")

</div>

Your conclusion is not correct.

What --limit says is “of the hosts that are selected in the hosts definition, additionally confine them to these additional patterns”.

It does not matter if groups or hostnames are used.

Skipping it if not in the group seems exactly like what it should do.

---

<div class="post-metadata">

**Author:** ![PJ1](https://avatars.discourse-cdn.com/v4/letter/p/eb8c5e/32.png) [@PJ1](https://forum.ansible.com/u/PJ1)\
**Post date:** [April 23, 2013, 12:27am UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706/15 "2013-04-23T00:27:16Z")

</div>

I see…  
Then how would I do if I wanted to run some playbook(s) against a specific host or hosts, without having to modify the playbook(s) every time?

---

<div class="post-metadata">

**Author:** ![Michael\_DeHaan2](https://avatars.discourse-cdn.com/v4/letter/m/da6949/32.png) [@Michael\_DeHaan2](https://forum.ansible.com/u/Michael_DeHaan2)\
**Post date:** [April 23, 2013, 3:10am UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706/16 "2013-04-23T03:10:49Z")

</div>

Two options:

hosts: all

–limit [foo.example.com](http://foo.example.com) (or any group or pattern, or multiple patterns colon seperated)

or:

hosts: $hosts

–extra-vars=“hosts=”

---

<div class="post-metadata">

**Author:** ![PJ1](https://avatars.discourse-cdn.com/v4/letter/p/eb8c5e/32.png) [@PJ1](https://forum.ansible.com/u/PJ1)\
**Post date:** [April 23, 2013, 6:42am UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706/17 "2013-04-23T06:42:05Z")

</div>

Too much trees to see the forest. duh!

Thanks… sorry for hijacking the thread. 🙂

---

<div class="post-metadata">

**Author:** ![PJ1](https://avatars.discourse-cdn.com/v4/letter/p/eb8c5e/32.png) [@PJ1](https://forum.ansible.com/u/PJ1)\
**Post date:** [April 23, 2013, 6:56am UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706/18 "2013-04-23T06:56:19Z")

</div>

Just as a side note, using limit will not work as it will still skip any playbooks where hosts: has to contain a hostname.

---

<div class="post-metadata">

**Author:** ![Michael\_DeHaan2](https://avatars.discourse-cdn.com/v4/letter/m/da6949/32.png) [@Michael\_DeHaan2](https://forum.ansible.com/u/Michael_DeHaan2)\
**Post date:** [April 23, 2013, 12:23pm UTC](https://forum.ansible.com/t/understanding-variable-precedence/12706/19 "2013-04-23T12:23:23Z")

</div>

That’s not true, please stop by IRC if you would like to discuss/troubleshoot.
