Skip to main content
Cloud DevOps Solutions
Get a quote Talk to an engineer

Ansible / Config Mgmt

Building an Ansible Role Library Your Team Can Maintain

An Ansible role library is a set of small, versioned, idempotent roles that each configure one concern, such as nginx or chrony, composed by thin playbooks. We design the structure and variable conventions, write and test the roles, and hand over a library your own engineers can extend.

Key takeaways

  • Roles do one job each; playbooks only compose roles and map them to inventory groups.
  • Every task must be idempotent, so a second run reports no changes.
  • Variable names are prefixed and documented, with safe defaults and no hidden coupling between roles.
  • Roles are versioned in Git and consumed through collections or requirements files, not copy-pasted.
  • Handover includes tests and documentation so the library outlives our engagement.

Why a role library beats a folder of playbooks

Most estates that have been “automated” with Ansible look the same. One repository holds a dozen playbooks, each several hundred lines long, each written by a different person. Variables are defined in four places. Half the tasks use shell with a when guard that approximates idempotency. Nobody is confident enough to run anything against production without a long Slack thread first.

That is the gap between having Ansible and having a configuration management practice. A role library closes it. It turns tribal knowledge about how a web server, a database host or a bastion should be configured into versioned, reviewed, tested code. This page describes how we build that library as part of our wider Ansible and configuration management service.

What a good library looks like

Concrete criteria we hold ourselves to:

  • One concern per role. An nginx role installs and configures nginx. It does not also create application users or open firewall ports for unrelated services.
  • Idempotent by construction. A second run reports changed=0. Anything else is a bug, not a feature.
  • Safe defaults, explicit overrides. defaults/main.yml contains values that are fine in any environment. Environment-specific values live in group_vars.
  • Prefixed variables. Every variable starts with the role name, such as nginx_worker_connections, which prevents collisions and makes search trivial.
  • Fully qualified module names. ansible.builtin.template, not template, so the code is unambiguous when collections overlap.
  • Documented interfaces. Each role declares its inputs in meta/argument_specs.yml, so a typo in a variable fails early with a clear message.

How it works in practice

Repository layout

For most clients we recommend one Git repository for roles and a separate one for inventories and playbooks. That lets roles be versioned and released independently of environments.

roles/
  nginx/
    defaults/main.yml
    handlers/main.yml
    meta/argument_specs.yml
    tasks/main.yml
    templates/nginx.conf.j2
playbooks/
  web.yml
inventories/
  production/
    hosts.yml
    group_vars/web.yml
requirements.yml

A role written for idempotency

The most common defect we find is shell commands standing in for modules. Compare the two approaches to a simple task:

# Fragile: runs every time, reports changed every time
- name: Set max open files
  ansible.builtin.shell: echo "fs.file-max = 200000" >> /etc/sysctl.conf

# Idempotent: changes only when the value differs
- name: Set max open files
  ansible.posix.sysctl:
    name: fs.file-max
    value: "200000"
    state: present
    sysctl_set: true
    reload: true

The second form is declarative, supports check mode and gives honest change reporting. When no module exists, we use command with creates, removes or changed_when so the task is still safe to repeat.

Thin playbooks

A playbook should read like a table of contents:

- name: Configure web tier
  hosts: web
  become: true
  roles:
    - role: baseline
    - role: nginx
      vars:
        nginx_worker_connections: 4096
    - role: app_deploy

If you can understand what a server is meant to be by reading ten lines, the design is working.

Consuming roles

Roles are pinned in requirements.yml, so every environment installs the same version:

roles:
  - name: nginx
    src: [email protected]:your-org/ansible-role-nginx.git
    scm: git
    version: v1.4.0

Running ansible-galaxy install -r requirements.yml pulls exactly that tag. Promoting a new role version through dev, staging and production becomes a normal pull request.

Decisions and trade-offs

Write your own role or use a community one? Community roles save time for commodity software but bring their own variable names, supported platforms and upgrade pace. We often use a well-maintained community role as a starting point, read it fully, and fork it when your needs diverge. For security-sensitive pieces such as SSH configuration, we prefer roles we have read line by line.

One big role or several small ones? Small roles compose better and test faster. The price is more files and more pins. For anything shared across teams, small wins.

Roles inside the application repository or a central one? Central is easier to govern and test. Embedded is faster for a single team that owns one deployment. We pick based on how many teams touch the same servers.

When not to build this. If your fleet is mostly containers on Kubernetes with immutable images, a large role library is overkill. A small baseline for nodes and the occasional bastion may be all you need. Our Docker and Kubernetes teams will tell you honestly if that is your situation.

Common failure modes

  • Variable sprawl. The same setting defined in role defaults, group vars, host vars and extra vars. We document precedence, keep defaults minimal and forbid host-level overrides without a comment.
  • Shell as a crutch. Replaced with modules, or guarded properly.
  • Hidden role dependencies. Role A silently assumes role B ran first. We declare dependencies in meta/main.yml or restructure.
  • Untested templates. A Jinja2 edit that breaks a config file only on one distribution. Covered by Molecule tests.
  • Secrets in variables. Handled through Vault integration rather than plaintext.

Delivery and handover

We work in your repositories from day one, so there is no big-bang transfer at the end. Every role arrives through a pull request your engineers review. By the end you have the library, CI that lints and tests it, a role catalogue and contribution guide, and the knowledge to add the next role without us. If you want to run playbooks from a web interface with approvals, the AWX deployment builds directly on this library.

A note on ownership

Every role in the library should name an owner or owning team in its metadata. Unowned roles drift, collect unreviewed changes and become the ones nobody dares touch, so we assign owners during handover.

How to tell it is working

  • A routine run against a stable server reports zero changes.
  • A new engineer can find the role for a task in minutes and change it through a pull request.
  • Building a new server of a known type is a single playbook run with no manual steps.
  • Role versions are pinned, and production runs a version that has already passed through staging.
  • Review comments focus on design, not on whether the playbook will work.

If your automation is a pile of scripts today, a short review will show which parts to keep. Contact us to discuss your estate.

References

How we deliver it

  1. 01

    Audit what exists

    We read your current scripts, playbooks and runbooks, and list which servers are configured by code and which by hand.

  2. 02

    Design the catalogue

    We agree role boundaries, naming, variable prefixes, tag strategy and repository layout before writing any task.

  3. 03

    Write roles to a standard

    Each role gets defaults, handlers, templates, argument specs, and fully qualified module names, written for idempotency.

  4. 04

    Compose playbooks

    Thin playbooks map roles to inventory groups, with check mode and diff mode working out of the box.

  5. 05

    Test and release

    Roles are linted and tested in CI, tagged with semantic versions and published to a private Git source or Galaxy-compatible server.

  6. 06

    Hand over

    Your team reviews pull requests with us, then owns the library with a contribution guide and a role catalogue.

Tools and technologies

Ansibleansible-coreansible-lintMoleculeAnsible GalaxyAnsible collectionsGitJinja2yamllintGitHub ActionsAWX

Frequently asked questions

What is the difference between an Ansible role and a playbook?
A playbook maps hosts to tasks and roles, while a role is a reusable unit that configures one concern. Roles have a standard directory layout with defaults, tasks, handlers and templates. Keeping logic in roles and keeping playbooks short is what makes automation reusable across environments and teams.
Should we use roles or collections?
Use both. Roles hold your own configuration logic, while collections package modules, plugins and roles for distribution. Most teams consume community collections such as ansible.posix for modules, and keep their own roles in one Git repository or a private collection. We pin versions in a requirements file either way.
Can you refactor our existing playbooks instead of rewriting them?
Usually yes. We keep behaviour the same, extract repeated blocks into roles, replace shell and command tasks with proper modules, and add tests before each change. Refactoring in small steps keeps production safe and shows exactly where idempotency was previously broken.
How do you keep roles from becoming unmaintainable?
By limiting scope, naming variables consistently and testing every role in CI. A role that tries to configure several services becomes a tangle, so we split it. Linting and Molecule runs on each pull request catch regressions long before they reach a server.
Do we need Ansible if we already use Terraform?
Often yes, because they solve different problems. Terraform creates infrastructure, while Ansible configures operating systems and applications on it. If your workloads are fully containerised and immutable, you may need little Ansible, and we will say so.

Talk to an engineer about ansible playbook and role library

Book a free 30-minute call. We review your current setup and tell you honestly whether and how we can help.

Written by Saddique Khan, DevOps / SRE / Cloud Consultant Engineer. Last updated 2026-10-08.