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

Ansible / Config Mgmt

Testing Ansible Roles with Molecule: Scenarios, Idempotence and CI

Molecule is the standard framework for testing Ansible roles. It creates a disposable instance, runs the role, runs it again to prove idempotence, then verifies the result. We build scenarios per supported operating system and run them on every pull request.

Key takeaways

  • The idempotence check alone catches a large share of real role bugs.
  • Containers are fast but differ from real hosts; use VMs for roles that touch systemd, kernel or networking.
  • Test the end state (ports, files, services), not that tasks ran.
  • A matrix of supported distributions turns upgrade surprises into failed pull requests.
  • Tests must stay fast, or people will stop running them.

Untested roles are production experiments

A role that has only ever been run against the server it was written for works on exactly one server. The first time it meets a different distribution release, a different package version, or an already-configured host, it fails, and usually in production, because that is where the variety is.

Molecule fixes this by making role testing a normal development activity. You change a role, run one command, and a fresh instance proves that the role converges, that a second run changes nothing, and that the resulting system is what you intended. It is the testing layer in our Ansible configuration management service, sitting beside the linting pipeline.

What good looks like

  • Every shared role has at least one Molecule scenario.
  • The scenario matrix covers each operating system and major version you run in production.
  • The idempotence check is on, and not skipped.
  • Verification asserts the end state, not the existence of tasks.
  • A full test run takes minutes, and developers run it locally before pushing.

How it works in practice

The test sequence

A default molecule test runs these stages: dependency, cleanup, destroy, syntax, create, prepare, converge, idempotence, side effect, verify, cleanup and destroy. The important ones:

  • create starts a disposable instance.
  • prepare installs what the role assumes already exists, such as Python or a package repository.
  • converge applies the role.
  • idempotence applies it again and fails on any change.
  • verify checks the end state.

Scenario configuration

A scenario lives in molecule/default/ inside the role. A configuration using containers for two distributions:

# molecule/default/molecule.yml
driver:
  name: docker
platforms:
  - name: rocky9
    image: geerlingguy/docker-rockylinux9-ansible
    pre_build_image: true
    privileged: true
    command: /usr/lib/systemd/systemd
    volumes:
      - /sys/fs/cgroup:/sys/fs/cgroup:rw
    cgroupns_mode: host
  - name: ubuntu2204
    image: geerlingguy/docker-ubuntu2204-ansible
    pre_build_image: true
    privileged: true
    command: /lib/systemd/systemd
    volumes:
      - /sys/fs/cgroup:/sys/fs/cgroup:rw
    cgroupns_mode: host
provisioner:
  name: ansible
verifier:
  name: ansible

The image names above are widely used community images with systemd preinstalled; we verify current tags when setting up your project and may build our own images so supply does not depend on a third party. Recent Molecule releases moved container and VM drivers into the separate molecule-plugins package, so the pinned requirements file lists it explicitly.

Converge and verify

# molecule/default/converge.yml
- name: Converge
  hosts: all
  become: true
  roles:
    - role: baseline
      vars:
        baseline_ssh_permit_root_login: "no"
# molecule/default/verify.yml
- name: Verify
  hosts: all
  gather_facts: false
  tasks:
    - name: Read effective sshd configuration
      ansible.builtin.command: sshd -T
      register: sshd_t
      changed_when: false

    - name: Root login is disabled
      ansible.builtin.assert:
        that: "'permitrootlogin no' in sshd_t.stdout"

    - name: chrony is running
      ansible.builtin.service_facts:

    - name: Time service is active
      ansible.builtin.assert:
        that: "ansible_facts.services['chronyd.service'].state == 'running'"

Verification should read like a specification. If a future change breaks SSH policy, the test fails with a message about root login, not a stack trace.

Containers or virtual machines

AspectContainersVMs (Vagrant or cloud)
SpeedSeconds to startMinutes
FidelityShares host kernelFull kernel and init
Good forPackages, templates, users, servicesKernel parameters, firewalls, mounts, reboots
CostVery lowHigher, especially in CI

We use containers for the bulk of tests and add a smaller VM scenario for roles that need it. Roles that reboot hosts or change kernel settings simply cannot be proven in a container, and we say so.

In CI

A pull request triggers a matrix job, one entry per role and per platform. We install pinned versions of ansible-core, molecule and plugins, cache pip downloads, and run only the roles whose files changed. Test output is shown in the job log, and a failing scenario can be reproduced locally with the same command. For platform choice and speed advice, see our CI/CD service and the guide to pipeline best practices.

Decisions and trade-offs

Molecule verifier or Testinfra. The Ansible verifier keeps everything in one language. Testinfra with pytest offers richer assertions for teams comfortable with Python. We follow your team’s skills.

How many platforms. Test what you run. Testing eight distributions you do not use wastes minutes on every pull request.

Unit-like tests or full-stack tests. Molecule is excellent for single roles. Testing a whole system, with a load balancer and a database, is better done in a staging environment, perhaps created with Terraform.

When not to use it. One-off playbooks that are run once, or throwaway migration scripts, do not need scenarios.

Common failure modes

  • Skipping idempotence because it fails. That failure is the finding. Fix the task.
  • Tests that pass trivially. Verify only checks that a file exists. Assert content.
  • Image drift. A community image changes under you. We pin tags or digests and keep our own images.
  • Slow suites. Tests creep to twenty minutes and are bypassed. We cap scope and parallelise.
  • Cgroup and systemd quirks. Container settings differ across hosts and CI runners. We document a known-good configuration.

Delivery and handover

We add scenarios to your most important roles first, set up the CI matrix, and pair with your engineers on writing the next ones. The result is a tested role library and a contribution guide that says a role is not done until its scenario passes.

Keeping the test suite fast enough to be used

A test suite nobody waits for is a test suite nobody runs. Use lightweight containers for most scenarios and reserve full virtual machines for roles that touch kernel settings, systemd behaviour or disks. Run the quick scenarios on every pull request and the heavier ones on merge or on a nightly schedule. If a scenario regularly takes longer than the team will tolerate, split the role rather than skipping the test.

How to tell it is working

  • Pull requests that break a role fail before review, with a clear assertion.
  • Operating system upgrades are tested by adding one line to the platform matrix.
  • Production incidents caused by role changes become rare.
  • New contributors write a test before changing a role, because it is the easiest way to see their change work.

To see a scenario run against one of your own roles, contact us.

References

How we deliver it

  1. 01

    Pick what to test

    We rank roles by blast radius and change frequency, starting with the baseline and the roles every server uses.

  2. 02

    Choose drivers and images

    Container images for speed, plus VM-based scenarios for roles that need a real init system, kernel settings or networking.

  3. 03

    Write scenarios

    Each scenario defines platforms, preparation steps, the converge playbook and variables that mirror real use.

  4. 04

    Add verification

    Assertions check the final state: packages, files, listening ports, service status and rendered configuration.

  5. 05

    Run in CI

    A matrix job tests changed roles across supported distributions on every pull request, with caching to keep it quick.

  6. 06

    Teach the workflow

    Your engineers learn to write the test first, run it locally and debug a failing scenario.

Tools and technologies

MoleculeAnsibleDockerPodmanVagrantTestinfrapytestansible-lintGitHub ActionsGitLab CImolecule-plugins

Frequently asked questions

What is Molecule in Ansible?
Molecule is a testing framework for Ansible roles and collections. It manages the lifecycle of a test instance: create, prepare, converge, idempotence, verify and destroy. This lets you run your role in a clean environment repeatedly, and prove it works, before it touches a real server.
What does the idempotence test check?
After the first converge, Molecule runs the role a second time and fails if any task reports a change. That proves the role only changes what differs from the desired state. Tasks that use raw shell commands, timestamps or unconditional restarts are the usual failures, and fixing them improves production behaviour.
Can Molecule test systemd services in containers?
Partly. Containers can be configured to run systemd with the right image and privileges, but behaviour still differs from a full machine, especially around kernel parameters, firewalls and mounts. For roles that depend on those, we use VM-based scenarios or run a reduced set of assertions in containers.
How should we verify results?
Assert the state that matters to users. Ansible's own assert and stat modules work well, and Testinfra offers Python-based checks if you prefer them. Verify ports are listening, files have correct modes, the service is enabled and running, and that rendered configuration contains the expected values.
Is Molecule worth it for a small codebase?
For a handful of simple roles, the effort may exceed the benefit, and a linting pipeline plus a staging run could be enough. It pays off once roles are shared across teams, support several operating systems, or are changed often by several people.

Talk to an engineer about molecule ansible role testing

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.