Ansible Role Dependencies
Last chapter deliberately left one question open: why did applying the same role twice with different parameters run it fully both times, rather than being deduplicated? This chapter answers that, alongside meta/main.yml’s other real purpose — declaring that one role depends on another.
Declaring A Dependency In meta/main.yml
dependencies:
- role: commonThis means: whenever myapp is applied, common is automatically applied first, before myapp’s own tasks/main.yml even starts. Nobody using myapp needs to remember to also list common themselves — the dependency is baked into the role.
Execution Order: Dependencies Run First
A dependency’s entire task list completes before the depending role’s own tasks begin — and this is recursive. If common itself declared a dependency on some even more foundational role, that would run before common, which runs before myapp.
Initialize common role (with ansible-galaxy role init), place tasks and verify dependencies run first. A sample output for the given playbook:
- name: Deploy Two Different Apps With The Same Role
hosts: ubuntu
roles:
- myappTASK [common : Common Role] ***************************
ok: [ubuntu] => {
"msg": "I am common"
}
TASK [myapp : Display Role Name] ***************************
ok: [ubuntu] => {
"msg": "Role Name = myapp"
}The Deduplication Rule
By default, if the same role — with the same parameters — would be applied more than once within a single play, Ansible only actually runs it once. This applies whether the repetition comes from listing it twice directly, or from multiple different roles all declaring it as a dependency. This is exactly the same philosophy as the handler notification deduplication rule from the blocks and handlers section: don’t repeat identical work just because more than one thing asked for it.
dependencies:
- role: commondependencies:
- role: common- name: Full Setup
hosts: ubuntu
roles:
- myapp
- monitoringcommon runs exactly once — pulled in as myapp’s dependency, before myapp’s own tasks — even though monitoring also declares a dependency on it. By the time execution reaches monitoring, common has already been applied in this play, so it isn’t run a second time.
If you are following everything carefully about roles, you should be able to reproduce what the above paragraph is saying. I am only providing sample output:
PLAY [Deploy Two Different Apps With The Same Role] ****************************
TASK [Gathering Facts] *********************************************************
ok: [ubuntu]
TASK [common : Display Role Name] **********************************************
ok: [ubuntu] => {
"msg": "Role Name = common"
}
TASK [myapp : Display Role Name] ***********************************************
ok: [ubuntu] => {
"msg": "Role Name = myapp"
}
TASK [monitoring : Display Role Name] ******************************************
ok: [ubuntu] => {
"msg": "Role Name = monitoring"
}
PLAY RECAP *********************************************************************
ubuntu : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 You can see that common role despite being dependency of both myapp and monitoring only run once which is what we have been talking till some time.
Resolving The Previous Chapter’s Example
Here’s the actual rule behind that earlier “same role, two different apps” example running twice: deduplication only kicks in when a role would be applied with identical parameters. Since app_name and app_port genuinely differed between the two roles: entries in that example, Ansible correctly treated them as two distinct applications of the role, not duplicates — and ran both fully. Deduplication is specifically about avoiding identical, redundant work, not about limiting how many times a role can genuinely be used with different data.
Forcing A Role To Run Again: allow_duplicates
A role can opt out of deduplication entirely:
allow_duplicates: trueWith this set, the role runs every time it’s listed or depended on, even with identical parameters. Worth knowing this exists, but it’s a narrow need — appropriate only for a role specifically designed to be safely re-run repeatedly, not a general-purpose override.
With duplicates allowed, compare this output with the earlier one:
PLAY [Deploy Two Different Apps With The Same Role] ****************************
TASK [Gathering Facts] *********************************************************
ok: [ubuntu]
TASK [common : Display Role Name] **********************************************
ok: [ubuntu] => {
"msg": "Role Name = common"
}
TASK [myapp : Display Role Name] ***********************************************
ok: [ubuntu] => {
"msg": "Role Name = myapp"
}
TASK [common : Display Role Name] **********************************************
ok: [ubuntu] => {
"msg": "Role Name = common"
}
TASK [monitoring : Display Role Name] ******************************************
ok: [ubuntu] => {
"msg": "Role Name = monitoring"
}
PLAY RECAP *********************************************************************
ubuntu : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 Now common role is run twice — it runs exactly the same number of times any dependent role triggers it.
A Realistic Example: A Common Role As A Shared Dependency
The genuine real-world payoff of dependencies — universal, base-level setup that every other role can rely on being applied, exactly once, without anyone needing to remember to include it manually:
- name: Ensure base packages are installed
ansible.builtin.package:
name:
- curl
- vim
state: presentdependencies:
- role: commondependencies:
- role: commonAny role depending on common now gets consistent base setup automatically, exactly once per play, regardless of how many separate roles in that play happen to need it unless allow_duplicates is set true for common roles — which makes no sense here.
Best Practices
- Declare genuine prerequisites as dependencies in
meta/main.yml, rather than requiring anyone using your role to remember to list them manually. - Trust the automatic deduplication for shared dependencies — a common dependency declared by several roles in the same play isn’t wastefully repeated.
- Use
allow_duplicates: trueonly for roles specifically designed to be safely repeatable, not as a general workaround. - Remember deduplication requires identical parameters — a role applied more than once with genuinely different data runs fully each time, exactly as it should.