Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Ansible Roles and Galaxy

By Kokil Thapa | Last reviewed: September 2026

You inherit a Ubuntu server that needs PHP-FPM, Nginx, Redis, and a Laravel deploy user. Copy-pasting the same tasks into every playbook gets old fast. Ansible Roles and Galaxy solve that by packaging tasks, handlers, variables, and templates into reusable units you can version, test, and pull from a public or private registry. If you already run Ansible playbooks for server setup, roles are the next step toward maintainable infrastructure code.

What are Ansible Roles and Galaxy?

A role is a standard folder layout Ansible understands. Galaxy is the public hub where thousands of roles are published, searched, and downloaded. Think of a role as a mini-module for server configuration. A playbook declares what should run; the role holds how.

The default role skeleton looks like this:

roles/
  webserver/
    tasks/main.yml
    handlers/main.yml
    templates/
    files/
    vars/main.yml
    defaults/main.yml
    meta/main.yml
    README.md

tasks/main.yml holds the work. defaults/main.yml sets overridable values. vars/main.yml holds higher-priority variables. handlers/main.yml restarts services only when something changed. meta/main.yml declares Galaxy metadata and role dependencies.

Ansible Role Structureroles/webserver/tasks/main.ymlhandlers/main.ymltemplates/.j2 filesfiles/static assetsdefaults/overridable varsvars/fixed prioritymeta/dependenciesPlaybook calls role → Ansible loads tasks, vars, handlers in order
Standard Ansible Roles and Galaxy layout — every directory has a predictable purpose

Galaxy adds discovery and distribution. You search by tag, author, or platform. Popular roles cover Nginx, PostgreSQL, Docker, and hardening baselines. Official docs at Ansible role reuse documentation define the contract every role should follow.

On real client projects I often pair Ansible with Git-based deploy tools. Roles provision the box; Deployer or CI handles application releases. That split keeps infrastructure and app code in separate lifecycles. See automating server setup with Ansible for the playbook side.

How do you create an Ansible role from scratch?

Start with the built-in generator. It creates the skeleton so you never miss meta/main.yml or forget handler wiring.

Generate the skeleton

ansible-galaxy role init webserver --init-path roles/

This writes a compliant tree under roles/webserver/. Rename and extend from there.

Write tasks with idempotent modules

A PHP-FPM role might install packages, drop a pool config, and notify a handler:

# roles/php_fpm/tasks/main.yml
- name: Install PHP-FPM packages
  ansible.builtin.apt:
    name: "{{ php_packages }}"
    state: present
    update_cache: true
  when: ansible_os_family == "Debian"

- name: Deploy PHP-FPM pool template
  ansible.builtin.template:
    src: laravel-pool.conf.j2
    dest: "/etc/php/{{ php_version }}/fpm/pool.d/laravel.conf"
    owner: root
    group: root
    mode: "0644"
  notify: Reload php-fpm
# roles/php_fpm/handlers/main.yml
- name: Reload php-fpm
  ansible.builtin.service:
    name: "php{{ php_version }}-fpm"
    state: reloaded

Set defaults your team can override

# roles/php_fpm/defaults/main.yml
php_version: "8.3"
php_packages:
  - "php{{ php_version }}-fpm"
  - "php{{ php_version }}-mysql"
  - "php{{ php_version }}-redis"
  - "php{{ php_version }}-mbstring"
  - "php{{ php_version }}-xml"
  - "php{{ php_version }}-curl"

Host or group vars in your inventory override these without editing the role. That pattern scales when you manage staging and production from one repo.

Call the role from a playbook

# site.yml
- name: Configure Laravel application servers
  hosts: app_servers
  become: true
  roles:
    - common
    - php_fpm
    - nginx
    - redis

For Laravel stacks, a dedicated role per concern beats one 400-line playbook. I've used this approach when provisioning servers before a Linux system administration handoff to a client team.

How do you install and use roles from Ansible Galaxy?

Galaxy roles save hours on common software. You do not rewrite Nginx or firewall rules from scratch unless you have a strong reason.

Search and inspect before you install

ansible-galaxy search nginx --platforms Ubuntu
ansible-galaxy info geerlingguy.nginx

Check download counts, last update, supported platforms, and open issues on GitHub. A stale role targeting Ubuntu 18.04 is a liability on Ubuntu 24.04.

Galaxy Role WorkflowSearch Galaxyansible-galaxy searchReview Roleinfo + GitHubInstallrequirements.ymlPin Versiontag or commitroles/ directory on control nodegeerlingguy.nginx, geerlingguy.php, your custom rolesansible-playbook site.yml → target hosts configured
Ansible Roles and Galaxy workflow — search, vet, install with pinned versions, then run playbooks

Install with requirements.yml

Never install Galaxy roles by hand on production CI runners. Pin them in a file and commit it:

# requirements.yml
roles:
  - name: geerlingguy.nginx
    version: "3.2.0"
  - name: geerlingguy.php
    version: "6.0.0"
  - name: geerlingguy.redis
    version: "1.8.0"

collections:
  - name: community.general
    version: "10.3.0"
ansible-galaxy install -r requirements.yml -p roles/

The -p roles/ flag keeps third-party roles beside your own under roles/. Some teams prefer roles/external/ to separate vendor code from internal roles. Pick one convention and document it in your repo README.

Override Galaxy role variables

# group_vars/app_servers/nginx.yml
nginx_user: "www-data"
nginx_worker_processes: "auto"
nginx_vhosts:
  - listen: "443 ssl http2"
    server_name: "app.example.com"
    root: "/var/www/current/public"
    template: "laravel-ssl.j2"

Read the role's defaults/main.yml and README before overriding. Variable names differ between authors. Guessing leads to silent misconfiguration.

How do you structure role dependencies and collections?

Complex stacks need roles that depend on other roles. Ansible resolves this through meta/main.yml and Galaxy requirements files.

Declare dependencies in meta/main.yml

# roles/laravel_app/meta/main.yml
galaxy_info:
  author: kokil
  description: Laravel application server baseline
  license: MIT
  min_ansible_version: "2.16"
  platforms:
    - name: Ubuntu
      versions:
        - jammy
        - noble

dependencies:
  - role: geerlingguy.php
    vars:
      php_enable_webserver: false
  - role: geerlingguy.nginx
  - role: geerlingguy.redis

When your playbook includes laravel_app, Ansible installs and runs dependencies first. Pass vars in the dependency block to tune upstream roles without forking them.

Prefer collections for modules and plugins

Modern Ansible splits content into collections under ~/.ansible/collections/ or a project-local path. Install them alongside roles:

ansible-galaxy collection install -r requirements.yml

Collections ship modules like community.general.sudoers or community.mysql.mysql_user. Roles consume those modules. Keep collections pinned the same way you pin Composer packages on a Laravel 13 project running PHP 8.3 or higher.

Role Execution Order1. Dependency roles (meta/main.yml) run first2. Role tasks execute top to bottom3. Handlers flush at end of each play sectionChanged tasknotify handlerHandler runsonce per play
Dependency resolution and handler flush order inside Ansible Roles and Galaxy playbooks

Store secrets outside plain YAML. Use Ansible Vault for encrypted vars files referenced from roles. Never commit database passwords or API keys in defaults/main.yml. Generate strong passphrases with a password generator and store vault keys in your CI secret store.

Ansible roles vs standalone playbooks: which should you use?

Playbooks alone work for one server and ten tasks. Roles win when repetition, teams, and versioning enter the picture.

CriteriaStandalone playbookRole-based layout
Reuse across projectsCopy-paste tasks; drift is likelyImport same role with different vars
TestingHard to isolate one concernTest each role with Molecule independently
Galaxy sharingNot supportedPublish to Galaxy or a private Automation Hub
ReadabilityGood for quick one-offsPlaybook stays a high-level map
Team ownershipOne file, many conflictsSplit by domain: web, db, monitoring
CI integrationSimple lint on one fileLint per role; pin Galaxy versions in CI

My rule of thumb: if you configure the same stack twice, extract a role. If a third project needs it, publish internally or pull from Galaxy with a pinned version.

Compare this with other config tools in Ansible vs Puppet vs Chef vs Salt. Ansible roles feel closest to functions in code — inputs via vars, side effects via handlers, composition via dependencies.

For PHP-specific patterns, see Ansible playbooks for PHP server provisioning. Roles wrap those patterns into units you can drop onto any inventory group.

How do you test, version, and publish Ansible roles?

Untested roles break production quietly. A missing handler or wrong variable default shows up at 2 a.m., not during ansible-playbook --check.

Run syntax checks and dry runs

  1. ansible-playbook --syntax-check site.yml
  2. ansible-playbook site.yml --check --diff against staging
  3. ansible-lint site.yml roles/ to catch anti-patterns

Fix lint warnings before merging. They catch unquoted versions, bare variables in templates, and risky command modules where built-in modules exist.

Use Molecule for role-level tests

Molecule spins up a test instance (often Docker), applies your role, and runs assertions:

# molecule/default/molecule.yml
dependency:
  name: galaxy
driver:
  name: docker
platforms:
  - name: ubuntu-noble
    image: geerlingguy/docker-ubuntu2404-ansible
provisioner:
  name: ansible
verifier:
  name: ansible
molecule test

Even one converge-and-verify cycle catches broken templates and missing packages before they hit a client's EC2 box.

Publish to Galaxy or keep roles private

Public roles need a GitHub repo and a Galaxy namespace linked to your account. Private teams use scm: git entries in requirements.yml:

roles:
  - name: internal.laravel_stack
    scm: git
    src: git@gitlab.com:yourorg/ansible-role-laravel.git
    version: v2.4.1

Tag releases semantically. Your CI pipeline should run ansible-galaxy install -r requirements.yml before every deploy job — the same discipline you apply when running composer install on a Laravel app.

CI Pipeline for RolesGit Pushrole repoansible-lintstyle checksmolecule testconverge verifyTag Releasev1.2.3Staging: ansible-galaxy install -r requirements.ymlansible-playbook site.yml --check then live runProduction servers match role-defined baselinePHP 8.3, Nginx, Redis, UFW, fail2ban
Recommended CI flow for Ansible Roles and Galaxy — lint, test, pin, then apply to staging before production

Sister sites I maintain on shared EC2 use GitLab CI for application deploys. The same pipeline mindset applies to Ansible repos: every change gets linted, every role gets a tagged release, and production never pulls floating main branches. Read Terraform vs Ansible if you split provisioning (cloud resources) from configuration (packages and services).

What production pitfalls should you avoid with Galaxy roles?

Galaxy quality varies. A role with 500k downloads can still ship outdated defaults or unsafe permissions.

  • Pin everything. Unpinned version: fields pull latest on every CI run. That breaks reproducibility.
  • Read the tasks. Open the role's tasks/main.yml on GitHub before trusting it in production.
  • Watch for deprecated modules. Older roles use apt_key or unpinned get_url patterns Ansible now discourages.
  • Separate concerns. Do not cram database schema migrations into an infrastructure role. App logic stays in deploy scripts.
  • Limit privilege escalation. Use become: true only on tasks that need root, not the entire role.
  • Document overrides. Your team needs a group_vars map showing which Galaxy vars you changed and why.

On budget-sensitive Nepal hosting (Rs 3,000–8,000/month VPS tiers, roughly USD 22–60), a well-tested role stack prevents costly manual rebuilds after a bad config drift. Pair automation with support and maintenance so someone watches drift alerts and security updates.

For enterprise setups with multiple environments, see enterprise application development patterns that treat infrastructure as versioned code alongside the Laravel or Symfony application.

The public Galaxy site at galaxy.ansible.com remains the starting point for discovery. Treat it like Packagist or npm — useful, but you still audit what you install.

Key Takeaways

  • Roles package tasks, handlers, templates, and vars into a reusable directory Ansible loads automatically.
  • Install Galaxy roles through requirements.yml with pinned versions — never copy vendor YAML by hand.
  • Declare dependencies in meta/main.yml and pass vars to tune upstream roles without forking them.
  • Run ansible-lint, --check, and Molecule tests before any production playbook apply.
  • Keep secrets in Vault; use defaults only for non-sensitive, overridable configuration values.
  • Extract a role the moment you configure the same stack twice — playbooks should read like a table of contents.

People Also Ask

What is the difference between Ansible roles and playbooks?

A playbook is the orchestration file that maps hosts to automation. A role is the reusable unit of tasks and templates the playbook calls. One playbook can invoke many roles; one role can be shared across many playbooks without duplication.

How do I use a Galaxy role in my playbook?

Add the role to requirements.yml, run ansible-galaxy install -r requirements.yml, then list the role under roles: in your playbook or use import_role. Override behaviour through group_vars or host_vars using variable names documented in the role README.

Can I use private roles instead of Ansible Galaxy?

Yes. Point requirements.yml at a private Git repository with scm: git and a version tag. Many teams host internal roles on GitLab or GitHub Enterprise and skip public Galaxy entirely while keeping the same install workflow.

Do Ansible roles work with Laravel and PHP deployments?

Roles handle server baseline — PHP-FPM pools, Nginx vhosts, Redis, queue workers, cron, and system users. Application deploys typically stay in Deployer, GitLab CI, or similar tools. The role prepares the box; the deploy tool ships code. That separation keeps infrastructure and application release cycles independent.

Build repeatable servers with Ansible Roles and Galaxy

Manual server setup does not scale past a handful of boxes. Ansible Roles and Galaxy give you composable, testable building blocks — your own roles for business-specific config, Galaxy roles for commodity software, and pinned requirements files for CI reproducibility. Start with one role extracted from an existing playbook, add Molecule tests, and grow the library as your stack matures.

If you want help provisioning Laravel, WordPress, or eCommerce servers with audited automation, review the Adventure Third Pole Trek stack and other portfolio projects I have shipped. For hands-on infrastructure work, explore Linux system administration services or contact us to discuss your inventory, roles, and deployment pipeline.

Frequently Asked Questions

A role is Ansible’s standard reusable folder of tasks, handlers, templates, variables, and metadata. Galaxy is the public registry where you search, download, and publish those roles for discovery and distribution.

A playbook maps hosts to automation and stays thin. A role holds the reusable how — tasks, templates, handlers, and vars — that playbooks call via roles: or import_role.

When you configure the same stack twice. If a third project needs it, publish internally or pull from Galaxy with a pinned version in requirements.yml.

Run ansible-galaxy role init webserver --init-path roles/ to generate the compliant skeleton under roles/webserver/. Add idempotent tasks in tasks/main.yml using modules like ansible.builtin.apt and ansible.builtin.template, wire service reloads through handlers/main.yml, and put overridable values in defaults/main.yml. Host or group vars in inventory override defaults without editing the role. Call the role from site.yml under roles: alongside other concerns like common, php_fpm, nginx, and redis. For Laravel stacks, one role per concern beats a single long playbook.

Every role follows a predictable layout Ansible loads automatically. tasks/main.yml holds the work. handlers/main.yml restarts services only when something changed. templates/ and files/ store Jinja templates and static files. defaults/main.yml sets overridable values; vars/main.yml holds higher-priority variables. meta/main.yml declares Galaxy metadata and role dependencies. README.md documents usage. That contract is defined in official Ansible role reuse documentation and keeps teams aligned on where configuration lives.

Search and vet first with ansible-galaxy search nginx --platforms Ubuntu and ansible-galaxy info geerlingguy.nginx — check downloads, last update, supported platforms, and GitHub issues. A stale role targeting old Ubuntu releases is a liability on current servers. Pin roles in requirements.yml, run ansible-galaxy install -r requirements.yml -p roles/, then list the role under roles: in your playbook. Override behaviour through group_vars or host_vars after reading the role’s defaults/main.yml and README, because variable names differ between authors and guessing causes silent misconfiguration.

Never install Galaxy roles by hand on production CI runners. Commit a requirements.yml that pins each role name and version, for example geerlingguy.nginx 3.2.0, geerlingguy.php 6.0.0, and geerlingguy.redis 1.8.0, plus collections like community.general 10.3.0. Run ansible-galaxy install -r requirements.yml -p roles/ before every pipeline job — the same discipline as composer install on a Laravel app. Unpinned version fields pull latest on every run and break reproducibility. Tag private role releases semantically when using scm: git sources.

Complex stacks declare upstream roles under dependencies in meta/main.yml. When your playbook includes laravel_app, Ansible installs and runs those dependencies first. Pass vars in the dependency block — such as php_enable_webserver: false on geerlingguy.php — to tune upstream roles without forking them. Also set galaxy_info with author, license, min_ansible_version 2.16, and supported platforms like Ubuntu jammy and noble. Prefer collections for modules and plugins; install them alongside roles via requirements.yml and pin versions the same way you pin Composer packages.

Yes. Point requirements.yml at a private repository using scm: git, a src URL like git@gitlab.com:yourorg/ansible-role-laravel.git, and a version tag such as v2.4.1. Many teams host internal roles on GitLab or GitHub Enterprise and skip public Galaxy entirely while keeping the same ansible-galaxy install workflow. That suits business-specific Laravel baselines you do not want published. Public roles still need a GitHub repo and a Galaxy namespace linked to your account if you publish to galaxy.ansible.com.

Standalone playbooks suit one server and roughly ten tasks. Roles win when repetition, teams, and versioning matter. With playbooks alone you copy-paste tasks and drift is likely; roles import the same unit with different vars across projects. Testing isolates one concern with Molecule per role. Galaxy sharing is not supported for raw playbooks but roles publish publicly or to a private Automation Hub. Playbooks read like a high-level map; roles split ownership by domain — web, database, monitoring — reducing merge conflicts in one giant file.

Untested roles break production quietly. Run ansible-playbook --syntax-check site.yml, then ansible-playbook site.yml --check --diff against staging, and ansible-lint site.yml roles/ to catch anti-patterns like unquoted versions or risky command modules. Use Molecule for role-level tests: it spins up a Docker instance such as geerlingguy/docker-ubuntu2404-ansible, applies your role, and runs assertions via molecule test. Even one converge-and-verify cycle catches broken templates and missing packages before they hit a client EC2 box. Fix lint warnings before merging.

Yes, with a clear split of responsibilities. Roles handle the server baseline — PHP-FPM pools, Nginx vhosts, Redis, queue workers, cron, and deploy users — using defaults like php_version 8.3 and packages for fpm, mysql, redis, mbstring, xml, and curl. Application deploys stay in Deployer, GitLab CI, or similar tools. The role prepares the box; the deploy tool ships code. That keeps infrastructure and application release cycles independent. On real client projects I pair Ansible roles for provisioning with Git-based deploy tools for Laravel releases.

Galaxy quality varies — high download counts do not guarantee safe defaults. Pin everything in requirements.yml; floating versions break CI reproducibility. Read tasks/main.yml on GitHub before trusting a role; older ones may use deprecated modules like apt_key or unpinned get_url patterns Ansible now discourages. Do not cram database schema migrations into infrastructure roles — app logic stays in deploy scripts. Use become: true only on tasks that need root, not the entire role. Document overrides in group_vars so your team knows which Galaxy vars changed and why.

Store secrets outside plain YAML. Use Ansible Vault for encrypted vars files referenced from roles. Never commit database passwords or API keys in defaults/main.yml — defaults are for non-sensitive, overridable configuration only. Generate strong passphrases with a password generator and store vault keys in your CI secret store. That pattern matters on shared EC2 infrastructure where multiple environments pull from the same Ansible repo. Treat vault discipline with the same seriousness as keeping .env out of Git on a Laravel application.

They solve different layers. Terraform provisions cloud resources — instances, networks, storage. Ansible roles configure what runs on those boxes — packages, services, users, and templates. Read Terraform vs Ansible if you split provisioning from configuration. Roles feel closest to functions in code: inputs via vars, side effects via handlers, composition via dependencies. Sister sites on shared EC2 use GitLab CI for application deploys while Ansible repos follow the same pipeline mindset — lint every change, tag every role release, and never pull floating main branches into production.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: