Skip to content

molecule

Molecule is the testing framework for Ansible content. It manages the full lifecycle of test infrastructure — provisioning instances, applying your Ansible content, running verification, and tearing everything down. Molecule supports testing against containers, virtual machines, cloud infrastructure, network devices, and APIs.

Molecule uses scenario-based configuration where each scenario is a self-contained test with its own infrastructure, playbooks, and verification steps.

Terminal window
# Via the full suite (recommended)
pip install ansible-dev-tools
# Standalone
pip install molecule
# With Docker driver
pip install molecule-plugins[docker]
# With Podman driver
pip install molecule-plugins[podman]

Verify installation:

Terminal window
molecule --version

A scenario is a self-contained test case with its own:

  • Infrastructure definition (what to create)
  • Converge playbook (what to test)
  • Verification playbook (how to validate)
  • Destroy playbook (how to clean up)

Scenarios live under extensions/molecule/ (for collections) or molecule/ (for roles and playbooks):

extensions/molecule/
├── default/
│ ├── molecule.yml
│ ├── create.yml
│ ├── converge.yml
│ ├── verify.yml
│ └── destroy.yml
└── integration/
├── molecule.yml
├── converge.yml
└── verify.yml

Molecule executes tests as an ordered sequence of actions:

dependency → cleanup → destroy → syntax → create → prepare →
converge → idempotence → side_effect → verify → cleanup → destroy

Each action maps to a playbook or built-in check:

Action Playbook Purpose
dependency — Install role/collection dependencies
cleanup cleanup.yml Clean external resources before destroy
destroy destroy.yml Tear down test infrastructure
syntax — Ansible syntax check on converge playbook
create create.yml Provision test infrastructure
prepare prepare.yml Pre-configure instances (runs once)
converge converge.yml Apply the role/playbook under test
idempotence — Re-run converge, assert no changes
side_effect side_effect.yml Trigger side effects (e.g., failover)
verify verify.yml Run verification assertions

Drivers manage the test infrastructure lifecycle. Available via the molecule-plugins package:

Driver Install extra Description
docker [docker] Docker containers
podman [podman] Podman containers
vagrant [vagrant] Vagrant VMs
ec2 [ec2] AWS EC2 instances
gce [gce] Google Compute Engine
azure [azure] Azure VMs
openstack [openstack] OpenStack instances

The default (delegated) driver is built into Molecule core — you provide your own create.yml and destroy.yml playbooks using any Ansible modules.

Additional drivers:

Driver Package Description
kubevirt molecule-kubevirt KubeVirt VMs
Section titled “Ansible-native configuration (recommended)”

The modern configuration approach uses molecule.yml with four top-level sections:

---
ansible:
executor:
backend: ansible-playbook
args:
ansible_playbook:
- --inventory=inventory/
- --extra-vars=environment=test
env:
ANSIBLE_FORCE_COLOR: "true"
ANSIBLE_HOST_KEY_CHECKING: "false"
ANSIBLE_STDOUT_CALLBACK: yaml
cfg:
defaults:
host_key_checking: false
gathering: smart
timeout: 30
ssh_connection:
pipelining: true
playbooks:
create: create.yml
destroy: destroy.yml
converge: converge.yml
prepare: prepare.yml
verify: verify.yml
cleanup: cleanup.yml
side_effect: side_effect.yml
scenario:
test_sequence:
- create
- converge
- verify
- destroy
shared_state: true
verifier:
name: ansible

The legacy format uses driver, platforms, and provisioner sections:

---
driver:
name: docker
platforms:
- name: instance-1
image: ubuntu:22.04
groups:
- web_servers
- name: instance-2
image: centos:stream9
groups:
- db_servers
provisioner:
name: ansible
config_options:
defaults:
host_key_checking: false
env:
ANSIBLE_STDOUT_CALLBACK: yaml
playbooks:
converge: converge.yml
inventory:
group_vars:
all:
app_version: "2.0"
verifier:
name: ansible
scenario:
test_sequence:
- dependency
- cleanup
- destroy
- syntax
- create
- prepare
- converge
- idempotence
- verify
- cleanup
- destroy
dependency:
name: galaxy
options:
role-file: requirements.yml
requirements-file: collections.yml

Molecule searches for configuration in this order:

  1. $HOME/.config/molecule/config.yml — global defaults
  2. Project root config.yml — project-wide defaults
  3. extensions/molecule/config.yml — collection base config
  4. Per-scenario molecule.yml — scenario-specific overrides

molecule.yml supports shell-style variable substitution:

platforms:
- name: instance-${MOLECULE_SCENARIO_NAME}
image: ${CONTAINER_IMAGE:-ubuntu:22.04}
Variable Description
MOLECULE_DEBUG Enable debug output
MOLECULE_SCENARIO_NAME Current scenario name
MOLECULE_EPHEMERAL_DIRECTORY Temp directory for scenario
MOLECULE_SCENARIO_DIRECTORY Path to scenario directory
MOLECULE_PROJECT_DIRECTORY Path to project root
MOLECULE_INVENTORY_FILE Generated inventory path
MOLECULE_INSTANCE_CONFIG Instance configuration path
MOLECULE_VERBOSITY Verbosity level

The default verifier runs verify.yml with Ansible assertion modules:

verify.yml
---
- name: Verify
hosts: all
gather_facts: false
tasks:
- name: Check that nginx is installed
ansible.builtin.package_facts:
manager: auto
- name: Assert nginx is present
ansible.builtin.assert:
that:
- "'nginx' in ansible_facts.packages"
fail_msg: "nginx is not installed"
- name: Check nginx is running
ansible.builtin.service_facts:
- name: Assert nginx service is active
ansible.builtin.assert:
that:
- "ansible_facts.services['nginx.service'].state == 'running'"

For Python-based testing with pytest/testinfra:

verifier:
name: testinfra
options:
n: 1
verbose: true
additional_files_or_dirs:
- ../shared_tests/
tests/test_default.py
def test_nginx_installed(host):
nginx = host.package("nginx")
assert nginx.is_installed
def test_nginx_running(host):
nginx = host.service("nginx")
assert nginx.is_running
assert nginx.is_enabled
Terminal window
# Run the full test sequence for the default scenario
molecule test
# Test a specific scenario
molecule test -s integration
# Test all scenarios
molecule test --all
# Run scenarios in parallel
molecule test --all --parallel
# Run with multiple workers
molecule test --all --workers 4
# Pass extra arguments to ansible-playbook
molecule converge -- --extra-vars="debug=true"
Terminal window
# Create instances only
molecule create
# Apply the converge playbook (create if needed)
molecule converge
# Run verification
molecule verify
# Destroy instances
molecule destroy
# Destroy all scenarios
molecule destroy --all
# Prepare instances (run once)
molecule prepare
# Check idempotence
molecule idempotence
# Interactive login to an instance
molecule login --host instance-1
# Run syntax check
molecule syntax
Terminal window
# Initialize a new scenario
molecule init scenario my_scenario
# List scenario status
molecule list
# Show the action matrix for a command
molecule matrix test
# Reset molecule temporary files
molecule reset
# List available drivers
molecule drivers
Flag Description
--debug Verbose output (passes -vvv to ansible-playbook)
-s, --scenario-name Target specific scenario
--parallel / --no-parallel Run scenarios concurrently (deprecated, use --workers)
--workers N Number of concurrent scenario workers

Configure which actions run for each command:

scenario:
create_sequence:
- dependency
- create
- prepare
converge_sequence:
- dependency
- create
- prepare
- converge
destroy_sequence:
- cleanup
- destroy
test_sequence:
- dependency
- cleanup
- destroy
- syntax
- create
- prepare
- converge
- idempotence
- verify
- cleanup
- destroy

For complex test scenarios, include multiple side effects and verification steps:

test_sequence:
- converge
- side_effect reboot.yaml
- verify after_reboot/
- side_effect alter_configs.yaml
- converge
- verify

Runs playbooks directly using locally installed Ansible:

ansible:
executor:
backend: ansible-playbook
args:
ansible_playbook:
- --inventory=inventory/

Runs playbooks inside execution environments for reproducible testing:

ansible:
executor:
backend: ansible-navigator
args:
ansible_navigator:
- --execution-environment-image=custom-ee:latest
- --mode=stdout
name: Molecule
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: pip install ansible-dev-tools molecule-plugins[docker]
- name: Run molecule
run: molecule test --all
env:
PY_COLORS: "1"
ANSIBLE_FORCE_COLOR: "1"
molecule:
image: ghcr.io/ansible/community-ansible-dev-tools:latest
services:
- docker:dind
variables:
DOCKER_HOST: tcp://docker:2375
script:
- molecule test --all
  • Set PY_COLORS=1 and ANSIBLE_FORCE_COLOR=1 to force colored output
  • Use --workers for parallel scenario execution
  • Set unique MOLECULE_EPHEMERAL_DIRECTORY per parallel job to avoid conflicts
  • Use the community-ansible-dev-tools container image for pre-installed tooling

Enable shared state to let scenarios share ephemeral data for efficient multi-scenario testing:

shared_state: true

This is useful when multiple scenarios test different aspects of the same infrastructure and you want to avoid reprovisioning for each scenario.

Projects scaffolded by ansible-creator include Molecule scenarios by default. The VS Code extension recognizes Molecule directory structures and provides:

  • File detection for molecule.yml and scenario playbooks
  • Ansible language support in all Molecule playbooks
  • Integration with ansible-lint for scenario files

Your scenarios pass locally. Now scale your testing and prepare for execution: