Ansible import_tasks vs include_tasks: Key Differences (2026 Guide)
By Luca Berton · Published 2024-01-01 · Category: installation
Complete guide to Ansible import_tasks vs include_tasks. Understand static vs dynamic task loading, when to use each, performance differences, and migration tips with practical examples.
Ansible provides two ways to load external task files: import_tasks (static) and include_tasks (dynamic). They look similar but behave very differently at parse time vs runtime. Choosing the wrong one causes subtle bugs with tags, handlers, and conditionals.
Quick Comparison
Key Differences
| Feature | import_tasks (static) | include_tasks (dynamic) | |---------|------------------------|--------------------------| | When loaded | Playbook parse time | Runtime (when task is reached) | | Tags | Applied to all imported tasks | Only on the include task itself | | when condition | Applied to each imported task | Evaluated once before loading | | Handlers | Can notify imported handlers | Cannot notify included handlers | | Loops | ❌ Cannot use with loop | ✅ Can use with loop | | Variables | Cannot use task-level vars in filename | Can use runtime variables in filename | | --list-tasks | Shows imported tasks | Shows only the include task | | --list-tags | Shows tags from imports | Does not show include tags | | Error detection | Syntax errors caught early | Syntax errors caught at runtime |
import_tasks (Static)
Tasks are inserted into the play at parse time, as if you copied them directly into the playbook.
Basic Usage
Tags with import_tasks
Tags cascade to ALL imported tasks:
when with import_tasks
The when condition is applied to each imported task individually:
❌ Cannot Use Loops
include_tasks (Dynamic)
Tasks are loaded at runtime when the include task is reached.
Basic Usage
Dynamic Filenames
Loops with include_tasks
Tags with include_tasks
Tags apply only to the include task itself, NOT to the tasks inside:
To make tags work with includes, add tags inside the included file:
Or use apply:
When to Use Each
Use import_tasks When: • You need tags to work across the included file • You need handlers defined in the imported file • The filename is static (known at parse time) • You want --list-tasks and --list-tags to show everything • You want early error detection (syntax errors caught before execution)
Use include_tasks When: • The filename is dynamic (uses variables or facts) • You need to run includes in a loop • You want conditional loading (load different files based on conditions) • The included file might not exist (use ignore_errors) • You want tasks to load only when needed (performance)
Roles: import_role vs include_role
The same static/dynamic distinction applies to roles:
import_playbook
For including entire playbooks (only works at the play level, not inside tasks):
> Note: There is no include_playbook. Playbook inclusion is always static.
Common Patterns
OS-Specific Task Loading
Role Structure
Common Mistakes
Tags Not Working with include_tasks
Variable Filename with import_tasks
FAQ
What is the difference between import_tasks and include_tasks?
import_tasks is static — tasks are loaded at playbook parse time and behave as if written directly in the playbook. include_tasks is dynamic — tasks are loaded at runtime when the task is reached. This affects how tags, conditions, and loops work.
Can I use loops with import_tasks?
No, import_tasks does not support loops. Use include_tasks when you need to loop over a task file multiple times.
Why don't my tags work with include_tasks?
Tags on include_tasks only apply to the include task itself, not to the tasks inside the file. Use import_tasks for tag-based filtering, or add apply: with tags to pass them through to included tasks.
Which is better for performance?
include_tasks can be slightly better for large playbooks because tasks are only loaded when needed. import_tasks loads everything upfront. In practice, the difference is negligible for most playbooks.
Should I use import_tasks or include_tasks by default?
Use import_tasks as the default for static, predictable task loading. Switch to include_tasks when you need dynamic filenames, loops, or conditional file loading. If you use tags extensively, prefer import_tasks.
Conclusion • import_tasks — Static, parsed at load time. Tags and when cascade. No loops. Best for predictable structure. • include_tasks — Dynamic, loaded at runtime. Supports loops and variable filenames. Tags need special handling. • Rule of thumb: Use import_tasks unless you need dynamic behavior.
Related Articles • Ansible Roles: Create Reusable Automation • Ansible Handlers: Trigger Actions on Change • Ansible Playbook Examples: Complete Guide • Ansible Collections: Install, Use & Create
Category: installation