Ansible modules are the building blocks of automation—each module performs one kind of work on a managed host. Collections package modules (and plugins) under a namespace; ansible-doc shows their options on the control node before you write a playbook or ad hoc command.
This guide covers builtin modules, FQCN naming, common module families, ansible-doc, collection install paths, requirements.yml, and module discovery. I ran commands on rocky1 from the EX294 lab with ansible-core 2.16. Read install Ansible and project layout first.
Tested on: Rocky Linux 10.2 (Red Quartz); kernel 6.12.0-211.16.1.el10_2.0.1.x86_64; ansible-core 2.16.16.
~/ansible-project, inventory group lab, and playbooks in playbooks/. Use your own host names and paths if yours differ.
What are Ansible Modules?
A module is a unit of code Ansible runs on a managed host (or locally for some connection: local tasks). You invoke modules from:
- Ad hoc commands:
ansible lab -m ansible.builtin.ping - Playbook tasks:
- name: Ensure httpd/ansible.builtin.dnf: ...
Each module accepts arguments, performs work, and returns JSON—changed, msg, and module-specific keys. Ansible ships dozens of modules in ansible.builtin; extra modules arrive in collections such as ansible.posix or community.general. When you must run a plain command instead of a state module, see command vs shell vs raw for which executor to pick.
How Ansible Runs a Module
For a normal Linux target, Ansible follows this simplified flow:
- The control node reads inventory, variables, and task arguments.
- Ansible opens the configured connection, usually SSH.
- Module code and arguments are prepared for the target.
- The module runs on the managed node, usually through Python.
- The module returns JSON to the control node.
- Ansible prints
ok,changed,failed, orunreachable.
Managed nodes need SSH access and Python for most Linux modules—they do not need ansible-core or installed collections. Collections live on the control node; module code is sent or invoked during the task.
Ansible Modules vs Plugins vs Collections
| Piece | Runs where | Purpose | Example |
|---|---|---|---|
| Module | Usually on the managed host; some run locally or use action plugins | One task action | ansible.builtin.copy, ansible.builtin.dnf |
| Plugin | Control node | Extend Ansible internals | inventory plugins, connection plugins, callbacks |
| Collection | Installed on control node | Bundle modules, plugins, roles, docs | ansible.posix, community.general |
For normal Linux automation, Ansible usually transfers module code to the managed node and executes it there. Some modules have controller-side action plugins or interact with APIs instead of running entirely on the target.
Modules answer “what should this host do?” Plugins answer “how does Ansible connect, parse inventory, or format output?” Collections are how Red Hat and the community ship and version those pieces separately from ansible-core.
Builtin Modules and ansible.builtin
ansible-core includes the ansible.builtin collection—no extra install for modules such as:
ping,setup— connectivity and factscommand,shell— run programscopy,file,template— files and templatesdnf,package,service— packages and servicesuser,group— local accounts
On Rocky Linux 10, dnf install ansible-core provides the engine and builtin modules. Extra collections come from EPEL RPMs or ansible-galaxy collection install (install guide).
Short Module Names vs FQCN
What is FQCN in Ansible?
FQCN (Fully Qualified Collection Name) identifies a module as namespace.collection.module:
ansible.builtin.copy
│ │ └── module name
│ └── collection name
└── namespace (Ansible-maintained for builtin)Another example: ansible.posix.firewalld — the firewalld module from the ansible.posix collection.
Why FQCN is recommended
- Removes ambiguity when collections contain similar module names
- Makes documentation lookup easier:
ansible-doc ansible.builtin.copy - Works better with linters and production playbooks
- Avoids surprises from short-name collection search order
Ansible 2.10+ playbook style recommends FQCN so the intended module is selected explicitly. Use FQCN in playbooks and exams: ansible.builtin.dnf, not bare dnf.
When short module names are still commonly used
Short names (ping, copy, dnf) still resolve when the module is unique in configured paths. They appear in older docs and quick ad hoc examples:
cd ~/ansible-project
ansible lab -m pingSample output:
rocky2 | SUCCESS => {
"changed": false,
"ping": "pong"
}The FQCN form is equivalent when ansible.builtin is on the path:
ansible lab -m ansible.builtin.pingPrefer FQCN in new playbooks; use short names only when you know the module source.
FQCN vs collections Keyword
Playbooks can use the collections: keyword to shorten module names:
collections:
- ansible.posixThen a task may use firewalld instead of ansible.posix.firewalld. For beginner labs and production readability, prefer FQCN anyway:
- name: Allow http through firewalld
ansible.posix.firewalld:
service: http
state: enabled
permanent: trueFQCN makes the module source visible in every task and avoids relying on collection search order.
Common Ansible Module Categories
| Category | Example modules (FQCN) | Typical use |
|---|---|---|
| Connection and test | ansible.builtin.ping, ansible.builtin.setup |
SSH/Python check, gather facts |
| Command and shell | ansible.builtin.command, ansible.builtin.shell |
Run programs |
| Package | ansible.builtin.dnf, ansible.builtin.package, ansible.builtin.apt |
Install or remove packages |
| Service | ansible.builtin.service, ansible.builtin.systemd_service |
Start, stop, enable units |
| File and copy | ansible.builtin.copy, ansible.builtin.file, ansible.builtin.fetch |
Push files, directories, permissions |
| User and group | ansible.builtin.user, ansible.builtin.group |
Local accounts |
| Template and line editing | ansible.builtin.template, ansible.builtin.lineinfile, ansible.builtin.blockinfile |
Templated configs, one-line edits |
Collection modules extend the table—for example ansible.posix.firewalld for firewalld rules when that collection is installed.
How to Find Module Documentation with ansible-doc
ansible-doc reads module documentation installed on the control node. Run it on rocky1 before guessing -a arguments.
List available modules
ansible-doc -lSample output:
ansible.builtin.add_host Add a host (and alternatively a grou...
ansible.builtin.apt Manages apt-packages
ansible.builtin.apt_key Add or remove an apt key
ansible.builtin.copy Copy files to remote locationsFilter the list:
ansible-doc -l | grep -i firewalldSample output:
ansible.posix.firewalld Manage arbitrary ports/services with...
ansible.posix.firewalld_info Gather information about firewalldView documentation for one module
ansible-doc ansible.builtin.copySample output:
> ANSIBLE.BUILTIN.COPY (.../ansible/modules/copy.py)
The [ansible.builtin.copy] module copies a file or a directory
structure from the local or remote machine to a location on
the remote machine.Check module options
The synopsis (-s) shows parameters in playbook shape:
ansible-doc -s ansible.builtin.fileSample output:
- name: Manage files and file properties
file:
path: # Path to the file being managed.
state: # absent | directory | file | hard | link | touch
mode: # Permissions the resulting filesystem object should have.Check examples
Scroll the full doc for an EXAMPLES: section, or extract it:
ansible-doc ansible.builtin.dnf | sed -n '/^EXAMPLES:/,/^RETURN VALUES:/p' | head -15Sample output:
EXAMPLES:
- name: Install the latest version of Apache
ansible.builtin.dnf:
name: httpd
state: latest
- name: Install the latest version of Apache and MariaDB
ansible.builtin.dnf:
name:
- httpd
- mariadb-server
state: latestCheck return values
Return values appear under RETURN VALUES: at the end of the same doc:
ansible-doc ansible.builtin.copy | sed -n '/^RETURN VALUES:/,/^$/p' | head -12Sample output:
RETURN VALUES:
- backup_file
Name of backup file created.
returned: changed and if backup=yes
sample: /path/to/file.txt.2015-02-12@22:09~
type: strUse return values when you register a task and need to know which keys to expect in later tasks.
View plugin documentation
By default, ansible-doc looks for modules. Use -t for other plugin types:
ansible-doc -t inventory ansible.builtin.iniSample output:
> ANSIBLE.BUILTIN.INI (.../ansible/plugins/inventory/ini.py)
INI file based inventory, sections are groups or group related
with special `:modifiers'.ansible-doc -t connection ansible.builtin.sshSample output:
> ANSIBLE.BUILTIN.SSH (.../ansible/plugins/connection/ssh.py)
This connection plugin allows Ansible to communicate to the
target machines through normal SSH command line.Connection and lookup plugins use the same -t pattern—for example ansible-doc -t lookup ansible.builtin.file.
Understanding Module Options, State and Return Values
Most modules use key=value arguments:
name— package name, service name, file path, or username (module-specific)state— desired end state:present,absent,started,stopped,directory, and so onmode,owner,group— permissions on file modules
Example task shape:
- name: Install tmux
ansible.builtin.dnf:
name: tmux
state: presentAfter the module runs, Ansible shows JSON on the CLI or in playbook output. Common keys:
| Key | Meaning |
|---|---|
changed |
true if the module altered the system |
failed |
Task error (playbook recap) |
msg |
Human-readable message on failure |
| Module-specific | e.g. checksum from copy, results from dnf |
Idempotent modules set changed: false when the system already matches state.
Check Module Attributes: Check Mode, Diff Mode, and Idempotency
Not every module supports every Ansible feature. In ansible-doc, check the ATTRIBUTES: section for:
| Attribute | Meaning |
|---|---|
| Check mode | Whether the module can predict changes with --check |
| Diff mode | Whether the module can show before/after differences with --diff |
| Idempotency | Whether the module can detect current state before changing it |
| Platform | Which operating systems or targets the module supports |
ansible-doc ansible.builtin.copySample output:
ATTRIBUTES:
check_mode:
description: Can run in check_mode and return changed status prediction without
modifying target
support: full
diff_mode:
description: Will return details on what has changed (or possibly needs changing
in check_mode), when in diff mode
support: fullModules such as copy, file, dnf, and service are usually better for repeatable automation than raw command or shell because they understand desired state.
What are Ansible Collections?
A collection is a distributable package of modules, plugins, roles, and documentation under a namespace. ansible-core ships ansible.builtin; vendors and community publish others (ansible.posix, community.general, amazon.aws, …). For RHEL System Roles and production collection workflows, see collections and RHEL System Roles.
Collections version independently from ansible-core. Pin versions in requirements.yml so teammates and CI install the same content.
On Rocky Linux 10, EPEL provides RPMs such as ansible-collection-ansible-posix—see install Ansible. ansible-galaxy collection install is the portable alternative when you use PyPI ansible-core or need a collection not packaged as an RPM.
Install Ansible Collections with ansible-galaxy
Install collections on the control node as the ansible user (or root for system-wide paths).
Install one collection
ansible-galaxy collection install ansible.posixWhen the collection is already present:
Starting galaxy collection install process
Nothing to do. All requested collections are already installed. If you want to reinstall them, consider using `--force`.On this lab, ansible.posix came from the EPEL RPM ansible-collection-ansible-posix.
Install collections into a project directory
Install beside a project so the tree is self-contained:
ansible-galaxy collection install ansible.posix \
-p ~/ansible-project/collectionsWhen you install with -p ./collections, Ansible expects the collection under collections/ansible_collections/NAMESPACE/NAME/. This layout is commonly used beside a playbook or project root. If Ansible warns that the path is not in collections_path, add the project collection path in ansible.cfg:
[defaults]
collections_path = ./collections:/home/ansible/.ansible/collections:/usr/share/ansible/collectionsList installed collections
ansible-galaxy collection listSample output:
# /usr/share/ansible/collections/ansible_collections
Collection Version
------------- -------
ansible.posix 2.1.0Remove or update collections
- Reinstall:
ansible-galaxy collection install NAMESPACE.NAME --force - Remove: delete the collection directory under
ansible_collections/in the relevantcollections_path, or remove the EPEL RPM on Rocky Linux - Upgrade: install with a newer version constraint or update the RPM
Use requirements.yml for Collections
Pin collections in requirements.yml at the project root (project layout). Treat this file as the contract for your control node and CI: commit it to Git, install from it before every pipeline run, and bump versions deliberately after you test—not on every fresh clone.
Why pin collection versions in production
Collections version independently from ansible-core. A playbook that works today can fail tomorrow when ansible-galaxy collection install without a pin pulls a newer release with renamed options or stricter dependencies. For labs, CI, and production:
- Pin exact versions (
version: "2.1.0") for reproducible installs - Run
ansible-galaxy collection install -r requirements.ymlon every control node and in CI beforeansible-playbook - Keep collection RPM pins on Rocky Linux aligned with the same
requirements.ymlwhen you mix EPEL and Galaxy installs
Floating installs are fine for quick experiments; pinned requirements.yml is the best practice for anything you rerun or share.
Basic requirements.yml structure
---
collections:
- name: ansible.posix
version: "2.1.0"
- name: community.general
version: ">=8.0.0"Install collections from requirements.yml
ansible-galaxy collection install -r requirements.yml \
-p ~/ansible-project/collectionsIf collections are already satisfied, output matches a single-collection install. When the target path is outside collections_path, Ansible warns—add collections_path under [defaults] in ansible.cfg as shown above.
Pin collection versions
Use exact versions for reproducible labs and CI:
collections:
- name: ansible.posix
version: "2.1.0"Use ranges only when you intentionally accept upgrades: version: ">=2.0.0,<3.0.0".
Module Discovery and Collection Paths
Ansible searches for modules in:
ansible.builtin(withansible-core)- Paths in
COLLECTIONS_PATHS/collections_pathinansible.cfg ~/.ansible/collectionsand/usr/share/ansible/collectionsby default
Check effective settings:
ansible-config dump | grep -i collectionSample output:
COLLECTIONS_PATHS(default) = ['/home/ansible/.ansible/collections', '/usr/share/ansible/collections']
COLLECTIONS_ON_ANSIBLE_VERSION_MISMATCH(default) = warningModule names in tasks must resolve to an installed collection on the control node. Managed nodes only need Python and whatever the module executes—they do not run ansible-galaxy.
Troubleshoot Module Not Found Errors
Wrong or missing module:
cd ~/ansible-project
ansible lab -m nonexistent.moduleSample output:
rocky2 | FAILED! => {
"msg": "The module nonexistent.module was not found in configured module paths"
}| Symptom | Likely cause | Fix |
|---|---|---|
module was not found in configured module paths |
Typo, missing collection, or wrong FQCN | ansible-doc -l | grep KEYWORD; install collection |
| Short name fails but FQCN works | Search order or deprecated redirect | Use FQCN in playbooks |
| Collection installed but unused | Path not in collections_path |
Add path to ansible.cfg or use -p beside project |
ansible.posix.* missing |
Collection not installed | EPEL RPM or ansible-galaxy collection install ansible.posix |
| Works on one host, not another | Different control node paths | Same requirements.yml and ansible.cfg on every controller |
Works as root, fails as ansible user |
Collection installed under root's home | Install as the same user that runs Ansible or use system path |
requirements.yml installed but module missing |
Installed into a path outside collections_path |
Check ansible-config dump | grep -i collection |
Discovery checklist:
ansible-galaxy collection listansible-doc -l | grep -i MODULE_KEYWORDBest Practices for Using Modules and Collections
- Use FQCN in playbooks and roles (
ansible.builtin.copy) - Run
ansible-docbefore new modules—options, examples, and return values - Pin collections in
requirements.ymland commit it with the project - Install collections on the control node only; keep versions aligned across teammates
- Prefer idempotent modules (
dnf,file,service) over rawshellwhen a module exists - On Rocky labs, know both EPEL RPM and
ansible-galaxyinstall paths - After adding collections, run
ansible-doc NAMESPACE.collection.moduleto confirm the install
What Not to Cover in This Article
This page stops at modules, ansible-doc, and collections. Deeper topics belong in other guides:
- Writing custom modules or plugins
- Full Ansible Vault workflow
- Role variable precedence inside roles
- Dynamic inventory plugins
- Execution environment / navigator internals (ansible-navigator.yml)
- Full playbook tutorial
What comes next
- Ansible ad hoc commands — run modules from the CLI
- Ansible playbook examples — tasks and modules in YAML
- group_vars, host_vars and patterns — variables and targeting
References
- Using Ansible modules
- ansible-doc command
- Ansible collections overview
- ansible-galaxy collection install
Summary
Ansible modules perform task work—usually on managed hosts after SSH and Python are in place. ansible.builtin ships with ansible-core; other modules live in collections installed on the control node. Use FQCN names (ansible.builtin.dnf), read ATTRIBUTES and options with ansible-doc, and install collections via EPEL RPMs or ansible-galaxy collection install plus requirements.yml. If Ansible cannot find a module, verify the FQCN, run ansible-galaxy collection list, and confirm collections_path includes your install directory.

