{"id":51391,"date":"2025-08-07T09:03:53","date_gmt":"2025-08-07T09:03:53","guid":{"rendered":"https:\/\/www.devopsschool.com\/blog\/?p=51391"},"modified":"2025-08-07T09:03:53","modified_gmt":"2025-08-07T09:03:53","slug":"ansible-step-by-step-tutorial-on-ansible-collections","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/ansible-step-by-step-tutorial-on-ansible-collections\/","title":{"rendered":"Ansible: step-by-step tutorial on Ansible Collections"},"content":{"rendered":"\n<p>Here\u2019s a <strong>very detailed, step-by-step tutorial on Ansible Collections<\/strong>\u2014covering what they are, why you need them, how to use them, how to create your own, and key best practices and facts. This is suitable for beginners and intermediate Ansible users alike.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\"><strong>1. What is an Ansible Collection?<\/strong><\/h1>\n\n\n\n<p><strong>Ansible Collections<\/strong> are a standardized way to package, distribute, and reuse <strong>Ansible content<\/strong> (modules, roles, plugins, documentation, playbooks, etc.).<br>Introduced in Ansible 2.9+, collections are now the foundation of all Ansible automation.<\/p>\n\n\n\n<p><strong>A collection is:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A folder structure with a clear, predictable layout<\/li>\n\n\n\n<li>Can contain:\n<ul class=\"wp-block-list\">\n<li><strong>Modules<\/strong> (custom tasks)<\/li>\n\n\n\n<li><strong>Roles<\/strong><\/li>\n\n\n\n<li><strong>Plugins<\/strong> (connection, lookup, filter, etc.)<\/li>\n\n\n\n<li><strong>Playbooks<\/strong> and tasks<\/li>\n\n\n\n<li><strong>Documentation<\/strong> and tests<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p><strong>Think:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Collections = \u201cmini-libraries\u201d or \u201capps\u201d of Ansible content<\/li>\n\n\n\n<li>Similar to Python packages or Node.js modules, but for Ansible<\/li>\n<\/ul>\n\n\n\n<p><strong>Example<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>ansible.builtin<\/code> (core modules shipped with Ansible)<\/li>\n\n\n\n<li><code>community.general<\/code> (community-contributed modules and roles)<\/li>\n\n\n\n<li><code>amazon.aws<\/code> (all AWS cloud content)<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\"><strong>2. Why Do We Need Ansible Collections?<\/strong><\/h1>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Before collections:<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Roles, modules, plugins, and playbooks were scattered and hard to version<\/li>\n\n\n\n<li>Everything was bundled in the \u201cmonolithic\u201d Ansible project<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>With collections:<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Modularity:<\/strong> Develop and release modules\/roles\/plugins independently of Ansible core<\/li>\n\n\n\n<li><strong>Versioning:<\/strong> Each collection has its own version and changelog<\/li>\n\n\n\n<li><strong>Distribution:<\/strong> Publish to Ansible Galaxy, Automation Hub, or internal\/private registries<\/li>\n\n\n\n<li><strong>Reusability:<\/strong> Share automation content across teams and projects easily<\/li>\n\n\n\n<li><strong>Isolation:<\/strong> Avoid conflicts by scoping dependencies per collection<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\"><strong>3. How to Use Ansible Collections<\/strong><\/h1>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>A. Installing a Collection<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>From <strong>Ansible Galaxy<\/strong> (default public repo): <code>ansible-galaxy collection install community.general<\/code><\/li>\n\n\n\n<li>From <strong>Automation Hub<\/strong> (enterprise): <code>ansible-galaxy collection install my_org.my_collection --server https:\/\/my_ah_url\/<\/code><\/li>\n\n\n\n<li>From a <strong>requirements.yml<\/strong> file (recommended for projects): <code>--- collections: - name: community.general version: \">=6.0.0\" - name: amazon.aws version: \">=5.0.0\"<\/code> <code>ansible-galaxy collection install -r requirements.yml<\/code><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>B. Using Content from a Collection<\/strong><\/h2>\n\n\n\n<p><strong>Syntax for modules:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>namespace.collection.module_name<\/code><\/li>\n\n\n\n<li>Example: <code>community.general.ini_file<\/code><\/li>\n<\/ul>\n\n\n\n<p><strong>Playbook example:<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">- name: Use a <span class=\"hljs-built_in\">module<\/span> <span class=\"hljs-keyword\">from<\/span> a collection\n  <span class=\"hljs-attr\">hosts<\/span>: localhost\n  <span class=\"hljs-attr\">tasks<\/span>:\n    - name: Use the ini_file <span class=\"hljs-built_in\">module<\/span> <span class=\"hljs-keyword\">from<\/span> community.general\n      community.general.ini_file:\n        path: <span class=\"hljs-regexp\">\/tmp\/<\/span>example.ini\n        <span class=\"hljs-attr\">section<\/span>: <span class=\"hljs-string\">\"defaults\"<\/span>\n        <span class=\"hljs-attr\">option<\/span>: <span class=\"hljs-string\">\"enabled\"<\/span>\n        <span class=\"hljs-attr\">value<\/span>: <span class=\"hljs-string\">\"yes\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><strong>Shortcuts:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>If a collection is installed, and the FQCN (<code>namespace.collection.module<\/code>) is used, Ansible finds it automatically.<\/li>\n\n\n\n<li>For roles: <code>roles: - amazon.aws.ec2_instance<\/code><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>C. Listing Installed Collections<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">ansible-galaxy collection <span class=\"hljs-keyword\">list<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>D. Where are Collections Installed?<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Default: <code>~\/.ansible\/collections\/<\/code> (user), or <code>\/usr\/share\/ansible\/collections\/<\/code> (system)<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\"><strong>4. How to Create Ansible Collections<\/strong><\/h1>\n\n\n\n<p>Let\u2019s make a new collection from scratch!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>A. Prerequisites<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ansible installed (<code>pip install ansible<\/code>)<\/li>\n\n\n\n<li><code>ansible-galaxy<\/code> CLI available (comes with Ansible)<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>B. Create a Collection Skeleton<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">ansible-galaxy<\/span> <span class=\"hljs-selector-tag\">collection<\/span> <span class=\"hljs-selector-tag\">init<\/span> <span class=\"hljs-selector-tag\">my_namespace<\/span><span class=\"hljs-selector-class\">.my_collection<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><strong>This creates:<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">my_namespace\/my_collection\/\n  plugins\/\n    modules\/\n    lookup\/\n    filter\/\n  roles\/\n  playbooks\/\n  docs\/\n  tests\/\n  README.md\n  galaxy.yml\n<\/code><\/span><\/pre>\n\n\n<ul class=\"wp-block-list\">\n<li><code>my_namespace<\/code> is your organization or username (required)<\/li>\n\n\n\n<li><code>my_collection<\/code> is your collection name<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>C. Add Content<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Add a Role:<\/strong> <code>cd my_namespace\/my_collection\/roles ansible-galaxy role init myrole<\/code><\/li>\n\n\n\n<li><strong>Add a Module:<\/strong><br>Place your custom module script in <code>plugins\/modules\/<\/code> (write in Python).<\/li>\n\n\n\n<li><strong>Add Plugins\/Docs\/Tests:<\/strong><br>Use the standard subfolders.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>D. Update Metadata (<code>galaxy.yml<\/code>)<\/strong><\/h2>\n\n\n\n<p>Set:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>namespace<\/code><\/li>\n\n\n\n<li><code>name<\/code><\/li>\n\n\n\n<li><code>version<\/code><\/li>\n\n\n\n<li><code>authors<\/code><\/li>\n\n\n\n<li><code>description<\/code><\/li>\n\n\n\n<li><code>dependencies<\/code> (optional: specify other collections)<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>E. Lint and Test Your Collection<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">ansible-test sanity\nansible-lint roles\/myrole\/\n<\/code><\/span><\/pre>\n\n\n<h2 class=\"wp-block-heading\"><strong>F. Build Your Collection<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">ansible-galaxy collection build\n<\/code><\/span><\/pre>\n\n\n<p>This outputs a <code>.tar.gz<\/code> package.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>G. Publish Your Collection<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>To Galaxy:<\/strong> <code>ansible-galaxy collection publish my_namespace-my_collection-1.0.0.tar.gz<\/code><\/li>\n\n\n\n<li><strong>To Automation Hub:<\/strong><br>Upload via web UI or API.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>H. Use Your Collection<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Install it as shown above.<\/li>\n\n\n\n<li>Use its modules\/roles with their FQCN in your playbooks.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\"><strong>5. Other Advanced\/Missing Info<\/strong><\/h1>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>A. Dependencies<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Collections can depend on other collections (declare in <code>galaxy.yml<\/code>).<\/li>\n\n\n\n<li>Example: <code>dependencies: community.general: \">=6.0.0\"<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>B. Versioning<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Collections use semantic versioning.<\/li>\n\n\n\n<li>Specify versions when installing to ensure reproducibility.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>C. Private\/Internal Collections<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use your own Galaxy server or Automation Hub to distribute proprietary content.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>D. Testing<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use <code>ansible-test<\/code> for units, integration, sanity checks.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>E. Collection Structure<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/docs.ansible.com\/ansible\/latest\/dev_guide\/collections_galaxy_meta.html#collection-structure\" target=\"_blank\" rel=\"noopener\">Collection structure documentation<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>F. Importing in Playbooks<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Always use the <strong>Fully Qualified Collection Name (FQCN)<\/strong>:<br><code>namespace.collection.module<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>G. Updating Collections<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>To upgrade: <code>ansible-galaxy collection install community.general --upgrade<\/code><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\"><strong>6. Summary Table<\/strong><\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Action<\/th><th>Command\/Syntax<\/th><\/tr><\/thead><tbody><tr><td>Initialize a collection<\/td><td><code>ansible-galaxy collection init ns.coll<\/code><\/td><\/tr><tr><td>Build a collection<\/td><td><code>ansible-galaxy collection build<\/code><\/td><\/tr><tr><td>Install a collection<\/td><td><code>ansible-galaxy collection install ns.coll<\/code><\/td><\/tr><tr><td>Install from requirements.yml<\/td><td><code>ansible-galaxy collection install -r requirements.yml<\/code><\/td><\/tr><tr><td>List installed collections<\/td><td><code>ansible-galaxy collection list<\/code><\/td><\/tr><tr><td>Use collection module<\/td><td><code>namespace.collection.module_name:<\/code> in playbook<\/td><\/tr><tr><td>Publish to Galaxy<\/td><td><code>ansible-galaxy collection publish myns-mycol-1.0.0.tar.gz<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\"><strong>7. Best Practices &amp; Tips<\/strong><\/h1>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Always <strong>version<\/strong> your collections and keep a changelog.<\/li>\n\n\n\n<li>Use <strong>galaxy.yml<\/strong> to declare dependencies, authors, and metadata.<\/li>\n\n\n\n<li>Use <strong>roles<\/strong> for reusable sets of tasks; use <strong>modules\/plugins<\/strong> for custom logic.<\/li>\n\n\n\n<li>Test and lint your content before publishing.<\/li>\n\n\n\n<li>Use <strong>requirements.yml<\/strong> for consistent environments.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>References<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/docs.ansible.com\/ansible\/latest\/dev_guide\/collections_tech_preview.html\" target=\"_blank\" rel=\"noopener\">Ansible Collections User Guide (Official Docs)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.ansible.com\/ansible\/latest\/dev_guide\/collections_galaxy_meta.html#collection-structure\" target=\"_blank\" rel=\"noopener\">Collection structure<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/galaxy.ansible.com\/docs\/\" target=\"_blank\" rel=\"noopener\">Ansible Galaxy documentation<\/a><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here\u2019s a very detailed, step-by-step tutorial on Ansible Collections\u2014covering what they are, why you need them, how to use them, how to create your own, and key best practices and&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[2],"tags":[],"class_list":["post-51391","post","type-post","status-publish","format-standard","hentry","category-uncategorised"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/51391","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=51391"}],"version-history":[{"count":1,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/51391\/revisions"}],"predecessor-version":[{"id":51392,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/51391\/revisions\/51392"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=51391"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=51391"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=51391"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}