ansible-rehearse
Rehearse your Ansible playbook in a throwaway container and see the real state diff — packages, files, services, ports, users — before you touch production.
Think terraform plan, but for Ansible: instead of predicting what a playbook
might do, rehearse actually runs it inside an ephemeral container matched to
your target distro, snapshots the system before and after, and shows you exactly
what changed.
Why not just --check?
ansible-playbook --check lies by omission:
- Modules without check-mode support are silently skipped —
shell,command,script,rawtasks are never executed, so their effects are invisible. - Registered variables stay undefined, so
when:conditionals silently take the wrong branch and dependent tasks are mis-simulated or explode. - Some modules simply behave differently under
--check(a task that works in a real run can fail in check mode, and vice versa).
rehearse takes the opposite approach: run everything for real, in a sandbox,
and measure what actually happened. Your shell tasks run. Your register +
when chains take the same branches they would take on a real host. Then you get
a terraform-plan-style diff of observed state — not a simulation.
Quick start
pip install ansible-rehearse
You need Docker (or Podman) running. Ansible itself is not required on your
machine — it runs inside the container, so rehearse works from Linux, macOS and
Windows hosts alike.
# Rehearse against a plain Ubuntu 22.04 container
rehearse run site.yml
# Service state too? Use a systemd-enabled container
rehearse run site.yml --systemd
# Match your target distro
rehearse run site.yml --distro rocky9
# Machine-readable output for CI
rehearse run site.yml --json result.json
The first run per distro prepares the container (installs a pinned
ansible-core into an isolated venv) and caches it as an image; later runs
start in seconds.
What you get
- Packages — installed / removed / upgraded, with versions
- Files — created / deleted / modified under watched directories
(
/etc,/usr/local,/opt,/srv,/root,/home,/var/spool/cron,/var/www, plus--watch DIR), including mode / owner / content changes - Services — enabled / started / stopped (in
--systemdmode) - Listening ports — opened / closed, with the owning process
- Users & groups — added / removed / modified
- A per-task fidelity table — how much to trust each result (see below)
The fidelity model
A container is not your production host, and pretending otherwise is how preview tools lose your trust. Every task gets an explicit fidelity label:
| Label | Meaning |
|---|---|
exact |
The state change happens for real in the container and is fully observable. |
real-exec |
Arbitrary code (shell, command…) executed for real — the thing --check can never show you. |
needs-systemd |
Service state needs a service manager; rerun with --systemd. |
approximate |
Runs, but a container's shared kernel / missing subsystems may differ from a real host (sysctl, firewall, SELinux). |
not-rehearsable |
No meaningful container equivalent (reboot, kernel modules, partitioning). |
external |
Would touch systems outside the container — refused by default. |
Run rehearse modules to see the full matrix.
Safety model
- External-effect gate. Before anything runs, the playbook is statically
scanned. Tasks that would touch real infrastructure — cloud collections
(
amazon.aws.*,azure.*,kubernetes.core.*…), notification modules, mutatinguricalls — causerehearseto refuse to run (exit code 3) unless you pass--allow-external. Both modern FQCN and legacyaction:/local_action:syntax are scanned. - No tampering window. The state collector is piped into the container over stdin for each snapshot; there is no on-disk script a playbook could rewrite between snapshots.
--systemdis privileged. Systemd-in-docker requires a privileged container sharing host cgroups: kernel-level operations can leak to the docker host.rehearselabels those tasks accordingly and warns loudly — only rehearse playbooks you trust, especially in--systemdmode.- Third-party content. Roles/collections from
requirements.ymlare installed at runtime inside the container and are not covered by the static gate — review them yourself.
A rehearsal is not your production host
Honest limitations, so you can decide what to trust:
- The container starts clean: the diff shows what the playbook does to a fresh OS-matched system, not to your drifted production host with its years of accumulated state.
- Kernel, hardware, network topology and cross-host orchestration are not reproduced. Cloud/network modules cannot be rehearsed at all (they are blocked by default precisely because they would run for real).
- Facts differ (hostname, IPs, memory), so playbooks branching on them may take different paths than in production.
What it is great at: catching broken task logic, missing packages, wrong paths, template errors, misbehaving conditionals, unintended file/permission changes, and services that do not come up — before any real host is involved.
How it compares
| executes tasks | real state diff | needs test code | target | |
|---|---|---|---|---|
ansible-playbook --check |
no (simulates, skips) | no | no | your real hosts |
| check-mode formatters | no (same skips, prettier) | no | no | your real hosts |
| Molecule | yes | no (you write assertions) | yes | fresh test instance |
| rehearse | yes | yes (packages/files/services/ports/users) | no | throwaway container |
Molecule is the right tool for testing roles with assertions in CI over time;
rehearse answers a different question — "what exactly will this playbook do,
right now, before I run it for real?" — with zero test code.
Supported targets
| profile | plain image | --systemd image |
|---|---|---|
ubuntu22 (default) |
ubuntu:22.04 |
geerlingguy/docker-ubuntu2204-ansible |
ubuntu24 |
ubuntu:24.04 |
geerlingguy/docker-ubuntu2404-ansible |
debian12 |
debian:12 |
geerlingguy/docker-debian12-ansible |
rocky9 |
rockylinux:9 |
geerlingguy/docker-rockylinux9-ansible |
Or bring your own image with --image.
Exit codes
| code | meaning |
|---|---|
| 0 | rehearsal completed, playbook succeeded |
| 1 | rehearsal completed, playbook failed (partial diff shown) |
| 2 | usage / playbook / internal error |
| 3 | refused: playbook contains external-effect tasks |
| 4 | container engine unavailable |
Contributing
See CONTRIBUTING.md. Bug reports with a failing playbook snippet are gold; so are fidelity-matrix corrections from people who know a module better than we do.
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ansible_rehearse-0.1.0.tar.gz.
File metadata
- Download URL: ansible_rehearse-0.1.0.tar.gz
- Upload date:
- Size: 69.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
90aa4d8be03131c7c0e5cb7dac92ab48d79c61425b5e27d3feb6424a28e21c60
|
|
| MD5 |
e2140c9553a971eb49bf1441058f38e6
|
|
| BLAKE2b-256 |
12af034aa4832a77a315c7c9ade9178ddbf3ac4f1bd164b7ba275fc8827757c3
|
Provenance
The following attestation bundles were made for ansible_rehearse-0.1.0.tar.gz:
Publisher:
release.yml on bogdancolceriu/ansible-rehearse
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ansible_rehearse-0.1.0.tar.gz -
Subject digest:
90aa4d8be03131c7c0e5cb7dac92ab48d79c61425b5e27d3feb6424a28e21c60 - Sigstore transparency entry: 2374671399
- Sigstore integration time:
-
Permalink:
bogdancolceriu/ansible-rehearse@b2800cc426b5f9d5048eada947e28104f46dc8e3 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/bogdancolceriu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b2800cc426b5f9d5048eada947e28104f46dc8e3 -
Trigger Event:
release
-
Statement type:
File details
Details for the file ansible_rehearse-0.1.0-py3-none-any.whl.
File metadata
- Download URL: ansible_rehearse-0.1.0-py3-none-any.whl
- Upload date:
- Size: 36.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b6a556b38ef1bcd2d7763738498168c752a8e121b4504295b7cb6ba3743eed8d
|
|
| MD5 |
1fd9953c590b5222ae1412895fdc47e7
|
|
| BLAKE2b-256 |
4c2af6d656f3695bb92b13125be9a2922bda14b85808dcf551f08d781b59ebad
|
Provenance
The following attestation bundles were made for ansible_rehearse-0.1.0-py3-none-any.whl:
Publisher:
release.yml on bogdancolceriu/ansible-rehearse
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ansible_rehearse-0.1.0-py3-none-any.whl -
Subject digest:
b6a556b38ef1bcd2d7763738498168c752a8e121b4504295b7cb6ba3743eed8d - Sigstore transparency entry: 2374671896
- Sigstore integration time:
-
Permalink:
bogdancolceriu/ansible-rehearse@b2800cc426b5f9d5048eada947e28104f46dc8e3 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/bogdancolceriu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b2800cc426b5f9d5048eada947e28104f46dc8e3 -
Trigger Event:
release
-
Statement type: