
September 11, 2026
12 min read
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.
ansible-galaxy and reference them in playbooks with roles: or import_role, keeping playbooks thin and servers consistent.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.
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.
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.
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.
| Criteria | Standalone playbook | Role-based layout |
|---|---|---|
| Reuse across projects | Copy-paste tasks; drift is likely | Import same role with different vars |
| Testing | Hard to isolate one concern | Test each role with Molecule independently |
| Galaxy sharing | Not supported | Publish to Galaxy or a private Automation Hub |
| Readability | Good for quick one-offs | Playbook stays a high-level map |
| Team ownership | One file, many conflicts | Split by domain: web, db, monitoring |
| CI integration | Simple lint on one file | Lint 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
ansible-playbook --syntax-check site.ymlansible-playbook site.yml --check --diffagainst stagingansible-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.
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.ymlon GitHub before trusting it in production. - Watch for deprecated modules. Older roles use
apt_keyor unpinnedget_urlpatterns 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: trueonly on tasks that need root, not the entire role. - Document overrides. Your team needs a
group_varsmap 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.ymlwith pinned versions — never copy vendor YAML by hand. - Declare dependencies in
meta/main.ymland 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
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.

